string Utility::selectCartridge() { audio.clear(); QString filename = QFileDialog::getOpenFileName(0, "Load Cartridge", utf8() << (config.path.rom != "" ? config.path.rom : config.path.current), "SNES images (*.smc *.sfc *.swc *.fig *.bs *.st" #if defined(GZIP_SUPPORT) " *.zip *.gz" #endif #if defined(JMA_SUPPORT) " *.jma" #endif ");;" "All files (*)" ); string name = filename.toUtf8().constData(); if(strlen(name) > 0) config.path.current = basepath(name); return name; } string Utility::selectFolder(const char *title) { audio.clear(); QString pathname = QFileDialog::getExistingDirectory(0, title, utf8() << config.path.current, QFileDialog::ShowDirsOnly); string path = pathname.toUtf8().constData(); strtr(path, "\\", "/"); if(strend(path, "/") == false) path << "/"; return path; } void Utility::loadCartridge(const char *filename) { SNES::MappedRAM memory; if(loadCartridge(filename, memory) == false) return; SNES::Cartridge::Type type = SNES::cartridge.detect_image_type(memory.data(), memory.size()); memory.reset(); switch(type) { case SNES::Cartridge::TypeNormal: loadCartridgeNormal(filename); break; case SNES::Cartridge::TypeBsxSlotted: winLoader->loadBsxSlottedCartridge(filename, ""); break; case SNES::Cartridge::TypeBsxBios: winLoader->loadBsxCartridge(filename, ""); break; case SNES::Cartridge::TypeBsx: winLoader->loadBsxCartridge(config.path.bsx, filename); break; case SNES::Cartridge::TypeSufamiTurboBios: winLoader->loadSufamiTurboCartridge(filename, "", ""); break; case SNES::Cartridge::TypeSufamiTurbo: winLoader->loadSufamiTurboCartridge(config.path.st, filename, ""); break; case SNES::Cartridge::TypeSuperGameBoyBios: winLoader->loadSuperGameBoyCartridge(filename, ""); break; case SNES::Cartridge::TypeGameBoy: winLoader->loadSuperGameBoyCartridge(config.path.sgb, filename); break; } } bool Utility::loadCartridgeNormal(const char *base) { unloadCartridge(); if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; SNES::cartridge.load(SNES::Cartridge::ModeNormal); loadMemory(cartridge.baseName, ".srm", SNES::memory::cartram); loadMemory(cartridge.baseName, ".rtc", SNES::memory::cartrtc); cartridge.name = basename(base); modifySystemState(LoadCartridge); return true; } bool Utility::loadCartridgeBsxSlotted(const char *base, const char *slot) { unloadCartridge(); if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; loadCartridge(cartridge.slotAName = slot, SNES::memory::bsxflash); SNES::cartridge.load(SNES::Cartridge::ModeBsxSlotted); loadMemory(cartridge.baseName, ".srm", SNES::memory::cartram); loadMemory(cartridge.baseName, ".rtc", SNES::memory::cartrtc); cartridge.name = basename(base); if(*slot) cartridge.name << " + " << basename(slot); modifySystemState(LoadCartridge); return true; } bool Utility::loadCartridgeBsx(const char *base, const char *slot) { unloadCartridge(); if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; loadCartridge(cartridge.slotAName = slot, SNES::memory::bsxflash); SNES::cartridge.load(SNES::Cartridge::ModeBsx); loadMemory(cartridge.baseName, ".srm", SNES::memory::bsxram ); loadMemory(cartridge.baseName, ".psr", SNES::memory::bsxpram); cartridge.name = (*slot ? basename(slot) : basename(base)); modifySystemState(LoadCartridge); return true; } bool Utility::loadCartridgeSufamiTurbo(const char *base, const char *slotA, const char *slotB) { unloadCartridge(); if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; loadCartridge(cartridge.slotAName = slotA, SNES::memory::stArom); loadCartridge(cartridge.slotBName = slotB, SNES::memory::stBrom); SNES::cartridge.load(SNES::Cartridge::ModeSufamiTurbo); loadMemory(cartridge.slotAName, ".srm", SNES::memory::stAram); loadMemory(cartridge.slotBName, ".srm", SNES::memory::stBram); if(!*slotA && !*slotB) cartridge.name = basename(base); else if(!*slotB) cartridge.name = basename(slotA); else if(!*slotA) cartridge.name = basename(slotB); else cartridge.name = string() << basename(slotA) << " + " << basename(slotB); modifySystemState(LoadCartridge); return true; } bool Utility::loadCartridgeSuperGameBoy(const char *base, const char *slot) { unloadCartridge(); if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; loadCartridge(cartridge.slotAName = slot, SNES::memory::gbrom); SNES::cartridge.load(SNES::Cartridge::ModeSuperGameBoy); loadMemory(cartridge.slotAName, ".sav", SNES::memory::gbram); cartridge.name = (*slot ? basename(slot) : basename(base)); modifySystemState(LoadCartridge); return true; } void Utility::saveMemory() { if(SNES::cartridge.loaded() == false) return; switch(SNES::cartridge.mode()) { case SNES::Cartridge::ModeNormal: case SNES::Cartridge::ModeBsxSlotted: { saveMemory(cartridge.baseName, ".srm", SNES::memory::cartram); saveMemory(cartridge.baseName, ".rtc", SNES::memory::cartrtc); } break; case SNES::Cartridge::ModeBsx: { saveMemory(cartridge.baseName, ".srm", SNES::memory::bsxram ); saveMemory(cartridge.baseName, ".psr", SNES::memory::bsxpram); } break; case SNES::Cartridge::ModeSufamiTurbo: { saveMemory(cartridge.slotAName, ".srm", SNES::memory::stAram); saveMemory(cartridge.slotBName, ".srm", SNES::memory::stBram); } break; case SNES::Cartridge::ModeSuperGameBoy: { saveMemory(cartridge.slotAName, ".sav", SNES::memory::gbram); } break; } } void Utility::unloadCartridge() { if(SNES::cartridge.loaded() == false) return; saveMemory(); modifySystemState(UnloadCartridge); } void Utility::modifySystemState(system_state_t state) { video.clear(); audio.clear(); switch(state) { case LoadCartridge: { //must call cartridge.load_cart_...() before calling modifySystemState(LoadCartridge) if(SNES::cartridge.loaded() == false) break; loadCheats(); application.power = true; application.pause = false; SNES::system.power(); //warn if unsupported hardware detected string chip; if(SNES::cartridge.has_superfx()) chip = "SuperFX"; else if(SNES::cartridge.has_st011()) chip = "ST011"; else if(SNES::cartridge.has_st018()) chip = "ST018"; else if(SNES::cartridge.has_dsp3()) chip = "DSP-3"; //unplayable; only partially supported if(chip != "") { QMessageBox::warning(winMain->window, "Warning", utf8() << "

Warning:
Unsupported " << chip << " chip detected. " << "It is unlikely that this title will work properly.

"); } showMessage(utf8() << "Loaded " << cartridge.name << (false ? ", and applied UPS patch." : ".")); winMain->window->setWindowTitle(utf8() << BSNES_TITLE << " - " << cartridge.name); } break; case UnloadCartridge: { if(SNES::cartridge.loaded() == false) break; //no cart to unload? saveCheats(); SNES::cartridge.unload(); application.power = false; application.pause = true; showMessage(utf8() << "Unloaded " << cartridge.name << "."); winMain->window->setWindowTitle(utf8() << BSNES_TITLE); } break; case PowerOn: { if(SNES::cartridge.loaded() == false || application.power == true) break; application.power = true; application.pause = false; SNES::system.power(); showMessage("Power on."); } break; case PowerOff: { if(SNES::cartridge.loaded() == false || application.power == false) break; application.power = false; application.pause = true; showMessage("Power off."); } break; case PowerCycle: { if(SNES::cartridge.loaded() == false) break; application.power = true; application.pause = false; SNES::system.power(); showMessage("System power was cycled."); } break; case Reset: { if(SNES::cartridge.loaded() == false || application.power == false) break; application.pause = false; SNES::system.reset(); showMessage("System was reset."); } break; } winMain->syncUi(); winCodeEditor->dismiss(); winCheatEditor->reloadList(); winCheatEditor->syncUi(); } // bool Utility::loadCartridge(const char *filename, SNES::MappedRAM &memory) { if(file::exists(filename) == false) return false; Reader::Type filetype = Reader::detect(filename, config.file.autodetect_type); uint8_t *data; unsigned size; switch(filetype) { default: case Reader::Normal: { FileReader fp(filename); if(!fp.ready()) return false; size = fp.size(); data = fp.read(); } break; #ifdef GZIP_SUPPORT case Reader::GZIP: { GZReader fp(filename); if(!fp.ready()) return false; size = fp.size(); data = fp.read(); } break; case Reader::ZIP: { ZipReader fp(filename); if(!fp.ready()) return false; size = fp.size(); data = fp.read(); } break; #endif #ifdef JMA_SUPPORT case Reader::JMA: { try { JMAReader fp(filename); size = fp.size(); data = fp.read(); } catch(JMA::jma_errors) { return false; } } break; #endif } if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); memory.map(data, size); return true; } bool Utility::loadMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { if(memory.size() == 0 || memory.size() == -1U) return false; string name; name << filepath(basename(filename), config.path.save); name << extension; file fp; if(fp.open(name, file::mode_read) == false) return false; unsigned size; uint8_t *data = new uint8_t[size = fp.size()]; fp.read(data, size); fp.close(); memcpy(memory.data(), data, min(size, memory.size())); return true; } bool Utility::saveMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { if(memory.size() == 0 || memory.size() == -1U) return false; string name; name << filepath(basename(filename), config.path.save); name << extension; file fp; if(fp.open(name, file::mode_write) == false) return false; fp.write(memory.data(), memory.size()); fp.close(); return true; } void Utility::loadCheats() { string name, data; name << filepath(basename(cartridge.baseName), config.path.cheat); name << ".cht"; SNES::cheat.clear(); if(data.readfile(name)) SNES::cheat.load(data); } void Utility::saveCheats() { string name; name << filepath(basename(cartridge.baseName), config.path.cheat); name << ".cht"; file fp; if(SNES::cheat.count() > 0 || file::exists(name)) { if(fp.open(name, file::mode_write)) { fp.print(SNES::cheat.save()); fp.close(); } } } // //ensure file path is absolute (eg resolve relative paths) string Utility::filepath(const char *filename, const char *pathname) { //if no pathname, return filename as-is string file(filename); file.replace("\\", "/"); string path = (!pathname || !*pathname) ? (const char*)config.path.current : pathname; //ensure path ends with trailing '/' path.replace("\\", "/"); if(!strend(path, "/")) path.append("/"); //replace relative path with absolute path if(strbegin(path, "./")) { ltrim(path, "./"); path = string() << config.path.base << path; } //remove folder part of filename lstring part; part.split("/", file); return path << part[part.size() - 1]; } //remove directory information and file extension ("/foo/bar.ext" -> "bar") string Utility::basename(const char *filename) { string name(filename); //remove extension for(signed i = strlen(name) - 1; i >= 0; i--) { if(name[i] == '.') { name[i] = 0; break; } } //remove directory information for(signed i = strlen(name) - 1; i >= 0; i--) { if(name[i] == '/' || name[i] == '\\') { i++; char *output = name(); while(true) { *output++ = name[i]; if(!name[i]) break; i++; } break; } } return name; } //remove filename and return path only ("/foo/bar.ext" -> "/foo/") string Utility::basepath(const char *filename) { string path(filename); path.replace("\\", "/"); //remove filename for(signed i = strlen(path) - 1; i >= 0; i--) { if(path[i] == '/') { path[i] = 0; break; } } if(!strend(path, "/")) path.append("/"); return path; }