mirror of
https://github.com/wavemotion-dave/NINTV-DS.git
synced 2025-06-18 13:55:33 -04:00
917 lines
32 KiB
C++
917 lines
32 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 "config.h"
|
|
#include "bgMenu-Green.h"
|
|
#include "bgMenu-White.h"
|
|
#include "Emulator.h"
|
|
#include "Rip.h"
|
|
#include "ROMBanker.h"
|
|
#include "RAM.h"
|
|
#include "CRC32.h"
|
|
#include "loadgame.h"
|
|
#include "debugger.h"
|
|
#include "printf.h"
|
|
|
|
// -------------------------------------------------------------
|
|
// We support up to 512 files per directory. More than enough.
|
|
// -------------------------------------------------------------
|
|
FICA_INTV intvromlist[MAX_ROMS];
|
|
u16 countintv=0;
|
|
static u16 ucFicAct=0;
|
|
char szName[256];
|
|
char szName2[256];
|
|
extern char directory[];
|
|
|
|
UINT8 load_options = 0x00;
|
|
|
|
extern Rip *currentRip;
|
|
extern u8 bShowDisc;
|
|
|
|
u8 bFavsOnlyMode = false;
|
|
|
|
static UINT8 bFirstTimeLoad = true;
|
|
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// Load the cart from a file on disk. We support .bin/.int (raw binary) and we
|
|
// also support .ROM (intellicart). The .bin file must have a matching CRC in
|
|
// our internal database or else must have a matching <filename>.cfg file so
|
|
// we know how to load the binary into memory. The intellivision allows the
|
|
// ROM to be located in many possible memory segments... the .ROM will give
|
|
// us this information and is easier to deal with as a self-contained file.
|
|
// -------------------------------------------------------------------------------
|
|
BOOL LoadCart(const CHAR* filename)
|
|
{
|
|
if (strlen(filename) < 5)
|
|
return FALSE;
|
|
|
|
bIsFatalError = false;
|
|
bGameLoaded = FALSE;
|
|
bShowDisc = false; // Normal overlay to start
|
|
multi_ovls = 0; // And assume non-multi-overlay
|
|
multi_ovl_idx = 0; // if the .ovl file indicates we are multi, this will get used to pull in alternate overlay graphics
|
|
bmulti_LR = 0; // Not a L/R multi-overlay
|
|
|
|
// Clear out the debug array with every new game loaded
|
|
memset(debug, 0x00, DEBUG_SIZE * (sizeof(UINT32)));
|
|
|
|
// Load up the configuration based on the CRC32 of the game. Do this early since we need some of those properties to load the RIP
|
|
FindAndLoadConfig(CRC32::getCrc(filename));
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Set the RAM Bankers for page-flipping all back to zero on a new load
|
|
// ----------------------------------------------------------------------
|
|
memset(gLastBankers, 0x00, sizeof(gLastBankers));
|
|
|
|
extern UINT8 gBankerIsMappedHere[16][16];
|
|
memset(gBankerIsMappedHere,0x00, sizeof(gBankerIsMappedHere));
|
|
|
|
// -------------------------------------------------------------------------
|
|
// When we load a new game, we can safely put back these indexes for
|
|
// re-allocation of memory so we don't "leak" from our fixed memory pools
|
|
// -------------------------------------------------------------------------
|
|
slow_ram16_idx = 0; // Nothing uses this internally so we can reset to 0
|
|
slow_ram8_idx = 0; // Nothing uses this internally so we can reset to 0
|
|
fast_ram16_idx = 0x200; // 512 bytes is more than enough for internal Inty RAM so this is safely above the threshold
|
|
|
|
const CHAR* extStart = filename + strlen(filename) - 4;
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
// A .bin is always assumed to be a flat binary file (and may or may not have a .cfg file to go with it).
|
|
// A .rom is always assumed to be a file with extra meta-data that lets us know where to load it in memory.
|
|
// A .int file can switch-hit and might be a .bin or a .rom -- we look for the signature 0xA8 byte to see if it's a .rom
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
UINT8 bIsROM = ((strcmpi(extStart, ".rom") == 0) ? true:false);
|
|
if (strcmpi(extStart, ".int") == 0)
|
|
{
|
|
// A .int file might or might not be actual .rom format...
|
|
FILE* file = fopen(filename, "rb");
|
|
UINT8 buf[2];
|
|
if (file == NULL)
|
|
{
|
|
FatalError("BIN FILE DOES NOT EXIST");
|
|
return FALSE;
|
|
}
|
|
u8 firstByte = fgetc(file);
|
|
if ((firstByte == 0xA8) || (firstByte == 0x41)) bIsROM = true; // We accept eitehr ICART (0xA8) or CC3 (0x41) as proof that this is probably a ROM file
|
|
fclose(file);
|
|
}
|
|
|
|
if (bIsROM)
|
|
{
|
|
//load the .rom file as a Rip with meta-data parsed
|
|
currentRip = Rip::LoadRom(filename);
|
|
if (currentRip == NULL)
|
|
{
|
|
return FALSE; // FatalError() will have already been called
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//load the binary file as a Rip - use internal database or maybe <filename>.cfg exists... LoadBin() handles all that.
|
|
currentRip = Rip::LoadBin(filename);
|
|
if (currentRip == NULL)
|
|
{
|
|
return FALSE; // FatalError() will have already been called
|
|
}
|
|
}
|
|
|
|
// The Tutorvision adds in some extra 16-bit RAM so we do that here...
|
|
if (bUseTutorvision)
|
|
{
|
|
currentRip->AddRAM(new RAM(0x1a0, 0x360, 0xFFFF, 0xFFFF, 16));
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------------------------
|
|
// The ECS uses 3 banked ROM areas... so we need to fill those in manually as we clear this array out on every load.
|
|
// ------------------------------------------------------------------------------------------------------------------
|
|
if (bUseECS)
|
|
{
|
|
gBankerIsMappedHere[0x2][1] = 1;
|
|
gBankerIsMappedHere[0x7][0] = 1;
|
|
gBankerIsMappedHere[0xE][1] = 1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// New game is loaded... (would have returned FALSE above otherwise)
|
|
// ---------------------------------------------------------------------
|
|
extern UINT16 fudge_timing;
|
|
|
|
fudge_timing = 500*myConfig.fudgeTiming;
|
|
|
|
dsShowScreenEmu();
|
|
dsShowScreenMain(false, false);
|
|
|
|
bGameLoaded = TRUE;
|
|
bInitEmulator = true;
|
|
|
|
bStartSoundFifo = true;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// If this is the first time we are being asked to load a directory of
|
|
// games, we will check the configuration to see if we should start in
|
|
// one of the common directories: /roms or /roms/intv ... otherwise
|
|
// we will just open up in the last directory the user picked.
|
|
// -----------------------------------------------------------------------
|
|
void CheckFirstTimeLoad(void)
|
|
{
|
|
// First time in we use the config setting to determine where we open files...
|
|
if (bFirstTimeLoad)
|
|
{
|
|
bFirstTimeLoad = false;
|
|
if (myGlobalConfig.rom_dir == 1)
|
|
{
|
|
chdir("/ROMS");
|
|
}
|
|
else if (myGlobalConfig.rom_dir == 2)
|
|
{
|
|
chdir("/ROMS/INTV");
|
|
}
|
|
}
|
|
}
|
|
|
|
void SearchForFile(char *directory, char *szFoundName, UINT16 size, UINT32 crc32)
|
|
{
|
|
// Look through all files in the current directory to see if we get a CRC32 match...
|
|
DIR *pdir;
|
|
struct dirent *pent;
|
|
|
|
CheckFirstTimeLoad();
|
|
|
|
pdir = opendir(directory);
|
|
|
|
if (pdir)
|
|
{
|
|
while (((pent=readdir(pdir))!=NULL))
|
|
{
|
|
if (pent->d_type != DT_DIR)
|
|
{
|
|
struct stat st;
|
|
strcpy(szName2, directory);
|
|
strcat(szName2, pent->d_name);
|
|
stat(szName2, &st);
|
|
if (st.st_size == size)
|
|
{
|
|
if (CRC32::getCrc(szName2) == crc32)
|
|
{
|
|
// Found it!!
|
|
strcpy(szFoundName, szName2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir(pdir);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
// If the exec.bin was not found, we will do a search for it in the current directory
|
|
// to see if we get a match via CRC32. We keep our search as fast as possible, we
|
|
// will only look at files that are exactly 8k in size. This will still take a solid
|
|
// second or two on the NDS - it's better if the user has the right filename to avoid this.
|
|
// ---------------------------------------------------------------------------------------------
|
|
void FindAndLoadExec(char *directory, char *szFoundName)
|
|
{
|
|
SearchForFile(directory, szFoundName, 8192, 0xcbce86f7); // Look for the normal exec.bin
|
|
}
|
|
|
|
void FindAndLoadTutorExec(char *directory, char *szFoundName)
|
|
{
|
|
SearchForFile(directory, szFoundName, 16384, 0x7558a4cf); // Look for the Tutorvision wbexec.bin
|
|
}
|
|
|
|
void FindAndLoadGrom(char *directory, char *szFoundName)
|
|
{
|
|
SearchForFile(directory, szFoundName, 2048, 0x683a4158); // Look for the normal grom.bin
|
|
}
|
|
|
|
void FindAndLoadTutorGrom(char *directory, char *szFoundName)
|
|
{
|
|
SearchForFile(directory, szFoundName, 2048, 0x82736456); // Look for the Tutorvision wbgrom.bin
|
|
}
|
|
|
|
void FindAndLoadIVoice(char *directory, char *szFoundName)
|
|
{
|
|
SearchForFile(directory, szFoundName, 2048, 0x0de7579d); // Look for the Intellivioice ivoice.bin
|
|
}
|
|
|
|
void FindAndLoadECS(char *directory, char *szFoundName)
|
|
{
|
|
SearchForFile(directory, szFoundName, (24*1024), 0xea790a06); // Look for the ecs.bin
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
// Peripheral roms are basically our BIOS files... grom.bin, exec.bin and ivoice.bin
|
|
// ---------------------------------------------------------------------------------------------
|
|
CHAR nextFile[MAX_PATH];
|
|
BOOL LoadPeripheralRoms(Peripheral* peripheral)
|
|
{
|
|
UINT16 count = peripheral->GetROMCount();
|
|
for (UINT16 i = 0; i < count; i++)
|
|
{
|
|
ROM* r = peripheral->GetROM(i);
|
|
if (r->isLoaded()) // If already loaded, we don't need to read the file again...
|
|
continue;
|
|
|
|
if (myGlobalConfig.bios_dir == 1) // In: /ROMS/BIOS
|
|
{
|
|
strcpy(nextFile, "/roms/bios/");
|
|
}
|
|
else if (myGlobalConfig.bios_dir == 2) // In: /ROMS/INTV/BIOS
|
|
{
|
|
strcpy(nextFile, "/roms/intv/bios/");
|
|
}
|
|
else if (myGlobalConfig.bios_dir == 3) // In: /DATA/BIOS/
|
|
{
|
|
strcpy(nextFile, "/data/bios/");
|
|
}
|
|
else
|
|
{
|
|
strcpy(nextFile, "./"); // In: Same DIR as ROM files
|
|
}
|
|
|
|
strcpy(directory, nextFile);
|
|
strcat(nextFile, r->getDefaultFileName());
|
|
|
|
if (!r->load(nextFile, r->getDefaultFileOffset()))
|
|
{
|
|
if (strstr(nextFile, "wbgrom") != NULL) FindAndLoadTutorGrom(directory, nextFile);
|
|
else if (strstr(nextFile, "grom") != NULL) FindAndLoadGrom(directory, nextFile);
|
|
|
|
if (strstr(nextFile, "wbexec") != NULL) FindAndLoadTutorExec(directory, nextFile);
|
|
else if (strstr(nextFile, "exec") != NULL) FindAndLoadExec(directory, nextFile);
|
|
|
|
if (strstr(nextFile, "ivoice") != NULL) FindAndLoadIVoice(directory, nextFile);
|
|
|
|
if (strstr(nextFile, "ecs") != NULL) FindAndLoadECS(directory, nextFile);
|
|
|
|
if (!r->load(nextFile, r->getDefaultFileOffset()))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
// Let the user know what buttons they can press to load various peripheral roms...
|
|
// ---------------------------------------------------------------------------------------------
|
|
void dsDisplayLoadInstructions(void)
|
|
{
|
|
dsPrintValue(1,22,0,(char*)"SEL=MARK, STA=SAVEFAV, L/R=FAVS");
|
|
dsPrintValue(1,23,0,(char*)"A=LOAD, X=LOAD OPTIONS, B=BACK ");
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
// This is our custom sorting compare routine. Directories always sort to the top otherwise
|
|
// we do a case-insensitive sort.
|
|
// ---------------------------------------------------------------------------------------------
|
|
int intvFilescmp (const void *c1, const void *c2)
|
|
{
|
|
FICA_INTV *p1 = (FICA_INTV *) c1;
|
|
FICA_INTV *p2 = (FICA_INTV *) c2;
|
|
|
|
if (p1->filename[0] == '.' && p2->filename[0] != '.')
|
|
return -1;
|
|
if (p2->filename[0] == '.' && p1->filename[0] != '.')
|
|
return 1;
|
|
if (p1->directory && !(p2->directory))
|
|
return -1;
|
|
if (p2->directory && !(p1->directory))
|
|
return 1;
|
|
return strcasecmp (p1->filename, p2->filename);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------
|
|
// Determine if this game is one of our 'favs'. We do a hash crc32 on the title of the game
|
|
// which is much faster than trying to get CRCs of the actual files in a directory (too slow).
|
|
// ----------------------------------------------------------------------------------------------
|
|
bool isFavorite(char *filename)
|
|
{
|
|
for (UINT8 i=0; i<64; i++)
|
|
{
|
|
if (myGlobalConfig.favorites[i] != 0x00000000)
|
|
{
|
|
if (myGlobalConfig.favorites[i] == CRC32::getCrc((UINT8* )filename, strlen(filename)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Set the current filename as a favorite. We use the CRC32 of the filename as the hash.
|
|
// ----------------------------------------------------------------------------------------
|
|
void setFavorite(char *filename)
|
|
{
|
|
for (UINT8 i=0; i<64; i++)
|
|
{
|
|
if (myGlobalConfig.favorites[i] == 0x00000000)
|
|
{
|
|
myGlobalConfig.favorites[i] = CRC32::getCrc((UINT8* )filename, strlen(filename));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Clear the current filename as a favorite. We use the CRC32 of the filename as the hash.
|
|
// ----------------------------------------------------------------------------------------
|
|
void clrFavorite(char *filename)
|
|
{
|
|
for (UINT8 i=0; i<64; i++)
|
|
{
|
|
if (myGlobalConfig.favorites[i] != 0x00000000)
|
|
{
|
|
if (myGlobalConfig.favorites[i] == CRC32::getCrc((UINT8* )filename, strlen(filename)))
|
|
{
|
|
myGlobalConfig.favorites[i] = 0x00000000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Find all .bin, .int and .rom files in the current directory (or, if this is the first
|
|
// time we are loading up files, use the global configuration to determine what directory
|
|
// we should be starting in... after the first time we just load up the current directory).
|
|
// ----------------------------------------------------------------------------------------
|
|
void intvFindFiles(void)
|
|
{
|
|
DIR *pdir;
|
|
struct dirent *pent;
|
|
|
|
countintv = 0;
|
|
memset(intvromlist, 0x00, sizeof(intvromlist));
|
|
|
|
// First time in we use the config setting to determine where we open files...
|
|
if (bFirstTimeLoad)
|
|
{
|
|
bFirstTimeLoad = false;
|
|
if (myGlobalConfig.rom_dir == 1)
|
|
{
|
|
chdir("/ROMS");
|
|
}
|
|
else if (myGlobalConfig.rom_dir == 2)
|
|
{
|
|
chdir("/ROMS/INTV");
|
|
}
|
|
}
|
|
|
|
pdir = opendir(".");
|
|
|
|
if (pdir)
|
|
{
|
|
while (((pent=readdir(pdir))!=NULL))
|
|
{
|
|
strcpy(szName2,pent->d_name);
|
|
szName2[MAX_PATH-1] = NULL;
|
|
if (pent->d_type == DT_DIR)
|
|
{
|
|
if (!( (szName2[0] == '.') && (strlen(szName2) == 1)))
|
|
{
|
|
// Filter out the emulator directories from the list
|
|
if (strcasecmp(szName2, "BIOS") == 0) continue;
|
|
if (strcasecmp(szName2, "bios") == 0) continue;
|
|
if (strcasecmp(szName2, "MAN") == 0) continue;
|
|
if (strcasecmp(szName2, "man") == 0) continue;
|
|
if (strcasecmp(szName2, "OVL") == 0) continue;
|
|
if (strcasecmp(szName2, "ovl") == 0) continue;
|
|
if (strcasecmp(szName2, "SAV") == 0) continue;
|
|
if (strcasecmp(szName2, "sav") == 0) continue;
|
|
|
|
intvromlist[countintv].directory = true;
|
|
strcpy(intvromlist[countintv].filename,szName2);
|
|
countintv++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ------------------------------------------------
|
|
// Filter out the BIOS files from the list...
|
|
// ------------------------------------------------
|
|
if (strcasecmp(szName2, "grom.bin") == 0) continue;
|
|
if (strcasecmp(szName2, "exec.bin") == 0) continue;
|
|
if (strcasecmp(szName2, "wbexec.bin") == 0) continue;
|
|
if (strcasecmp(szName2, "ivoice.bin") == 0) continue;
|
|
if (strcasecmp(szName2, "ecs.bin") == 0) continue;
|
|
if (strstr(szName2, "[BIOS]") != NULL) continue;
|
|
if (strstr(szName2, "[bios]") != NULL) continue;
|
|
if ((szName2[0] == '.') && (szName2[1] == '_')) continue; // For MAC files with the underscore starting a name
|
|
|
|
if (strlen(szName2)>4)
|
|
{
|
|
if ( (strcasecmp(strrchr(szName2, '.'), ".int") == 0) ||
|
|
(strcasecmp(strrchr(szName2, '.'), ".bin") == 0) ||
|
|
(strcasecmp(strrchr(szName2, '.'), ".rom") == 0) )
|
|
{
|
|
intvromlist[countintv].favorite = isFavorite(szName2);
|
|
if (bFavsOnlyMode && !intvromlist[countintv].favorite)
|
|
{
|
|
continue;
|
|
}
|
|
intvromlist[countintv].directory = false;
|
|
strcpy(intvromlist[countintv].filename,szName2);
|
|
countintv++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir(pdir);
|
|
}
|
|
|
|
// ----------------------------------------------
|
|
// If we found any files, go sort the list...
|
|
// ----------------------------------------------
|
|
if (countintv)
|
|
{
|
|
qsort (intvromlist, countintv, sizeof (FICA_INTV), intvFilescmp);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Display the files - up to 18 in a page. This is carried over from the
|
|
// oldest emulators I've worked on and there is some bug in here where
|
|
// it occasinoally gets out of sync... it's minor enough to not be an
|
|
// issue but at some point this routine should be re-written...
|
|
// --------------------------------------------------------------------------
|
|
void dsDisplayFiles(unsigned int NoDebGame,u32 ucSel)
|
|
{
|
|
unsigned short ucBcl,ucGame;
|
|
|
|
// Display all games if possible
|
|
unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32);
|
|
dmaFillWords(dmaVal | (dmaVal<<16),(void*) (bgGetMapPtr(bg1b)),32*24*2);
|
|
sprintf(szName,"%04d/%04d %s",(int)(1+ucSel+NoDebGame),countintv, (bFavsOnlyMode ? "FAVS":"GAMES"));
|
|
dsPrintValue(16-strlen(szName)/2,2,0,szName);
|
|
dsPrintValue(31,4,0,(char *) (NoDebGame>0 ? "<" : " "));
|
|
dsPrintValue(31,20,0,(char *) (NoDebGame+14<countintv ? ">" : " "));
|
|
|
|
dsDisplayLoadInstructions();
|
|
|
|
for (ucBcl=0;ucBcl<17; ucBcl++)
|
|
{
|
|
ucGame= ucBcl+NoDebGame;
|
|
if (ucGame < countintv)
|
|
{
|
|
strcpy(szName,intvromlist[ucGame].filename);
|
|
szName[29]='\0';
|
|
if (intvromlist[ucGame].directory)
|
|
{
|
|
szName[27]='\0';
|
|
sprintf(szName2,"[%s]",szName);
|
|
dsPrintValue(0,4+ucBcl,(ucSel == ucBcl ? 1 : 0),szName2);
|
|
}
|
|
else
|
|
{
|
|
if (intvromlist[ucGame].favorite)
|
|
{
|
|
dsPrintValue(0,4+ucBcl,0, (char*)"@");
|
|
}
|
|
dsPrintValue(1,4+ucBcl,(ucSel == ucBcl ? 1 : 0),szName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define LOAD_OPTION_MENU_ITEMS 11
|
|
const char *load_options_menu[LOAD_OPTION_MENU_ITEMS] =
|
|
{
|
|
"LOAD GAME NORMALLY",
|
|
"LOAD GAME WITH STOCK INTY",
|
|
"LOAD GAME WITH ONLY JLP",
|
|
"LOAD GAME WITH ONLY IVOICE",
|
|
"LOAD GAME WITH ONLY ECS",
|
|
"LOAD GAME WITH JLP+IVOICE",
|
|
"LOAD GAME WITH JLP+ECS",
|
|
"LOAD GAME WITH ECS+IVOICE",
|
|
"LOAD GAME WITH JLP+ECS+IV",
|
|
"LOAD GAME WITH TUTORVISION",
|
|
"EXIT THIS MENU",
|
|
};
|
|
|
|
UINT8 LoadWithOptions(void)
|
|
{
|
|
UINT8 current_entry = 0;
|
|
|
|
dsShowBannerScreen();
|
|
swiWaitForVBlank();
|
|
dsPrintValue(3,3,0, (char*) "LOAD GAME OPTIONS ");
|
|
dsPrintValue(3,20,0, (char*)"PRESS UP/DOWN AND A=SELECT");
|
|
|
|
for (int i=0; i<LOAD_OPTION_MENU_ITEMS; i++)
|
|
{
|
|
dsPrintValue(3,5+i, (i==0 ? 1:0), (char*)load_options_menu[i]);
|
|
}
|
|
|
|
int last_keys_pressed = -1;
|
|
while (1)
|
|
{
|
|
int keys_pressed = keysCurrent();
|
|
|
|
if (keys_pressed != last_keys_pressed)
|
|
{
|
|
last_keys_pressed = keys_pressed;
|
|
if (keys_pressed & KEY_DOWN)
|
|
{
|
|
dsPrintValue(3,5+current_entry, 0, (char*)load_options_menu[current_entry]);
|
|
if (current_entry < (LOAD_OPTION_MENU_ITEMS-1)) current_entry++; else current_entry=0;
|
|
dsPrintValue(3,5+current_entry, 1, (char*)load_options_menu[current_entry]);
|
|
}
|
|
if (keys_pressed & KEY_UP)
|
|
{
|
|
dsPrintValue(3,5+current_entry, 0, (char*)load_options_menu[current_entry]);
|
|
if (current_entry > 0) current_entry--; else current_entry=(LOAD_OPTION_MENU_ITEMS-1);
|
|
dsPrintValue(3,5+current_entry, 1, (char*)load_options_menu[current_entry]);
|
|
}
|
|
if (keys_pressed & KEY_A)
|
|
{
|
|
return current_entry;
|
|
}
|
|
swiWaitForVBlank();
|
|
}
|
|
}
|
|
return (LOAD_OPTION_MENU_ITEMS-1);
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Let the user see all relevant ROM files and pick the one they want...
|
|
// --------------------------------------------------------------------------
|
|
unsigned int dsWaitForRom(char *chosen_filename)
|
|
{
|
|
u8 bDone=false, bRet=false;
|
|
u16 ucHaut=0x00, ucBas=0x00,ucSHaut=0x00, ucSBas=0x00,romSelected= 0, firstRomDisplay=0,nbRomPerPage, uNbRSPage;
|
|
u16 uLenFic=0, ucFlip=0, ucFlop=0;
|
|
|
|
strcpy(chosen_filename, "tmpz");
|
|
intvFindFiles(); // Initial get of files...
|
|
|
|
dsShowBannerScreen();
|
|
|
|
nbRomPerPage = (countintv>=17 ? 17 : countintv);
|
|
uNbRSPage = (countintv>=5 ? 5 : countintv);
|
|
|
|
if (ucFicAct>countintv-nbRomPerPage)
|
|
{
|
|
firstRomDisplay=countintv-nbRomPerPage;
|
|
romSelected=ucFicAct-countintv+nbRomPerPage;
|
|
}
|
|
else
|
|
{
|
|
firstRomDisplay=ucFicAct;
|
|
romSelected=0;
|
|
}
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
|
|
// -----------------------------------------------------
|
|
// Until the user selects a file or exits the menu...
|
|
// -----------------------------------------------------
|
|
while (!bDone)
|
|
{
|
|
if (keysCurrent() & KEY_UP)
|
|
{
|
|
if (!ucHaut)
|
|
{
|
|
ucFicAct = (ucFicAct>0 ? ucFicAct-1 : countintv-1);
|
|
if (romSelected>uNbRSPage) { romSelected -= 1; }
|
|
else {
|
|
if (firstRomDisplay>0) { firstRomDisplay -= 1; }
|
|
else {
|
|
if (romSelected>0) { romSelected -= 1; }
|
|
else {
|
|
firstRomDisplay=countintv-nbRomPerPage;
|
|
romSelected=nbRomPerPage-1;
|
|
}
|
|
}
|
|
}
|
|
ucHaut=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else {
|
|
|
|
ucHaut++;
|
|
if (ucHaut>10) ucHaut=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else
|
|
{
|
|
ucHaut = 0;
|
|
}
|
|
if (keysCurrent() & KEY_DOWN)
|
|
{
|
|
if (!ucBas) {
|
|
ucFicAct = (ucFicAct< countintv-1 ? ucFicAct+1 : 0);
|
|
if (romSelected<uNbRSPage-1) { romSelected += 1; }
|
|
else {
|
|
if (firstRomDisplay<countintv-nbRomPerPage) { firstRomDisplay += 1; }
|
|
else {
|
|
if (romSelected<nbRomPerPage-1) { romSelected += 1; }
|
|
else {
|
|
firstRomDisplay=0;
|
|
romSelected=0;
|
|
}
|
|
}
|
|
}
|
|
ucBas=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else
|
|
{
|
|
ucBas++;
|
|
if (ucBas>10) ucBas=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else {
|
|
ucBas = 0;
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// Left and Right on the D-Pad will scroll 1 page at a time...
|
|
// -------------------------------------------------------------
|
|
if (keysCurrent() & KEY_RIGHT)
|
|
{
|
|
if (!ucSBas)
|
|
{
|
|
ucFicAct = (ucFicAct< countintv-nbRomPerPage ? ucFicAct+nbRomPerPage : countintv-nbRomPerPage);
|
|
if (firstRomDisplay<countintv-nbRomPerPage) { firstRomDisplay += nbRomPerPage; }
|
|
else { firstRomDisplay = countintv-nbRomPerPage; }
|
|
if (ucFicAct == countintv-nbRomPerPage) romSelected = 0;
|
|
ucSBas=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else
|
|
{
|
|
ucSBas++;
|
|
if (ucSBas>10) ucSBas=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else {
|
|
ucSBas = 0;
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// Left and Right on the D-Pad will scroll 1 page at a time...
|
|
// -------------------------------------------------------------
|
|
if (keysCurrent() & KEY_LEFT)
|
|
{
|
|
if (!ucSHaut)
|
|
{
|
|
ucFicAct = (ucFicAct> nbRomPerPage ? ucFicAct-nbRomPerPage : 0);
|
|
if (firstRomDisplay>nbRomPerPage) { firstRomDisplay -= nbRomPerPage; }
|
|
else { firstRomDisplay = 0; }
|
|
if (ucFicAct == 0) romSelected = 0;
|
|
if (romSelected > ucFicAct) romSelected = ucFicAct;
|
|
ucSHaut=0x01;
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
else
|
|
{
|
|
ucSHaut++;
|
|
if (ucSHaut>10) ucSHaut=0;
|
|
}
|
|
uLenFic=0; ucFlip=0; ucFlop=0;
|
|
}
|
|
else {
|
|
ucSHaut = 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// L/R Shoulder Buttons will toggle between all games and just favs...
|
|
// --------------------------------------------------------------------
|
|
if (keysCurrent() & (KEY_L | KEY_R))
|
|
{
|
|
bFavsOnlyMode = !bFavsOnlyMode;
|
|
intvFindFiles();
|
|
ucFicAct = 0;
|
|
nbRomPerPage = (countintv>=16 ? 16 : countintv);
|
|
uNbRSPage = (countintv>=5 ? 5 : countintv);
|
|
if (ucFicAct>countintv-nbRomPerPage) {
|
|
firstRomDisplay=countintv-nbRomPerPage;
|
|
romSelected=ucFicAct-countintv+nbRomPerPage;
|
|
}
|
|
else {
|
|
firstRomDisplay=ucFicAct;
|
|
romSelected=0;
|
|
}
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
while (keysCurrent() & (KEY_L | KEY_R))
|
|
;
|
|
WAITVBL;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// They B key will exit out of the ROM selection without picking a new game
|
|
// -------------------------------------------------------------------------
|
|
if ( keysCurrent() & KEY_B )
|
|
{
|
|
bDone=true;
|
|
while (keysCurrent() & KEY_B);
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
// The SELECT key will toggle this game as a 'favorite'
|
|
// -------------------------------------------------------
|
|
if ( keysCurrent() & KEY_SELECT )
|
|
{
|
|
if (!intvromlist[ucFicAct].directory)
|
|
{
|
|
if (intvromlist[ucFicAct].favorite)
|
|
clrFavorite(intvromlist[ucFicAct].filename);
|
|
else
|
|
setFavorite(intvromlist[ucFicAct].filename);
|
|
intvromlist[ucFicAct].favorite = isFavorite(intvromlist[ucFicAct].filename);
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
while (keysCurrent() & KEY_SELECT);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Since the user can select favorites, we also allow saving those out here.
|
|
// --------------------------------------------------------------------------
|
|
if ( keysCurrent() & KEY_START )
|
|
{
|
|
dsPrintValue(0,23,0, (char*)" SAVING CONFIGURATION ");
|
|
SaveConfig(0x00000000, FALSE);
|
|
WAITVBL;WAITVBL;WAITVBL;
|
|
dsDisplayLoadInstructions();
|
|
while (keysCurrent() & KEY_START);
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Any of these keys will pick the current ROM and try to load it...
|
|
// -------------------------------------------------------------------
|
|
if (keysCurrent() & KEY_A || keysCurrent() & KEY_X)
|
|
{
|
|
if (!intvromlist[ucFicAct].directory)
|
|
{
|
|
UINT8 opt = 0;
|
|
bUseJLP=false;
|
|
bUseIVoice=false;
|
|
bUseECS=false;
|
|
bUseTutorvision=false;
|
|
load_options = 0x00; // If the user selects a specific load configuration, the high bit of this will be set plus some combo of the LOAD_WITH_xxxx bits
|
|
if (keysCurrent() & KEY_X)
|
|
{
|
|
opt = LoadWithOptions();
|
|
if (opt == 0) {load_options = LOAD_NORMALLY; } // Load Normally - remove any overrides...
|
|
if (opt == 1) {load_options = LOAD_WITH_STOCK_INTY;}
|
|
if (opt == 2) {load_options = LOAD_WITH_JLP;}
|
|
if (opt == 3) {load_options = LOAD_WITH_IVOICE;}
|
|
if (opt == 4) {load_options = LOAD_WITH_ECS;}
|
|
if (opt == 5) {load_options = LOAD_WITH_JLP | LOAD_WITH_IVOICE;}
|
|
if (opt == 6) {load_options = LOAD_WITH_JLP | LOAD_WITH_ECS;}
|
|
if (opt == 7) {load_options = LOAD_WITH_ECS | LOAD_WITH_IVOICE;}
|
|
if (opt == 8) {load_options = LOAD_WITH_JLP | LOAD_WITH_ECS | LOAD_WITH_IVOICE;}
|
|
if (opt == 9) {load_options = LOAD_WITH_TUTORVISION;}
|
|
while (keysCurrent() & KEY_X);
|
|
WAITVBL;
|
|
}
|
|
if (opt != (LOAD_OPTION_MENU_ITEMS-1))
|
|
{
|
|
bRet=true;
|
|
bDone=true;
|
|
strcpy(chosen_filename, intvromlist[ucFicAct].filename);
|
|
}
|
|
else
|
|
{
|
|
bRet = false;
|
|
bDone = false;
|
|
dsShowBannerScreen();
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
}
|
|
WAITVBL;
|
|
}
|
|
else
|
|
{
|
|
chdir(intvromlist[ucFicAct].filename);
|
|
intvFindFiles();
|
|
ucFicAct = 0;
|
|
nbRomPerPage = (countintv>=16 ? 16 : countintv);
|
|
uNbRSPage = (countintv>=5 ? 5 : countintv);
|
|
if (ucFicAct>countintv-nbRomPerPage) {
|
|
firstRomDisplay=countintv-nbRomPerPage;
|
|
romSelected=ucFicAct-countintv+nbRomPerPage;
|
|
}
|
|
else {
|
|
firstRomDisplay=ucFicAct;
|
|
romSelected=0;
|
|
}
|
|
dsDisplayFiles(firstRomDisplay,romSelected);
|
|
while (keysCurrent() & KEY_A);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------
|
|
// If the filename is too long... scroll it.
|
|
// --------------------------------------------
|
|
if (strlen(intvromlist[ucFicAct].filename) > 29)
|
|
{
|
|
ucFlip++;
|
|
if (ucFlip >= 15)
|
|
{
|
|
ucFlip = 0;
|
|
uLenFic++;
|
|
if ((uLenFic+29)>strlen(intvromlist[ucFicAct].filename))
|
|
{
|
|
ucFlop++;
|
|
if (ucFlop >= 15)
|
|
{
|
|
uLenFic=0;
|
|
ucFlop = 0;
|
|
}
|
|
else
|
|
uLenFic--;
|
|
}
|
|
strncpy(szName,intvromlist[ucFicAct].filename+uLenFic,29);
|
|
szName[29] = '\0';
|
|
dsPrintValue(1,4+romSelected,1,szName);
|
|
}
|
|
}
|
|
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// We are going back to the main emulation now - restore bottom screen.
|
|
// ----------------------------------------------------------------------
|
|
dsShowScreenMain(false, false);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// End of Line
|