bool SaveState (char * filepath, bool silent) { bool retval = false; int datasize; int offset = 0; int device; if(!FindDevice(filepath, &device)) return 0; if(gameScreenPngSize > 0) { char screenpath[1024]; strncpy(screenpath, filepath, 1024); screenpath[strlen(screenpath)-4] = 0; sprintf(screenpath, "%s.png", screenpath); SaveFile((char *)gameScreenPng, screenpath, gameScreenPngSize, silent); } EMUFILE_MEMFILE save(SAVEBUFFERSIZE); FCEUSS_SaveMS(&save, Z_BEST_COMPRESSION); datasize = save.size(); if (datasize) offset = SaveFile(save.buf(), filepath, datasize, silent); if (offset > 0) { if (!silent) InfoPrompt("Save successful"); retval = true; } return retval; }
bool SavePalettes(bool silent) { char filepath[1024]; int datasize; int offset = 0; if(prefpath[0] == 0) return false; sprintf(filepath, "%s/%s", prefpath, PAL_FILE_NAME); // Now create the XML palette file if (!silent) ShowAction("Saving palette..."); AllocSaveBuffer(); datasize = preparePalData(palettes, loadedPalettes); offset = SaveFile(filepath, datasize, silent); FreeSaveBuffer(); CancelAction(); if (offset > 0) { if (!silent) InfoPrompt("Palette saved"); return true; } return false; }
bool DownloadUpdate() { bool result = false; if(updateURL[0] == 0 || appPath[0] == 0 || !ChangeInterface(appPath, NOTSILENT)) { ErrorPrompt("Update failed!"); updateFound = false; // updating is finished (successful or not!) return false; } // stop checking if devices were removed/inserted // since we're saving a file HaltDeviceThread(); int device; FindDevice(appPath, &device); char updateFile[50]; sprintf(updateFile, "%s%s Update.zip", pathPrefix[device], APPNAME); FILE * hfile = fopen (updateFile, "wb"); if (hfile) { if(http_request(updateURL, hfile, NULL, (1024*1024*10), NOTSILENT) > 0) { fclose (hfile); result = unzipArchive(updateFile, (char *)pathPrefix[device]); } else { fclose (hfile); } remove(updateFile); // delete update file } // go back to checking if devices were inserted/removed ResumeDeviceThread(); if(result) InfoPrompt("Update successful!"); else ErrorPrompt("Update failed!"); updateFound = false; // updating is finished (successful or not!) return result; }
bool DownloadUpdate() { bool result = false; if(strlen(updateURL) > 0) { // stop checking if devices were removed/inserted // since we're saving a file HaltDeviceThread(); FILE * hfile; char updateFile[50]; sprintf(updateFile, "sd:/%s Update.zip", APPNAME); hfile = fopen (updateFile, "wb"); if (hfile > 0) { int retval; retval = http_request(updateURL, hfile, NULL, (1024*1024*5)); fclose (hfile); } bool unzipResult = unzipArchive(updateFile, (char *)"sd:/"); remove(updateFile); // delete update file if(unzipResult) { result = true; InfoPrompt("Update successful!"); } else { result = false; ErrorPrompt("Update failed!"); } updateFound = false; // updating is finished (successful or not!) // go back to checking if devices were inserted/removed ResumeDeviceThread(); } return result; }
bool SavePrefs (bool silent) { char filepath[MAXPATHLEN]; int datasize; int offset = 0; int device = 0; if(prefpath[0] != 0) { sprintf(filepath, "%s/%s", prefpath, PREF_FILE_NAME); FindDevice(filepath, &device); } else if(appPath[0] != 0) { sprintf(filepath, "%s/%s", appPath, PREF_FILE_NAME); strcpy(prefpath, appPath); FindDevice(filepath, &device); } else { device = autoSaveMethod(silent); if(device == 0) return false; sprintf(filepath, "%s%s", pathPrefix[device], APPFOLDER); DIR *dir = opendir(filepath); if (!dir) { if(mkdir(filepath, 0777) != 0) return false; sprintf(filepath, "%s%s/roms", pathPrefix[device], APPFOLDER); if(mkdir(filepath, 0777) != 0) return false; sprintf(filepath, "%s%s/saves", pathPrefix[device], APPFOLDER); if(mkdir(filepath, 0777) != 0) return false; } else { closedir(dir); } sprintf(filepath, "%s%s/%s", pathPrefix[device], APPFOLDER, PREF_FILE_NAME); sprintf(prefpath, "%s%s", pathPrefix[device], APPFOLDER); } if(device == 0) return false; if (!silent) ShowAction ("Saving preferences..."); FixInvalidSettings(); AllocSaveBuffer (); datasize = preparePrefsData (); offset = SaveFile(filepath, datasize, silent); FreeSaveBuffer (); CancelAction(); if (offset > 0) { if (!silent) InfoPrompt("Preferences saved"); return true; } return false; }
bool SaveBatteryOrState(char * filepath, int action, bool silent) { bool result = false; int offset = 0; int datasize = 0; // we need the actual size of the data written int device; if(!FindDevice(filepath, &device)) return 0; if(action == FILE_SNAPSHOT && gameScreenPngSize > 0) { char screenpath[1024]; strncpy(screenpath, filepath, 1024); screenpath[strlen(screenpath)-4] = 0; sprintf(screenpath, "%s.png", screenpath); SaveFile((char *)gameScreenPng, screenpath, gameScreenPngSize, silent); } AllocSaveBuffer(); // put VBA memory into savebuffer, sets datasize to size of memory written if(action == FILE_SRAM) { if(cartridgeType == 1) datasize = MemgbWriteBatteryFile((char *)savebuffer); else datasize = MemCPUWriteBatteryFile((char *)savebuffer); if (cartridgeType == 1) { const char* generic_goomba_error = "Cannot save SRAM in Goomba format (did not load correctly.)"; // check for goomba sram format char* old_sram = (char*)malloc(GOOMBA_COLOR_SRAM_SIZE); size_t br = LoadFile(old_sram, filepath, GOOMBA_COLOR_SRAM_SIZE, true); if (br >= GOOMBA_COLOR_SRAM_SIZE && goomba_is_sram(old_sram)) { void* cleaned = goomba_cleanup(old_sram); if (cleaned == NULL) { ErrorPrompt(generic_goomba_error); datasize = 0; } else { if (cleaned != old_sram) { free(old_sram); old_sram = (char*)cleaned; } stateheader* sh = stateheader_for(old_sram, RomTitle); if (sh == NULL) { // Game probably doesn't use SRAM datasize = 0; } else { void* new_sram = goomba_new_sav(old_sram, sh, savebuffer, datasize); if (new_sram == NULL) { ErrorPrompt(goomba_last_error()); datasize = 0; } else { memcpy(savebuffer, new_sram, GOOMBA_COLOR_SRAM_SIZE); datasize = GOOMBA_COLOR_SRAM_SIZE; free(new_sram); } } } } free(old_sram); } } else { if(emulator.emuWriteMemState((char *)savebuffer, SAVEBUFFERSIZE)) datasize = *((int *)(savebuffer+4)) + 8; } // write savebuffer into file if(datasize > 0) { offset = SaveFile(filepath, datasize, silent); if(offset > 0) { if(!silent) InfoPrompt ("Save successful"); result = true; } } else { if(!silent) InfoPrompt("No data to save!"); } FreeSaveBuffer(); return result; }
/**************************************************************************** * http_request * Retrieves the specified URL, and stores it in the specified file or buffer ***************************************************************************/ int http_request(const char *url, FILE *hfile, u8 *buffer, u32 maxsize, bool silent, bool accept_encoding) { int res = 0; int chunked = 0; char http_host[64]; char http_path[256]; char content_encoding[16] = ""; u16 http_port; #ifdef DEBUGHEADERS int debugging = 0; #endif http_res result; u32 sizeread = 0; content_length = 0; int linecount; if(maxsize > MAX_SIZE){ #ifdef DEBUGERRORS InfoPrompt("maxsize > MAX_SIZE"); #endif return 0; } if (url == NULL || (hfile == NULL && buffer == NULL)){ #ifdef DEBUGERRORS InfoPrompt("!url || (!hfile && !buffer)"); #endif return 0; } if(!silent) ShowAction("Sending data..."); split_res = http_split_url(http_host, http_path, url); // 2 : https ; 1 : http ; 0 : invalid url if (split_res == 2){ http_port = 443; writeFunc = ssl_write; readFunc = ssl_read; }else if( split_res == 1 ){ http_port = 80; writeFunc = net_write; readFunc = net_read; }else{ #ifdef DEBUGERRORS InfoPrompt("Invalid url"); #endif return 0; } http_status = 404; int s = tcp_connect(http_host, http_port); if (s < 0) { result = HTTPR_ERR_CONNECT; #ifdef DEBUGERRORS InfoPrompt("Socket!"); #endif return 0; } int ssl_context = 0; if(split_res == 2){ ssl_context = ssl_setup(http_host, s); #ifdef DEBUGERRORS if(ssl_context < 0){ InfoPrompt("ssl_context() failed"); } #endif if(ssl_context < 0){ net_close(s); return 0; } scktctx = &ssl_context; } else{ scktctx = &s; } if(curl_request){ //Request made by through the CURL class res = tcp_write(*scktctx, (u8 *) curl_request, strlen(curl_request)); }else{ char request[1024]; char *r = request; r += sprintf(r, "GET %s HTTP/1.1\r\n", http_path); r += sprintf(r, "Host: %s\r\n", http_host); if(accept_encoding && hfile){ r += sprintf(r, "Accept-Encoding: gzip, deflate\r\n"); } r += sprintf(r, "Cache-Control: no-cache\r\n\r\n"); res = tcp_write(*scktctx, (u8 *) request, strlen(request)); } if(!silent) CancelAction(); #ifdef DEBUGHEADERS InfoPrompt(http_path); #endif char line[1024]; //Twitter sends a long header for (linecount = 0; linecount < 45; linecount++) { if (tcp_readln(*scktctx, line, 1024) != 0) { #ifdef DEBUGERRORS InfoPrompt("tcp_readln != 0"); #endif http_status = 404; result = HTTPR_ERR_REQUEST; break; } if (!line[0]) break; #ifdef DEBUGHEADERS if(sscanf(line, "HTTP/1.%*u %u", &http_status)){ if(http_status != 200) debugging = 1; } if(sscanf(line, "Content-Length: %u", &content_length) || sscanf(line, "Content-Encoding: %s", content_encoding)){ if(!debugging){ InfoPrompt(line); } } if(!strncmp(line, "Transfer-Encoding: chunked", 25)){ InfoPrompt("Transfer-Encoding: chunked"); chunked = 1; } #else sscanf(line, "HTTP/1.%*u %u", &http_status); sscanf(line, "Content-Length: %u", &content_length); sscanf(line, "Content-Encoding: %s", content_encoding); if(!strncmp(line, "Transfer-Encoding: chunked", 25)) chunked = 1; #endif u32 api_ratelimit=0; if(sscanf(line, "X-RateLimit-Remaining: %u", &api_ratelimit) && api_ratelimit <= 10 && api_ratelimit % 5 == 0){ WindowPrompt("You are on fire!", "You are about to reach Twitter's requests limit. WiiTweet will not work correctly then.", "I'll take a break", 0); } if(get_timeoffset){ if(!strncasecmp(line, "Date:", 5)){ //Case insensitiveness just in case... const char format[] = "%a, %d %b %Y %H:%M:%S %Z"; const char *pointline = line; pointline += 6; struct tm tm; memset(&tm, 0, sizeof(tm)); strptime(pointline, format, &tm); timeoffset = mktime(&tm) - time(NULL); get_timeoffset = 0; } } #ifdef DEBUGHEADERS if(debugging){ InfoPrompt(line); } #endif } if (http_status != 200) { result = HTTPR_ERR_STATUS; #ifdef DEBUGERRORS if(ssl_context){ if(ssl_shutdown(ssl_context)){ InfoPrompt("ssl_shutdown() 1"); } } net_close(s); #else if(ssl_context){ssl_shutdown(ssl_context);} net_close(s); #endif #ifdef DEBUGERRORS char status[64]; sprintf(status, "HTTP Status = %d", http_status); InfoPrompt(status); #endif return 0; }//Try to read anyways? ssl gets rude if it is not convinced there is no data //length unknown - just read as much as we can if(content_length == 0) { content_length = maxsize; } else if (content_length > maxsize) //ssl_shutdown() would fail in this case (?), but it is not likely for our purposes... { result = HTTPR_ERR_TOOBIG; #ifdef DEBUGERRORS if(ssl_context){ if(ssl_shutdown(ssl_context)){ InfoPrompt("ssl_shutdown() 2"); } } net_close(s); #else if(ssl_context){ssl_shutdown(ssl_context);} net_close(s); #endif #ifdef DEBUGERRORS InfoPrompt("content_length > maxsize"); #endif return 0; } unsigned int inflatetype = 0; if(!strncasecmp(content_encoding, "gzip", 4)){ inflatetype = 2; }else if(!strncasecmp(content_encoding, "deflate", 7)){ inflatetype = 1; }else if(content_encoding[0] != '\0'){//Unsupported encoding. This should never happen. #ifdef DEBUGERRORS if(ssl_context){ if(ssl_shutdown(ssl_context)){ InfoPrompt("ssl_shutdown() 3"); } } net_close(s); #else if(ssl_context){ssl_shutdown(ssl_context);} net_close(s); #endif #ifdef DEBUGERRORS InfoPrompt("Unsupported encoding"); #endif return 0; } if (buffer != NULL) { if(!silent) ShowAction("Downloading..."); if(inflatetype){ //Compressed content u8 * inflate_me = (u8 *) mem2_malloc(content_length, MEM2_OTHER); if(!inflate_me){ #ifdef DEBUGERRORS if(ssl_context){ if(ssl_shutdown(ssl_context)){ InfoPrompt("ssl_shutdown() 4"); } } net_close(s); #else if(ssl_context){ssl_shutdown(ssl_context);} net_close(s); #endif #ifdef DEBUGERRORS InfoPrompt("!inflate_me"); #endif return 0; } #ifdef DEBUGHEADERS int tcpread = tcp_read(*scktctx, inflate_me, content_length, chunked); char atoi[64]; sprintf(atoi, "%d", tcpread); WindowPrompt("tcp_read()", atoi, "ok", 0); #else int tcpread = tcp_read(*scktctx, inflate_me, content_length, chunked); #endif /* static int s = 0; char path[256]; sprintf(path, "sd:/catcha%d", s++); SaveFile ((char *)inflate_me, path, tcpread, 1); */ sizeread = httpInflate(buffer, inflate_me, tcpread, inflatetype); if(sizeread < 0){ mem2_free(inflate_me, MEM2_OTHER); #ifdef DEBUGERRORS if(ssl_context){ if(ssl_shutdown(ssl_context)){ InfoPrompt("ssl_shutdown() 5"); } } net_close(s); #else if(ssl_context){ssl_shutdown(ssl_context);} net_close(s); #endif #ifdef DEBUGERRORS InfoPrompt("sizeread < 0"); #endif return 0; } mem2_free(inflate_me, MEM2_OTHER); }else{ //Uncomprpessed content sizeread = tcp_read(*scktctx, buffer, content_length, chunked); } if(!silent) CancelAction(); } else // write into file { /* Uncompressed data. This may fail if the content is chunked and longer than 32KB+2B but chunked is not used in such scenarios */ u32 bufSize = (1024 * 32); u32 bytesLeft = content_length; u32 readSize; if(!silent) ShowProgress("Downloading...", 0, content_length); u8 * fbuffer = (u8 *) malloc(bufSize); if(fbuffer) { while (bytesLeft > 0) { if (bytesLeft < bufSize) readSize = bytesLeft; else readSize = bufSize; res = tcp_read(*scktctx, fbuffer, readSize, chunked); if (!res) break; sizeread += res; bytesLeft -= res; res = fwrite(fbuffer, 1, res, hfile); if (!res) break; if(!silent) ShowProgress("Downloading...", (content_length - bytesLeft), content_length); } free(fbuffer); } if(!silent) CancelAction(); } #ifdef DEBUGERRORS if(ssl_context){ if(ssl_shutdown(ssl_context)){ InfoPrompt("ssl_shutdown() 6"); } } net_close(s); #else if(ssl_context){ssl_shutdown(ssl_context);} net_close(s); #endif if (content_length < maxsize && sizeread != content_length && !inflatetype) { #ifdef DEBUGERRORS InfoPrompt("ERR_RECEIVE"); #endif result = HTTPR_ERR_RECEIVE; return 0; } if (http_status != 200){ #ifdef DEBUGERRORS InfoPrompt("http_status != 200"); #endif return 0; } if(result) //Avoid ugly compiler warning :p result = HTTPR_OK; return sizeread; }
bool SaveRAM (char * filepath, bool silent) { bool retval = false; int datasize = 0; int offset = 0; int device; if(!FindDevice(filepath, &device)) return 0; if(GameInfo->type == GIT_FDS) { if(!silent) InfoPrompt("RAM saving is not available for FDS games!"); return false; } AllocSaveBuffer (); // save game save to savebuffer if(GameInfo->type == GIT_CART) datasize = WiiFCEU_GameSave(&iNESCart, 0); else if(GameInfo->type == GIT_VSUNI) datasize = WiiFCEU_GameSave(&UNIFCart, 0); if (datasize) { // Check to see if this is a PocketNES save file FILE* file = fopen(filepath, "rb"); if (file) { uint32 tag; fread(&tag, sizeof(uint32), 1, file); fclose(file); if (goomba_is_sram(&tag)) { void* gba_data = malloc(GOOMBA_COLOR_SRAM_SIZE); file = fopen(filepath, "rb"); fread(gba_data, 1, GOOMBA_COLOR_SRAM_SIZE, file); fclose(file); void* cleaned = goomba_cleanup(gba_data); if (!cleaned) { ErrorPrompt(goomba_last_error()); } else if (cleaned != gba_data) { memcpy(gba_data, cleaned, GOOMBA_COLOR_SRAM_SIZE); free(cleaned); } // Look for just one save file. If there aren't any, or there is more than one, don't read any data. const stateheader* sh1 = NULL; const stateheader* sh2 = NULL; const stateheader* sh = stateheader_first(gba_data); while (sh && stateheader_plausible(sh)) { if (little_endian_conv_16(sh->type) != GOOMBA_SRAMSAVE) {} else if (sh1 == NULL) { sh1 = sh; } else { sh2 = sh; break; } sh = stateheader_advance(sh); } if (sh1 == NULL) { ErrorPrompt("PocketNES save file has no SRAM."); datasize = 0; } else if (sh2 != NULL) { ErrorPrompt("PocketNES save file has more than one SRAM."); datasize = 0; } else { char* newdata = goomba_new_sav(gba_data, sh1, savebuffer, datasize); if (!newdata) { ErrorPrompt(goomba_last_error()); datasize = 0; } else { memcpy(savebuffer, newdata, GOOMBA_COLOR_SRAM_SIZE); datasize = GOOMBA_COLOR_SRAM_SIZE; free(newdata); } } } } } if (datasize) { offset = SaveFile(filepath, datasize, silent); if (offset > 0) { if (!silent) InfoPrompt("Save successful"); retval = true; } } else { if (!silent) InfoPrompt("No data to save!"); } FreeSaveBuffer (); return retval; }
bool LoadRAM (char * filepath, bool silent) { int offset = 0; bool retval = false; int device; if(!FindDevice(filepath, &device)) return 0; if(GameInfo->type == GIT_FDS) // RAM saves don't exist for FDS games return false; AllocSaveBuffer (); offset = LoadFile(filepath, silent); // Check to see if this is a PocketNES save file if (goomba_is_sram(savebuffer)) { void* cleaned = goomba_cleanup(savebuffer); if (!cleaned) { ErrorPrompt(goomba_last_error()); } else if (cleaned != savebuffer) { memcpy(savebuffer, cleaned, GOOMBA_COLOR_SRAM_SIZE); free(cleaned); } // Look for just one save file. If there aren't any, or there is more than one, don't read any data. const stateheader* sh1 = NULL; const stateheader* sh2 = NULL; const stateheader* sh = stateheader_first(savebuffer); while (sh && stateheader_plausible(sh)) { if (little_endian_conv_16(sh->type) != GOOMBA_SRAMSAVE) { } else if (sh1 == NULL) { sh1 = sh; } else { sh2 = sh; break; } sh = stateheader_advance(sh); } if (sh1 == NULL) { ErrorPrompt("PocketNES save file has no SRAM."); offset = 0; } else if (sh2 != NULL) { ErrorPrompt("PocketNES save file has more than one SRAM."); offset = 0; } else { goomba_size_t len; void* extracted = goomba_extract(savebuffer, sh1, &len); if (!extracted) ErrorPrompt(goomba_last_error()); else { memcpy(savebuffer, extracted, len); offset = len; free(extracted); } } } if (offset > 0) { if(GameInfo->type == GIT_CART) WiiFCEU_GameSave(&iNESCart, 1); else if(GameInfo->type == GIT_VSUNI) WiiFCEU_GameSave(&UNIFCart, 1); ResetNES(); retval = true; } else { // if we reached here, nothing was done! if(!silent) InfoPrompt ("Save file not found"); } FreeSaveBuffer (); return retval; }
bool DownloadUpdate() { bool result = false; if(updateURL[0] == 0 || appPath[0] == 0 || !ChangeInterface(appPath, NOTSILENT)) { ErrorPrompt("Did you unplug your SD/USB?"); return false; } // stop checking if devices were removed/inserted // since we're saving a file HaltDeviceThread(); int device; FindDevice(appPath, &device); char updateFile[50]; sprintf(updateFile, "%s%sUpdate.zip", pathPrefix[device], APPNAME); FILE * hfile = fopen (updateFile, "wb"); if (hfile) { if(http_request(updateURL, hfile, NULL, (1024*1024*10), NOTSILENT, 0) > 0) { ShowAction("Extracting update..."); fclose (hfile); CSHA1 sha1; if(sha1.HashFile(updateFile)){ char fileHash[41]=""; sha1.Final(); sha1.ReportHash(fileHash, 0); if(!strcasecmp(fileHash, updateHash)){ char destPath[256]; sprintf(destPath, "%sapps", pathPrefix[device]); result = unzipArchive(updateFile, destPath); }else{ ErrorPrompt("Corrupted file!"); } }else{ ErrorPrompt("Could not verify file!"); } } else { ErrorPrompt("Unable to download!"); fclose (hfile); } remove(updateFile); // delete update file }else{ ErrorPrompt("Unable to write to SD/USB!"); } // go back to checking if devices were inserted/removed ResumeDeviceThread(); if(result){ InfoPrompt("Update successful!"); } CancelAction(); return result; }