Esempio n. 1
0
// Checks if devices are available, prints name of device being detected for slow init devices
void populateDeviceAvailability() {
	DrawFrameStart();
	DrawMessageBox(D_INFO, "Detecting devices ...\nThis can be skipped by holding B next time");
	DrawFrameFinish();
	if(PAD_ButtonsHeld(0) & PAD_BUTTON_B) {
		deviceHandler_setAllDevicesAvailable();
		return;
	}
	const DISC_INTERFACE* carda = &__io_gcsda;
	const DISC_INTERFACE* cardb = &__io_gcsdb;
	// DVD
	deviceHandler_setDeviceAvailable(DVD_DISC, swissSettings.hasDVDDrive);
	// SD Gecko
	DrawFrameStart();
	DrawMessageBox(D_INFO, "Detecting devices [SD] ...\nThis can be skipped by holding B next time");
	DrawFrameFinish();
	deviceHandler_setDeviceAvailable(SD_CARD, carda->isInserted() || cardb->isInserted());
	// IDE-EXI
	DrawFrameStart();
	DrawMessageBox(D_INFO, "Detecting devices [IDE-EXI] ...\nThis can be skipped by holding B next time");
	DrawFrameFinish();
	deviceHandler_setDeviceAvailable(IDEEXI, ide_exi_inserted(0) || ide_exi_inserted(1));
	// Qoob
	deviceHandler_setDeviceAvailable(QOOB_FLASH, 0);	// Hidden by default, add auto detect at some point
	// WODE
	deviceHandler_setDeviceAvailable(WODE, 0);	// Hidden by default, add auto detect at some point
	// Memory card
	DrawFrameStart();
	DrawMessageBox(D_INFO, "Detecting devices [Memory Card] ...\nThis can be skipped by holding B next time");
	DrawFrameFinish();
	deviceHandler_setDeviceAvailable(MEMCARD, (initialize_card(0)==CARD_ERROR_READY) || (initialize_card(1)==CARD_ERROR_READY));
	// WKF/WASP
	DrawFrameStart();
	DrawMessageBox(D_INFO, "Detecting devices [WKF/WASP] ...\nThis can be skipped by holding B next time");
	DrawFrameFinish();
	deviceHandler_setDeviceAvailable(WKF, swissSettings.hasDVDDrive && (__wkfSpiReadId() != 0 && __wkfSpiReadId() != 0xFFFFFFFF));
	// USB Gecko
	deviceHandler_setDeviceAvailable(USBGECKO, usb_isgeckoalive(1));
	// BBA/SAMBA
	deviceHandler_setDeviceAvailable(SAMBA, exi_bba_exists());
	// System, always there
	deviceHandler_setDeviceAvailable(SYS, 1);
}
Esempio n. 2
0
void load_config(int forceSlot) {

	// Try to open up the config .ini in case it hasn't been opened already (SD, IDE-EXI only)
	if(!config_init(forceSlot)) {
		if(curDevice == SD_CARD || curDevice == IDEEXI || forceSlot) {
			if(!config_create(forceSlot)) {
				DrawFrameStart();
				DrawMessageBox(D_INFO,"Failed to create configuration file!");
				DrawFrameFinish();
				sleep(1);
			}
		}
	}
	else {
		DrawFrameStart();
		sprintf(txtbuffer,"Loaded %i entries from the config file",config_get_count());
		DrawMessageBox(D_INFO,txtbuffer);
		DrawFrameFinish();
		memcpy(&swissSettings, config_get_swiss_settings(), sizeof(SwissSettings));
	}
}
int deviceHandler_USBGecko_init(file_handle* file) {
	DrawFrameStart();
	DrawMessageBox(D_INFO,"Looking for USBGecko in Slot B");
	DrawFrameFinish();
	if(usb_isgeckoalive(1)) {
		int retries = 1000;
		
		DrawFrameStart();
		DrawMessageBox(D_INFO,"Waiting for PC ...");
		DrawFrameFinish();
		
		usb_flush(1);
		usbgecko_lock_file(0);
		// Wait for the PC and retry 1000 times
		while(!usbgecko_pc_ready() && retries) {
			VIDEO_WaitVSync();
			retries--;
		}
		if(!retries) {
			DrawFrameStart();
			DrawMessageBox(D_INFO,"Couldn't find PC!");
			DrawFrameFinish();
			sleep(5);
			return 0;	// Didn't find the PC
		}
		else {
			DrawFrameStart();
			DrawMessageBox(D_INFO,"Found PC !!");
			DrawFrameFinish();
			return 1;
		}
	}
	else {
		return 0;
	}
}
int deviceHandler_USBGecko_readDir(file_handle* ffile, file_handle** dir, unsigned int type){	
  
	// Set everything up to read
	int num_entries = 0, i = 0;
	file_handle *entry = NULL;
	if(strlen(ffile->name)!=1) {
		i = num_entries = 1;
		*dir = malloc( num_entries * sizeof(file_handle) );
		memset(*dir,0,sizeof(file_handle) * num_entries);
		(*dir)[0].fileAttrib = IS_SPECIAL;
		strcpy((*dir)[0].name, "..");
	}
	
	DrawFrameStart();
	DrawMessageBox(D_INFO,"Read directory!");
	DrawFrameFinish();
	// Read each entry of the directory
	int res = usbgecko_open_dir(&ffile->name[0]);
	if(!res) return -1;
	while( (entry = usbgecko_get_entry()) != NULL ){
		// Make sure we have room for this one
		if(i == num_entries) {
			++num_entries;
			*dir = realloc( *dir, num_entries * sizeof(file_handle) ); 
		}
		sprintf((*dir)[i].name, "%s", entry->name);
		(*dir)[i].offset		= 0;
		(*dir)[i].size			= entry->size;
		(*dir)[i].fileAttrib	= entry->fileAttrib;
		(*dir)[i].fp 			= 0;
		(*dir)[i].fileBase		= 0;
		(*dir)[i].meta = 0;
		++i;
	}
	
  return num_entries;
}
Esempio n. 5
0
/* Initialise Video, PAD, DVD, Font */
void* Initialise (void)
{
	VIDEO_Init ();
	PAD_Init ();  
	DVD_Init(); 
	*(volatile unsigned long*)0xcc00643c = 0x00000000; //allow 32mhz exi bus
	
	// Disable IPL modchips to allow access to IPL ROM fonts
	ipl_set_config(6); 
	usleep(1000); //wait for modchip to disable (overkill)
	
	
	__SYS_ReadROM(IPLInfo,256,0);	// Read IPL tag

	// Wii has no IPL tags for "PAL" so let libOGC figure out the video mode
	if(!is_gamecube()) {
		vmode = VIDEO_GetPreferredMode(NULL); //Last mode used
	}
	else {	// Gamecube, determine based on IPL
		int retPAD = 0, retCnt = 10000;
		while(retPAD <= 0 && retCnt >= 0) { retPAD = PAD_ScanPads(); usleep(100); retCnt--; }
		// L Trigger held down ignores the fact that there's a component cable plugged in.
		if(VIDEO_HaveComponentCable() && !(PAD_ButtonsDown(0) & PAD_TRIGGER_L)) {
			if(strstr(IPLInfo,"MPAL")!=NULL) {
				swissSettings.sramVideo = 2;
				vmode = &TVMpal480Prog; //Progressive 480p
			}
			else if((strstr(IPLInfo,"PAL")!=NULL)) {
				swissSettings.sramVideo = 1;
				vmode = &TVPal576ProgScale; //Progressive 576p
			}
			else {
				swissSettings.sramVideo = 0;
				vmode = &TVNtsc480Prog; //Progressive 480p
			}
		}
		else {
			//try to use the IPL region
			if(strstr(IPLInfo,"MPAL")!=NULL) {
				swissSettings.sramVideo = 2;
				vmode = &TVMpal480IntDf;        //PAL-M
			}
			else if(strstr(IPLInfo,"PAL")!=NULL) {
				swissSettings.sramVideo = 1;
				vmode = &TVPal576IntDfScale;         //PAL
			}
			else {
				swissSettings.sramVideo = 0;
				vmode = &TVNtsc480IntDf;        //NTSC
			}
		}
	}
	initialise_video(vmode);
	populateVideoStr(vmode);

	init_font();
	init_textures();
	whichfb = 0;
	
	drive_version(&driveVersion[0]);
	swissSettings.hasDVDDrive = *(u32*)&driveVersion[0] ? 1 : 0;
	
	if(!driveVersion[0]) {
		// Reset DVD if there was a modchip
		DrawFrameStart();
		WriteFontStyled(640/2, 250, "Initialise DVD .. (HOLD B if NO DVD Drive)", 0.8f, true, defaultColor);
		DrawFrameFinish();
		dvd_reset();	// low-level, basic
		dvd_read_id();
		if(!(PAD_ButtonsHeld(0) & PAD_BUTTON_B)) {
			dvd_set_streaming(*(char*)0x80000008);
		}
		drive_version(&driveVersion[0]);
		swissSettings.hasDVDDrive = *(u32*)&driveVersion[0] ? 1 : 0;
		if(!swissSettings.hasDVDDrive) {
			DrawFrameStart();
			DrawMessageBox(D_INFO, "No DVD Drive Detected !!");
			DrawFrameFinish();
			sleep(2);
		}
	}
	
	return xfb[0];
}
Esempio n. 6
0
// Returns the number of filesToPatch and fills out the filesToPatch array passed in (pre-allocated)
int parse_gcm(file_handle *file, ExecutableFile *filesToPatch) {

	DiskHeader header;
	char	*FST; 
	char	filename[256];
	int		numFiles = 0;
		
	// Grab disc header
	memset(&header,0,sizeof(DiskHeader));
 	deviceHandler_seekFile(file,0,DEVICE_HANDLER_SEEK_SET);
 	if(deviceHandler_readFile(file,&header,sizeof(DiskHeader)) != sizeof(DiskHeader)) {
		return -1;
	}
 	  
 	// Alloc and read FST
	FST=(char*)memalign(32,header.FSTSize); 
	if(!FST) {
		return -1;
	}
	deviceHandler_seekFile(file,header.FSTOffset,DEVICE_HANDLER_SEEK_SET);
 	if(deviceHandler_readFile(file,FST,header.FSTSize) != header.FSTSize) {
		free(FST); 
		return -1;
	}

	u32 entries=*(unsigned int*)&FST[8];
	u32 string_table_offset=FST_ENTRY_SIZE*entries;
		
	int i;
	// go through every entry
	for (i=1;i<entries;i++) 
	{ 
		u32 offset=i*0x0c; 
		if(FST[offset]==0) //skip directories
		{ 
			u32 file_offset,size = 0;
			u32 filename_offset=((*(unsigned int*)&FST[offset]) & 0x00FFFFFF); 
			memset(&filename[0],0,256);
			memcpy(&filename[0],&FST[string_table_offset+filename_offset],255); 
			memcpy(&file_offset,&FST[offset+4],4);
			memcpy(&size,&FST[offset+8],4);
			if(endsWith(filename,".dol") || endsWith(filename,".DOL")) {
				filesToPatch[numFiles].offset = file_offset;
				filesToPatch[numFiles].size = size;
				filesToPatch[numFiles].type = PATCH_DOL;
				memcpy(&filesToPatch[numFiles].name,&filename[0],64); 
				numFiles++;
			}
			if((endsWith(filename,".elf") || endsWith(filename,".ELF")) && size < 12*1024*1024) {
				filesToPatch[numFiles].offset = file_offset;
				filesToPatch[numFiles].size = size;
				filesToPatch[numFiles].type = PATCH_ELF;
				memcpy(&filesToPatch[numFiles].name,&filename[0],64); 
				numFiles++;
			}
			if(strstr(filename,"execD.img")) {
				filesToPatch[numFiles].offset = file_offset;
				filesToPatch[numFiles].size = size;
				filesToPatch[numFiles].type = PATCH_LOADER;
				memcpy(&filesToPatch[numFiles].name,&filename[0],64); 
				numFiles++;
			}
			if(endsWith(filename,".tgc") || endsWith(filename,".TGC")) {
				// Go through all the TGC's internal files
				ExecutableFile *filesInTGCToPatch = memalign(32, sizeof(ExecutableFile)*32);
				int numTGCFilesToPatch = parse_tgc(file, filesInTGCToPatch, file_offset), j;
				for(j=0; j<numTGCFilesToPatch; j++) {
					memcpy(&filesToPatch[numFiles], &filesInTGCToPatch[j], sizeof(ExecutableFile));
					numFiles++;
				}
				free(filesInTGCToPatch);
			}
		} 
	}
	free(FST);
	
	// Some games contain a single "default.dol", these do not need 
	// pre-patching because they are what is actually pointed to by the apploader (and loaded by us)
	if(numFiles==1 && (!strcmp(&filesToPatch[0].name[0],"default.dol"))) {
		numFiles = 0;
	}
	// Multi-DOL games may re-load the main DOL, so make sure we patch it too.
	if(numFiles>0) {
		DOLHEADER dolhdr;
		u32 main_dol_size = 0;
		// Calc size
		deviceHandler_seekFile(file,GCMDisk.DOLOffset,DEVICE_HANDLER_SEEK_SET);
		if(deviceHandler_readFile(file,&dolhdr,DOLHDRLENGTH) != DOLHDRLENGTH) {
			DrawFrameStart();
			DrawMessageBox(D_FAIL, "Failed to read Main DOL Header");
			DrawFrameFinish();
			while(1);
		}
		for (i = 0; i < MAXTEXTSECTION; i++) {
			if (dolhdr.textLength[i] + dolhdr.textOffset[i] > main_dol_size)
				main_dol_size = dolhdr.textLength[i] + dolhdr.textOffset[i];
		}
		for (i = 0; i < MAXDATASECTION; i++) {
			if (dolhdr.dataLength[i] + dolhdr.dataOffset[i] > main_dol_size)
				main_dol_size = dolhdr.dataLength[i] + dolhdr.dataOffset[i];
		}
		filesToPatch[numFiles].offset = GCMDisk.DOLOffset;
		filesToPatch[numFiles].size = main_dol_size;
		filesToPatch[numFiles].type = PATCH_DOL;
		sprintf(filesToPatch[numFiles].name, "Main DOL");
		numFiles++;
		
		// Patch the apploader too!
		// Calc Apploader trailer size
		u32 appldr_info[2];
		deviceHandler_seekFile(file,0x2454,DEVICE_HANDLER_SEEK_SET);
		if(deviceHandler_readFile(file,&appldr_info,8) != 8) {
			DrawFrameStart();
			DrawMessageBox(D_FAIL, "Failed to read Apploader info");
			DrawFrameFinish();
			while(1);
		}
		filesToPatch[numFiles].size = appldr_info[1];
		filesToPatch[numFiles].offset = 0x2460 + appldr_info[0];
		filesToPatch[numFiles].type = PATCH_LOADER;
		sprintf(filesToPatch[numFiles].name, "Apploader Trailer");
		numFiles++;
	}
	return numFiles;
}
Esempio n. 7
0
int patch_gcm(file_handle *file, ExecutableFile *filesToPatch, int numToPatch, int multiDol) {
	int i, num_patched = 0;
	
	// If the current device isn't SD Gecko, init SD Gecko Slot A or B to write patches.
	if(deviceHandler_initial != &initial_SD0 && deviceHandler_initial != &initial_SD1) {
		deviceHandler_setStatEnabled(0);
		if(deviceHandler_FAT_init(&initial_SD0)) {
			savePatchDevice = 0;
		}
		else if(deviceHandler_FAT_init(&initial_SD1)) {
			savePatchDevice = 1;
		}
		deviceHandler_setStatEnabled(1);
	}
	// Already using SD Gecko
	if(deviceHandler_initial == &initial_SD0)
		savePatchDevice = 0;
	else if(deviceHandler_initial == &initial_SD1)
		savePatchDevice = 1;
		
	if(savePatchDevice == -1) {
		DrawFrameStart();
		DrawMessageBox(D_FAIL, "No writable device present\nA SD Gecko must be inserted in\n order to utilise patches for this game.");
		DrawFrameFinish();
		sleep(5);
		return 0;
	}

	char patchFileName[256];
	char patchDirName[256];
	char patchBaseDirName[256];
	char gameID[8];
	memset(&gameID, 0, 8);
	memset(&patchDirName, 0, 256);
	memset(&patchBaseDirName, 0, 256);
	strncpy((char*)&gameID, (char*)&GCMDisk, 4);
	sprintf(&patchDirName[0],"%s:/swiss_patches/%s",(savePatchDevice ? "sdb":"sda"), &gameID[0]);
	sprintf(&patchBaseDirName[0],"%s:/swiss_patches",(savePatchDevice ? "sdb":"sda"));
	print_gecko("Patch dir will be: %s if required\r\n", patchDirName);
	*(u32*)VAR_EXECD_OFFSET = 0xFFFFFFFF;
	// Go through all the possible files we think need patching..
	for(i = 0; i < numToPatch; i++) {
		u32 patched = 0, crc32 = 0;

		sprintf(txtbuffer, "Patching File %i/%i",i+1,numToPatch);
		DrawFrameStart();
		DrawProgressBar((int)(((float)(i+1)/(float)numToPatch)*100), txtbuffer);
		DrawFrameFinish();
			
		// Round up to 32 bytes
		if(filesToPatch[i].size % 0x20) {
			filesToPatch[i].size += (0x20-(filesToPatch[i].size%0x20));
		}
		
		if(filesToPatch[i].size > 8*1024*1024) {
			print_gecko("Skipping %s %iKB too large\r\n", filesToPatch[i].name, filesToPatch[i].size/1024);
			continue;
		}
		print_gecko("Checking %s %iKb\r\n", filesToPatch[i].name, filesToPatch[i].size/1024);
		if(strstr(filesToPatch[i].name, "execD.")) {
			*(u32*)VAR_EXECD_OFFSET = filesToPatch[i].offset;
		}
		if(strstr(filesToPatch[i].name, "iwanagaD.dol") || strstr(filesToPatch[i].name, "switcherD.dol")) {
			continue;	// skip unused PSO files
		}
		int sizeToRead = filesToPatch[i].size;
		u8 *buffer = (u8*)memalign(32, sizeToRead);
		
		deviceHandler_seekFile(file,filesToPatch[i].offset, DEVICE_HANDLER_SEEK_SET);
		if(deviceHandler_readFile(file,buffer,sizeToRead)!= sizeToRead) {
			DrawFrameStart();
			DrawMessageBox(D_FAIL, "Failed to read!");
			DrawFrameFinish();
			sleep(5);
			return 0;
		}
		
		if(curDevice != DVD_DISC) {
			u32 ret = Patch_DVDLowLevelRead(buffer, sizeToRead, filesToPatch[i].type);
			if(READ_PATCHED_ALL != ret)	{
				DrawFrameStart();
				DrawMessageBox(D_FAIL, "Failed to find necessary functions for patching!");
				DrawFrameFinish();
				sleep(5);
			}
			else
				patched += 1;
		}
				
		if(swissSettings.debugUSB && usb_isgeckoalive(1) && !swissSettings.wiirdDebug) {
			patched += Patch_Fwrite(buffer, sizeToRead);
		}
		
		if(swissSettings.wiirdDebug || getEnabledCheatsSize() > 0) {
			Patch_CheatsHook(buffer, sizeToRead, filesToPatch[i].type);
		}
		
		if(curDevice == DVD_DISC && is_gamecube()) {
			patched += Patch_DVDLowLevelReadForDVD(buffer, sizeToRead, filesToPatch[i].type);
			patched += Patch_DVDReset(buffer, sizeToRead);
		}
		
		patched += Patch_VidMode(buffer, sizeToRead, filesToPatch[i].type);
		patched += Patch_FontEnc(buffer, sizeToRead);
		if(swissSettings.forceWidescreen)
			Patch_WideAspect(buffer, sizeToRead, filesToPatch[i].type);
		if(swissSettings.forceAnisotropy)
			Patch_TexFilt(buffer, sizeToRead, filesToPatch[i].type);
		if(patched) {
			// File handle for a patch we might need to write
			FILE *patchFile = 0;
			memset(patchFileName, 0, 256);
		
			// Make a base patches dir if we don't have one already
			if(mkdir(&patchBaseDirName[0], 0777) != 0) {
				if(errno != EEXIST) {
					return -2;
				}
			}
			if(mkdir(&patchDirName[0], 0777) != 0) {
				if(errno != EEXIST) {
					return -2;
				}
			}
			sprintf(patchFileName, "%s/%i",patchDirName, num_patched);

			// Work out the crc32
			crc32 = Crc32_ComputeBuf( 0, buffer, (u32) sizeToRead);

			// See if this file already exists, if it does, match crc
			patchFile = fopen( patchFileName, "rb" );
			if(patchFile) {
				//print_gecko("Old Patch exists\r\n");
				u32 oldCrc32 = 0;
				fseek(patchFile, 0L, SEEK_END);
				u32 file_size = ftell(patchFile);
				fseek(patchFile, file_size-4, SEEK_SET);
				fread(&oldCrc32, 1, 4, patchFile);
				if(oldCrc32 == crc32) {
					num_patched++;
					fclose(patchFile);
					free(buffer);
					print_gecko("CRC matched, no need to patch again\r\n");
					continue;
				}
				else {
					remove(patchFileName);
					fclose(patchFile);
					print_gecko("CRC mismatch, writing patch again\r\n");
				}
			}
			// Otherwise, write a file out for this game with the patched buffer inside.
			print_gecko("Writing patch file: %s %i bytes (disc offset %08X)\r\n", patchFileName, sizeToRead, filesToPatch[i].offset);
			patchFile = fopen(patchFileName, "wb");
			fwrite(buffer, 1, sizeToRead, patchFile);
			u32 magic = SWISS_MAGIC;
			fwrite(&filesToPatch[i].offset, 1, 4, patchFile);
			fwrite(&filesToPatch[i].size, 1, 4, patchFile);
			fwrite(&magic, 1, 4, patchFile);
			fwrite(&crc32, 1, 4, patchFile);
			fclose(patchFile);
			num_patched++;
		}
		free(buffer);
	}

	return num_patched;
}
Esempio n. 8
0
int show_settings(file_handle *file, ConfigEntry *config) {
    int page = 0, option = 0;

    // Refresh SRAM in case user changed it from IPL
    refreshSRAM();

    // Copy current settings to a temp copy in case the user cancels out
    memcpy((void*)&tempSettings,(void*)&swissSettings, sizeof(SwissSettings));

    // Setup the settings for the current game
    if(config != NULL) {
        page = 2;
    }

    while (PAD_ButtonsHeld(0) & PAD_BUTTON_A) {
        VIDEO_WaitVSync ();
    }
    while(1) {
        settings_draw_page(page, option, file);
        while (!((PAD_ButtonsHeld(0) & PAD_BUTTON_RIGHT)
                 || (PAD_ButtonsHeld(0) & PAD_BUTTON_LEFT)
                 || (PAD_ButtonsHeld(0) & PAD_BUTTON_UP)
                 || (PAD_ButtonsHeld(0) & PAD_BUTTON_DOWN)
                 || (PAD_ButtonsHeld(0) & PAD_BUTTON_B)
                 || (PAD_ButtonsHeld(0) & PAD_BUTTON_A)
                 || (PAD_ButtonsHeld(0) & PAD_TRIGGER_R)
                 || (PAD_ButtonsHeld(0) & PAD_TRIGGER_L)))
        {
            VIDEO_WaitVSync ();
        }
        u16 btns = PAD_ButtonsHeld(0);
        if(btns & PAD_BUTTON_RIGHT) {
            // If we're on a button (Back, Next, Save, Exit), allow left/right movement
            if((page != 1) && (option >= settings_count_pp[page]-2) && option < settings_count_pp[page]) {
                option++;
            }
            else if((page == 1) && (option >= settings_count_pp[page]-3) && option < settings_count_pp[page]) {
                option++;
            }
            else {
                settings_toggle(page, option, 1, file);
            }
        }
        if(btns & PAD_BUTTON_LEFT) {
            // If we're on a button (Back, Next, Save, Exit), allow left/right movement
            if((page != 1) && (option > settings_count_pp[page]-2)) {
                option--;
            }
            else if((page == 1) && (option > settings_count_pp[page]-3)) {
                option--;
            }
            else {
                settings_toggle(page, option, -1, file);
            }
        }
        if((btns & PAD_BUTTON_DOWN) && option < settings_count_pp[page])
            option++;
        if((btns & PAD_BUTTON_UP) && option > 0)
            option--;
        if((btns & PAD_TRIGGER_R) && page < 2) {
            page++;
            option = 0;
        }
        if((btns & PAD_TRIGGER_L) && page > 0) {
            page--;
            option = 0;
        }
        if((btns & PAD_BUTTON_B))
            option = settings_count_pp[page];
        // Handle all options/buttons here
        if((btns & PAD_BUTTON_A)) {
            // Generic Save/Cancel/Back/Next button actions
            if(option == settings_count_pp[page]-1) {
                DrawFrameStart();
                DrawMessageBox(D_INFO,"Saving changes!");
                DrawFrameFinish();
                // Change Swiss video mode if it was modified.
                if(tempSettings.uiVMode != swissSettings.uiVMode) {
                    GXRModeObj *newmode = getModeFromSwissSetting(swissSettings.uiVMode);
                    initialise_video(newmode);
                    vmode = newmode;
                }
                // Save settings to SRAM
                sram = __SYS_LockSram();
                sram->lang = swissSettings.sramLanguage;
                sram->flags = swissSettings.sramStereo ? (sram->flags|0x04):(sram->flags&~0x04);
                sram->flags = (swissSettings.sramVideo&0x03)|(sram->flags&~0x03);
                __SYS_UnlockSram(1);
                while(!__SYS_SyncSram());
                // Update our .ini
                if(config != NULL) {
                    config->gameVMode = swissSettings.gameVMode;
                    config->softProgressive = swissSettings.softProgressive;
                    config->muteAudioStreaming = swissSettings.muteAudioStreaming;
                    config->forceWidescreen = swissSettings.forceWidescreen;
                    config->forceAnisotropy = swissSettings.forceAnisotropy;
                    config->forceEncoding = swissSettings.forceEncoding;
                }
                else {
                    // Save the Swiss system settings since we're called from the main menu
                    if((curDevice == SD_CARD)||(curDevice == IDEEXI)) {
                        DrawFrameStart();
                        DrawMessageBox(D_INFO,"Saving Config ...");
                        DrawFrameFinish();
                        config_copy_swiss_settings(&swissSettings);
                        if(config_update_file()) {
                            DrawFrameStart();
                            DrawMessageBox(D_INFO,"Config Saved Successfully!");
                            DrawFrameFinish();
                        }
                        else {
                            DrawFrameStart();
                            DrawMessageBox(D_INFO,"Config Failed to Save!");
                            DrawFrameFinish();
                        }
                    }
                }
                return 1;
            }
            if(option == settings_count_pp[page]) {
                // Exit without saving (revert)
                memcpy((void*)&swissSettings, (void*)&tempSettings, sizeof(SwissSettings));
                return 0;
            }
            if((page != 2) && (option == settings_count_pp[page]-2)) {
                page++;
                option = 0;
            }
            if((page != 0) && (option == settings_count_pp[page]-(page != 2 ? 3:2))) {
                page--;
                option = 0;
            }
        }
        while ((PAD_ButtonsHeld(0) & PAD_BUTTON_RIGHT)
                || (PAD_ButtonsHeld(0) & PAD_BUTTON_LEFT)
                || (PAD_ButtonsHeld(0) & PAD_BUTTON_UP)
                || (PAD_ButtonsHeld(0) & PAD_BUTTON_DOWN)
                || (PAD_ButtonsHeld(0) & PAD_BUTTON_B)
                || (PAD_ButtonsHeld(0) & PAD_BUTTON_A)
                || (PAD_ButtonsHeld(0) & PAD_TRIGGER_R)
                || (PAD_ButtonsHeld(0) & PAD_TRIGGER_L))
        {
            VIDEO_WaitVSync ();
        }
    }
}