platform.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. auto Emulator::attach(higan::Node::Object node) -> void {
  2. if(interface && node->is<higan::Node::Screen>()) {
  3. screens = root->find<higan::Node::Screen>();
  4. }
  5. if(interface && node->is<higan::Node::Stream>()) {
  6. streams = root->find<higan::Node::Stream>();
  7. }
  8. }
  9. auto Emulator::detach(higan::Node::Object node) -> void {
  10. if(interface && node->is<higan::Node::Screen>()) {
  11. screens = root->find<higan::Node::Screen>();
  12. }
  13. if(interface && node->is<higan::Node::Stream>()) {
  14. streams = root->find<higan::Node::Stream>();
  15. }
  16. if(auto location = node->attribute("location")) {
  17. file::write({location, "settings.bml"}, node->save());
  18. }
  19. }
  20. auto Emulator::open(higan::Node::Object node, string name, vfs::file::mode mode, bool required) -> shared_pointer<vfs::file> {
  21. auto location = node->attribute("location");
  22. if(name == "manifest.bml") {
  23. if(!file::exists({location, name}) && directory::exists(location)) {
  24. if(auto manifest = execute("icarus", "--system", node->name(), "--manifest", location).output) {
  25. return vfs::memory::open(manifest.data<uint8_t>(), manifest.size());
  26. }
  27. }
  28. }
  29. if(auto result = vfs::disk::open({location, name}, mode)) return result;
  30. if(required) {
  31. //attempt to pull required system firmware (boot ROMs, etc) from system template
  32. if(location == emulator.system.data) {
  33. if(file::exists({emulator.system.templates, name})) {
  34. file::copy({emulator.system.templates, name}, {emulator.system.data, name});
  35. if(auto result = vfs::disk::open({location, name}, mode)) return result;
  36. }
  37. }
  38. if(MessageDialog()
  39. .setTitle("Warning")
  40. .setText({"Missing required file:\n",
  41. location, name, "\n\n",
  42. "Would you like to browse for this file now?"})
  43. .setAlignment(Alignment::Center)
  44. .question() == "No") return {};
  45. if(auto source = BrowserDialog()
  46. .setTitle({"Load ", name})
  47. .setPath(location)
  48. .setAlignment(Alignment::Center)
  49. .openFile()
  50. ) {
  51. if(auto input = vfs::memory::open(source, true)) {
  52. if(auto output = file::open({location, name}, file::mode::write)) {
  53. output.write({input->data(), (uint)input->size()});
  54. }
  55. }
  56. if(auto result = vfs::disk::open({location, name}, mode)) return result;
  57. }
  58. }
  59. return {};
  60. }
  61. auto Emulator::event(higan::Event event) -> void {
  62. if(event == higan::Event::Power) {
  63. events.power = true;
  64. }
  65. }
  66. auto Emulator::log(string_view message) -> void {
  67. if(!system.log) {
  68. directory::create({"/tmp/Logs/", system.name, "/"});
  69. string datetime = chrono::local::datetime().transform("-: ", " _").replace(" ", "");
  70. system.log.open({"/tmp/Logs/", system.name, "/event-", datetime, ".log"}, file::mode::write);
  71. }
  72. system.log.print(message);
  73. }
  74. auto Emulator::video(higan::Node::Screen node, const uint32_t* data, uint pitch, uint width, uint height) -> void {
  75. if(requests.captureScreenshot) {
  76. requests.captureScreenshot = false;
  77. captureScreenshot(data, pitch, width, height);
  78. }
  79. uint videoWidth = node->width() * node->scaleX();
  80. uint videoHeight = node->height() * node->scaleY();
  81. if(settings.video.aspectCorrection) {
  82. videoWidth = videoWidth * node->aspectX() / node->aspectY();
  83. }
  84. if(node->rotation() == 90 || node->rotation() == 270) {
  85. swap(videoWidth, videoHeight);
  86. }
  87. auto [viewportWidth, viewportHeight] = videoInstance.size();
  88. uint multiplierX = viewportWidth / videoWidth;
  89. uint multiplierY = viewportHeight / videoHeight;
  90. uint multiplier = min(multiplierX, multiplierY);
  91. uint outputWidth = videoWidth * multiplier;
  92. uint outputHeight = videoHeight * multiplier;
  93. if(multiplier == 0 || settings.video.output == "Scale") {
  94. float multiplierX = (float)viewportWidth / (float)videoWidth;
  95. float multiplierY = (float)viewportHeight / (float)videoHeight;
  96. float multiplier = min(multiplierX, multiplierY);
  97. outputWidth = videoWidth * multiplier;
  98. outputHeight = videoHeight * multiplier;
  99. }
  100. if(settings.video.output == "Stretch") {
  101. outputWidth = viewportWidth;
  102. outputHeight = viewportHeight;
  103. }
  104. pitch >>= 2;
  105. if(auto [output, length] = videoInstance.acquire(width, height); output) {
  106. length >>= 2;
  107. for(auto y : range(height)) {
  108. memory::copy<uint32>(output + y * length, data + y * pitch, width);
  109. }
  110. videoInstance.release();
  111. videoInstance.output(outputWidth, outputHeight);
  112. }
  113. static uint frameCounter = 0;
  114. static uint64_t previous, current;
  115. frameCounter++;
  116. current = chrono::timestamp();
  117. if(current != previous) {
  118. previous = current;
  119. setCaption({frameCounter, " fps"});
  120. frameCounter = 0;
  121. }
  122. }
  123. auto Emulator::audio(higan::Node::Stream) -> void {
  124. if(!streams) return; //should never occur
  125. //process all pending frames (there may be more than one waiting)
  126. while(true) {
  127. //only process a frame if all streams have at least one pending frame
  128. for(auto& stream : streams) {
  129. if(!stream->pending()) return;
  130. }
  131. //mix all frames together
  132. double samples[2] = {0.0, 0.0};
  133. for(auto& stream : streams) {
  134. double buffer[2];
  135. uint channels = stream->read(buffer);
  136. if(channels == 1) {
  137. //monaural -> stereo mixing
  138. samples[0] += buffer[0];
  139. samples[1] += buffer[0];
  140. } else {
  141. //stereo mixing
  142. samples[0] += buffer[0];
  143. samples[1] += buffer[1];
  144. }
  145. }
  146. //apply volume, balance, and clamping to the output frame
  147. double volume = !settings.audio.mute ? settings.audio.volume : 0.0;
  148. double balance = settings.audio.balance;
  149. for(uint c : range(2)) {
  150. samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
  151. if(balance < 0.0) samples[1] *= 1.0 + balance;
  152. if(balance > 0.0) samples[0] *= 1.0 - balance;
  153. }
  154. //send frame to the audio output device
  155. audioInstance.output(samples);
  156. }
  157. }
  158. auto Emulator::input(higan::Node::Input input) -> void {
  159. inputManager.poll();
  160. bool allow = program.viewport.focused();
  161. if(settings.input.unfocused == "Allow") allow = true;
  162. if(videoInstance.exclusive()) allow = true;
  163. if(auto button = input->cast<higan::Node::Button>()) {
  164. button->setValue(0);
  165. if(auto instance = button->attribute<shared_pointer<InputButton>>("instance")) {
  166. if(allow) button->setValue(instance->value());
  167. }
  168. }
  169. if(auto axis = input->cast<higan::Node::Axis>()) {
  170. axis->setValue(0);
  171. if(auto instance = axis->attribute<shared_pointer<InputAxis>>("instance")) {
  172. if(allow) axis->setValue(instance->value());
  173. }
  174. }
  175. }