123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- auto Emulator::attach(higan::Node::Object node) -> void {
- if(interface && node->is<higan::Node::Screen>()) {
- screens = root->find<higan::Node::Screen>();
- }
- if(interface && node->is<higan::Node::Stream>()) {
- streams = root->find<higan::Node::Stream>();
- }
- }
- auto Emulator::detach(higan::Node::Object node) -> void {
- if(interface && node->is<higan::Node::Screen>()) {
- screens = root->find<higan::Node::Screen>();
- }
- if(interface && node->is<higan::Node::Stream>()) {
- streams = root->find<higan::Node::Stream>();
- }
- if(auto location = node->attribute("location")) {
- file::write({location, "settings.bml"}, node->save());
- }
- }
- auto Emulator::open(higan::Node::Object node, string name, vfs::file::mode mode, bool required) -> shared_pointer<vfs::file> {
- auto location = node->attribute("location");
- if(name == "manifest.bml") {
- if(!file::exists({location, name}) && directory::exists(location)) {
- if(auto manifest = execute("icarus", "--system", node->name(), "--manifest", location).output) {
- return vfs::memory::open(manifest.data<uint8_t>(), manifest.size());
- }
- }
- }
- if(auto result = vfs::disk::open({location, name}, mode)) return result;
- if(required) {
- //attempt to pull required system firmware (boot ROMs, etc) from system template
- if(location == emulator.system.data) {
- if(file::exists({emulator.system.templates, name})) {
- file::copy({emulator.system.templates, name}, {emulator.system.data, name});
- if(auto result = vfs::disk::open({location, name}, mode)) return result;
- }
- }
- if(MessageDialog()
- .setTitle("Warning")
- .setText({"Missing required file:\n",
- location, name, "\n\n",
- "Would you like to browse for this file now?"})
- .setAlignment(Alignment::Center)
- .question() == "No") return {};
- if(auto source = BrowserDialog()
- .setTitle({"Load ", name})
- .setPath(location)
- .setAlignment(Alignment::Center)
- .openFile()
- ) {
- if(auto input = vfs::memory::open(source, true)) {
- if(auto output = file::open({location, name}, file::mode::write)) {
- output.write({input->data(), (uint)input->size()});
- }
- }
- if(auto result = vfs::disk::open({location, name}, mode)) return result;
- }
- }
- return {};
- }
- auto Emulator::event(higan::Event event) -> void {
- if(event == higan::Event::Power) {
- events.power = true;
- }
- }
- auto Emulator::log(string_view message) -> void {
- if(!system.log) {
- directory::create({"/tmp/Logs/", system.name, "/"});
- string datetime = chrono::local::datetime().transform("-: ", " _").replace(" ", "");
- system.log.open({"/tmp/Logs/", system.name, "/event-", datetime, ".log"}, file::mode::write);
- }
- system.log.print(message);
- }
- auto Emulator::video(higan::Node::Screen node, const uint32_t* data, uint pitch, uint width, uint height) -> void {
- if(requests.captureScreenshot) {
- requests.captureScreenshot = false;
- captureScreenshot(data, pitch, width, height);
- }
- uint videoWidth = node->width() * node->scaleX();
- uint videoHeight = node->height() * node->scaleY();
- if(settings.video.aspectCorrection) {
- videoWidth = videoWidth * node->aspectX() / node->aspectY();
- }
- if(node->rotation() == 90 || node->rotation() == 270) {
- swap(videoWidth, videoHeight);
- }
- auto [viewportWidth, viewportHeight] = videoInstance.size();
- uint multiplierX = viewportWidth / videoWidth;
- uint multiplierY = viewportHeight / videoHeight;
- uint multiplier = min(multiplierX, multiplierY);
- uint outputWidth = videoWidth * multiplier;
- uint outputHeight = videoHeight * multiplier;
- if(multiplier == 0 || settings.video.output == "Scale") {
- float multiplierX = (float)viewportWidth / (float)videoWidth;
- float multiplierY = (float)viewportHeight / (float)videoHeight;
- float multiplier = min(multiplierX, multiplierY);
- outputWidth = videoWidth * multiplier;
- outputHeight = videoHeight * multiplier;
- }
- if(settings.video.output == "Stretch") {
- outputWidth = viewportWidth;
- outputHeight = viewportHeight;
- }
- pitch >>= 2;
- if(auto [output, length] = videoInstance.acquire(width, height); output) {
- length >>= 2;
- for(auto y : range(height)) {
- memory::copy<uint32>(output + y * length, data + y * pitch, width);
- }
- videoInstance.release();
- videoInstance.output(outputWidth, outputHeight);
- }
- static uint frameCounter = 0;
- static uint64_t previous, current;
- frameCounter++;
- current = chrono::timestamp();
- if(current != previous) {
- previous = current;
- setCaption({frameCounter, " fps"});
- frameCounter = 0;
- }
- }
- auto Emulator::audio(higan::Node::Stream) -> void {
- if(!streams) return; //should never occur
- //process all pending frames (there may be more than one waiting)
- while(true) {
- //only process a frame if all streams have at least one pending frame
- for(auto& stream : streams) {
- if(!stream->pending()) return;
- }
- //mix all frames together
- double samples[2] = {0.0, 0.0};
- for(auto& stream : streams) {
- double buffer[2];
- uint channels = stream->read(buffer);
- if(channels == 1) {
- //monaural -> stereo mixing
- samples[0] += buffer[0];
- samples[1] += buffer[0];
- } else {
- //stereo mixing
- samples[0] += buffer[0];
- samples[1] += buffer[1];
- }
- }
- //apply volume, balance, and clamping to the output frame
- double volume = !settings.audio.mute ? settings.audio.volume : 0.0;
- double balance = settings.audio.balance;
- for(uint c : range(2)) {
- samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
- if(balance < 0.0) samples[1] *= 1.0 + balance;
- if(balance > 0.0) samples[0] *= 1.0 - balance;
- }
- //send frame to the audio output device
- audioInstance.output(samples);
- }
- }
- auto Emulator::input(higan::Node::Input input) -> void {
- inputManager.poll();
- bool allow = program.viewport.focused();
- if(settings.input.unfocused == "Allow") allow = true;
- if(videoInstance.exclusive()) allow = true;
- if(auto button = input->cast<higan::Node::Button>()) {
- button->setValue(0);
- if(auto instance = button->attribute<shared_pointer<InputButton>>("instance")) {
- if(allow) button->setValue(instance->value());
- }
- }
- if(auto axis = input->cast<higan::Node::Axis>()) {
- axis->setValue(0);
- if(auto instance = axis->attribute<shared_pointer<InputAxis>>("instance")) {
- if(allow) axis->setValue(instance->value());
- }
- }
- }
|