diff --git a/GNUmakefile b/GNUmakefile index 616430e8..55721904 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,7 +6,7 @@ gb := gb gba := gba profile := accuracy -target := higan +target := tomoko # arch := x86 # console := true diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index efa5b2b4..2582fcda 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "094.09"; + static const char Version[] = "094.10"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/out/.gitignore b/out/.gitignore index 98f54497..827a1ea8 100644 --- a/out/.gitignore +++ b/out/.gitignore @@ -1 +1,2 @@ higan +tomoko diff --git a/target-higan/higan.cpp b/target-higan/higan.cpp deleted file mode 100644 index ee967a88..00000000 --- a/target-higan/higan.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "higan.hpp" - -struct Presentation : Window { - MenuBar menuBar{this}; - Menu menuSystem{&menuBar}; - StatusBar statusBar{this}; - - Presentation() { - menuSystem.setText("System"); - onClose(&Application::quit); - - setBackgroundColor({0, 0, 0}); - setTitle({Emulator::Name, " v", Emulator::Version}); - setSize({640, 480}); - setCentered(); - setVisible(); - } -}; - -#include -auto nall::main(lstring args) -> void { - Application::setName("higan"); - new Presentation; - Application::run(); -} diff --git a/target-higan/GNUmakefile b/target-tomoko/GNUmakefile similarity index 52% rename from target-higan/GNUmakefile rename to target-tomoko/GNUmakefile index 2f741edc..48bdf8ff 100644 --- a/target-higan/GNUmakefile +++ b/target-tomoko/GNUmakefile @@ -1,13 +1,15 @@ -name := higan +name := tomoko processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050 include processor/GNUmakefile + include fc/GNUmakefile include sfc/GNUmakefile include gb/GNUmakefile include gba/GNUmakefile -ui_objects := ui-higan +ui_objects := ui-tomoko ui-program +ui_objects += ui-library ui-presentation ui_objects += ruby hiro # platform @@ -38,16 +40,33 @@ obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/) obj/hiro.o: hiro/hiro.cpp $(call rwildcard,hiro/) $(compiler) $(hiroflags) -c $< -o $@ -obj/ui-higan.o: $(ui)/higan.cpp $(call rwildcard,$(ui)/) +obj/ui-tomoko.o: $(ui)/tomoko.cpp $(call rwildcard,$(ui)/) +obj/ui-program.o: $(ui)/program/program.cpp $(call rwildcard,$(ui)/) +obj/ui-library.o: $(ui)/library/library.cpp $(call rwildcard,$(ui)/) +obj/ui-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/) -# build +# targets build: $(objects) $(strip $(compiler) -o out/$(name) $(objects) $(link)) install: +ifeq ($(shell id -un),root) + $(error "make install should not be run as root") +else ifeq ($(platform),windows) +else ifeq ($(platform),macosx) +else cp out/$(name) $(prefix)/bin/$(name) - cp data/higan.png $(prefix)/share/icons/higan.png + cp data/higan.png $(prefix)/share/icons/$(name).png + mkdir -p ~/Emulation/System/ + cp -R profile/* ~/Emulation/System/ +endif uninstall: +ifeq ($(shell id -un),root) + $(error "make uninstall should not be run as root") +else ifeq ($(platform),windows) +else ifeq ($(platform),macosx) +else if [ -f $(prefix)/bin/$(name) ]; then rm $(prefix)/bin/$(name); fi - if [ -f $(prefix)/share/icons/higan.png ]; then rm $(prefix)/share/icons/higan.png + if [ -f $(prefix)/share/icons/$(name).png ]; then rm $(prefix)/share/icons/$(name).png; fi +endif diff --git a/target-tomoko/library/browser.cpp b/target-tomoko/library/browser.cpp new file mode 100644 index 00000000..f994f5c7 --- /dev/null +++ b/target-tomoko/library/browser.cpp @@ -0,0 +1,35 @@ +LibraryBrowser::LibraryBrowser(TabFrame& parent, Emulator::Interface::Media& media) : TabFrameItem{&parent} { + this->media = media; + setText(media.name); + layout.setMargin(5); + gameList.onActivate([&] { + libraryManager->setVisible(false); + program->loadMedia({userpath(), "Emulation/", this->media.name, "/", gameList.selected()->text(), ".", this->media.type, "/"}); + }); +} + +auto LibraryBrowser::reload() -> void { + string path = {userpath(), "Emulation/", media.name}; + directory::create(path); + + gameList.reset(); + gameList.append(ListViewColumn()); + bool first = true; + auto folders = directory::folders(path, {"*.", media.type}); + for(auto& folder : folders) { + ListViewItem item; + item.setIcon(0, Icon::Emblem::Program); + item.setText(folder.rtrim({".", media.type, "/"})); + gameList.append(item); + if(first) { + first = false; + item.setFocused(); + } + } +} + +auto LibraryBrowser::select() -> void { + reload(); + setSelected(); + gameList.setFocused(); +} diff --git a/target-tomoko/library/library.cpp b/target-tomoko/library/library.cpp new file mode 100644 index 00000000..508041eb --- /dev/null +++ b/target-tomoko/library/library.cpp @@ -0,0 +1,3 @@ +#include "../tomoko.hpp" +#include "browser.cpp" +#include "manager.cpp" diff --git a/target-tomoko/library/library.hpp b/target-tomoko/library/library.hpp new file mode 100644 index 00000000..c79383c6 --- /dev/null +++ b/target-tomoko/library/library.hpp @@ -0,0 +1,21 @@ +struct LibraryBrowser : TabFrameItem { + LibraryBrowser(TabFrame& parent, Emulator::Interface::Media& media); + auto reload() -> void; + auto select() -> void; + + Emulator::Interface::Media media; + + VerticalLayout layout{this}; + ListView gameList{&layout, Size{~0, ~0}}; +}; + +struct LibraryManager : Window { + LibraryManager(); + auto show(const string& type) -> void; + + VerticalLayout layout{this}; + TabFrame libraryFrame{&layout, Size{~0, ~0}}; + vector libraryBrowsers; +}; + +extern LibraryManager* libraryManager; diff --git a/target-tomoko/library/manager.cpp b/target-tomoko/library/manager.cpp new file mode 100644 index 00000000..731469a7 --- /dev/null +++ b/target-tomoko/library/manager.cpp @@ -0,0 +1,29 @@ +LibraryManager* libraryManager = nullptr; + +LibraryManager::LibraryManager() { + libraryManager = this; + + layout.setMargin(5); + + for(auto& emulator : program->emulators) { + for(auto& media : emulator->media) { + if(media.bootable == false) continue; + auto browser = new LibraryBrowser(libraryFrame, media); + libraryBrowsers.append(browser); + } + } + + setTitle("Library"); + setSize({640, 800}); + setPosition({0, 0}); +} + +auto LibraryManager::show(const string& type) -> void { + for(auto& browser : libraryBrowsers) { + if(type != browser->media.type) continue; + browser->select(); + } + + setVisible(); + setFocused(); +} diff --git a/target-tomoko/presentation/presentation.cpp b/target-tomoko/presentation/presentation.cpp new file mode 100644 index 00000000..a5b172d6 --- /dev/null +++ b/target-tomoko/presentation/presentation.cpp @@ -0,0 +1,33 @@ +#include "../tomoko.hpp" +Presentation* presentation = nullptr; + +Presentation::Presentation() { + presentation = this; + + libraryMenu.setText("Library"); + for(auto& emulator : program->emulators) { + for(auto& media : emulator->media) { + if(media.bootable == false) continue; + auto item = new MenuItem{&libraryMenu}; + item->setText({media.name, " ..."}).onActivate([=] { + libraryManager->show(media.type); + }); + loadBootableMedia.append(item); + } + } + + superFamicomMenu.setText("Super Famicom"); + + settingsMenu.setText("Settings"); + + toolsMenu.setText("Tools"); + + statusBar.setFont(Font::sans(8, "Bold")); + + onClose(&Application::quit); + + setTitle({"tomoko v", Emulator::Version}); + setResizable(false); + setSize({640, 480}); + setCentered(); +} diff --git a/target-tomoko/presentation/presentation.hpp b/target-tomoko/presentation/presentation.hpp new file mode 100644 index 00000000..dfccbf18 --- /dev/null +++ b/target-tomoko/presentation/presentation.hpp @@ -0,0 +1,17 @@ +struct Presentation : Window { + Presentation(); + + MenuBar menuBar{this}; + Menu libraryMenu{&menuBar}; + vector loadBootableMedia; + Menu superFamicomMenu{&menuBar}; + Menu settingsMenu{&menuBar}; + Menu toolsMenu{&menuBar}; + + VerticalLayout layout{this}; + Viewport viewport{&layout, Size{~0, ~0}}; + + StatusBar statusBar{this}; +}; + +extern Presentation* presentation; diff --git a/target-tomoko/program/interface.cpp b/target-tomoko/program/interface.cpp new file mode 100644 index 00000000..8fa24a62 --- /dev/null +++ b/target-tomoko/program/interface.cpp @@ -0,0 +1,78 @@ +//request from emulation core to load non-volatile media folder +auto Program::loadRequest(unsigned id, string name, string type) -> void { +} + +//request from emulation core to load non-volatile media file +auto Program::loadRequest(unsigned id, string path) -> void { + string location = {mediaPaths(emulator().group(id)), path}; + if(!file::exists(location)) return; + mmapstream stream{location}; + return emulator().load(id, stream); +} + +auto Program::saveRequest(unsigned id, string path) -> void { +} + +auto Program::videoColor(unsigned source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 { + alpha >>= 8; + red >>= 8; + green >>= 8; + blue >>= 8; + return alpha << 24 | red << 16 | green << 8 | blue << 0; +} + +auto Program::videoRefresh(const uint32* palette, const uint32* data, unsigned pitch, unsigned width, unsigned height) -> void { + uint32* output; + unsigned length; + + if(video.lock(output, length, width, height)) { + pitch >>= 2, length >>= 2; + + for(auto y : range(height)) { + const uint32* sp = data + y * pitch; + uint32* dp = output + y * length; + for(auto x : range(width)) { + *dp++ = palette[*sp++]; + } + } + + video.unlock(); + video.refresh(); + } + + static unsigned frameCounter = 0; + static time_t previous, current; + frameCounter++; + + time(¤t); + if(current != previous) { + previous = current; + presentation->statusBar.setText({"FPS: ", frameCounter}); + frameCounter = 0; + } +} + +auto Program::audioSample(int16 lsample, int16 rsample) -> void { + signed samples[] = {lsample, rsample}; + dsp.sample(samples); + while(dsp.pending()) { + dsp.read(samples); + audio.sample(samples[0], samples[1]); + } +} + +auto Program::inputPoll(unsigned port, unsigned device, unsigned input) -> int16 { +} + +auto Program::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) -> void { +} + +auto Program::dipSettings(const Markup::Node& node) -> unsigned { +} + +auto Program::path(unsigned group) -> string { + return mediaPaths(group); +} + +auto Program::notify(string text) -> void { +} diff --git a/target-tomoko/program/media.cpp b/target-tomoko/program/media.cpp new file mode 100644 index 00000000..1c83370d --- /dev/null +++ b/target-tomoko/program/media.cpp @@ -0,0 +1,25 @@ +auto Program::loadMedia(string location) -> void { + location.transform("\\", "/"); + if(!directory::exists(location)) return; + + string type = suffixname(location).ltrim("."); + for(auto& emulator : emulators) { + for(auto& media : emulator->media) { + if(media.bootable == false) continue; + if(media.type != type) continue; + return loadMedia(*emulator, media, location); + } + } +} + +auto Program::loadMedia(Emulator::Interface& _emulator, Emulator::Interface::Media& media, const string& location) -> void { + mediaPaths(0) = {userpath(), "Emulation/System/", media.name, ".sys/"}; + mediaPaths(media.id) = location; + + setEmulator(_emulator); + emulator().paletteUpdate(Emulator::Interface::PaletteMode::Standard); + emulator().load(media.id); + emulator().power(); + + presentation->setTitle(emulator().title()); +} diff --git a/target-tomoko/program/program.cpp b/target-tomoko/program/program.cpp new file mode 100644 index 00000000..6a6d38e6 --- /dev/null +++ b/target-tomoko/program/program.cpp @@ -0,0 +1,80 @@ +#include "../tomoko.hpp" +#include +#include +#include +#include +#include "interface.cpp" +#include "media.cpp" +Program* program = nullptr; + +Program::Program() { + program = this; + Application::onMain({&Program::main, this}); + + emulators.append(new Famicom::Interface); + emulators.append(new SuperFamicom::Interface); + emulators.append(new GameBoy::Interface); + emulators.append(new GameBoyAdvance::Interface); + for(auto& emulator : emulators) emulator->bind = this; + + new LibraryManager; + new Presentation; + + presentation->setVisible(); + + video.driver("XShm"); + video.set(Video::Handle, presentation->viewport.handle()); + video.set(Video::Synchronize, false); + if(!video.init()) { video.driver("None"); video.init(); } + + audio.driver("OpenAL"); + audio.set(Audio::Handle, presentation->viewport.handle()); + audio.set(Audio::Synchronize, false); + audio.set(Audio::Frequency, 96000u); + audio.set(Audio::Latency, 80u); + if(!audio.init()) { audio.driver("None"); audio.init(); } + + input.driver("XInput"); + input.set(Input::Handle, presentation->viewport.handle()); + if(!input.init()) { input.driver("None"); input.init(); } + + dsp.setPrecision(16); + dsp.setBalance(0.0); + dsp.setVolume(1.0); + dsp.setFrequency(32040); + dsp.setResampler(DSP::ResampleEngine::Sinc); + dsp.setResamplerFrequency(96000); + + uint32* output; + unsigned length; + if(video.lock(output, length, 640, 480)) { + for(auto y : range(480)) { + uint32* dp = output + y * (length >> 2); + for(auto x : range(640)) { + *dp++ = 0xff401010; + } + } + + video.unlock(); + video.refresh(); + } +} + +auto Program::emulator() -> Emulator::Interface& { + if(activeEmulator == nullptr) throw; + return *activeEmulator; +} + +auto Program::main() -> void { + if(activeEmulator == nullptr || emulator().loaded() == false) { + audio.clear(); + usleep(20 * 1000); + return; + } + + emulator().run(); +} + +auto Program::setEmulator(Emulator::Interface& emulator) -> void { + activeEmulator = &emulator; +} diff --git a/target-tomoko/program/program.hpp b/target-tomoko/program/program.hpp new file mode 100644 index 00000000..d3706073 --- /dev/null +++ b/target-tomoko/program/program.hpp @@ -0,0 +1,32 @@ +struct Program : Emulator::Interface::Bind { + //program.cpp + Program(); + auto emulator() -> Emulator::Interface&; + auto main() -> void; + auto setEmulator(Emulator::Interface&) -> void; + + //interface.cpp + auto loadRequest(unsigned id, string name, string type) -> void override; + auto loadRequest(unsigned id, string path) -> void override; + auto saveRequest(unsigned id, string path) -> void override; + auto videoColor(unsigned source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 override; + auto videoRefresh(const uint32* palette, const uint32* data, unsigned pitch, unsigned width, unsigned height) -> void override; + auto audioSample(int16 lsample, int16 rsample) -> void override; + auto inputPoll(unsigned port, unsigned device, unsigned input) -> int16 override; + auto inputRumble(unsigned port, unsigned device, unsigned input, bool enable) -> void override; + auto dipSettings(const Markup::Node& node) -> unsigned override; + auto path(unsigned group) -> string override; + auto notify(string text) -> void override; + + //media.cpp + auto loadMedia(string location) -> void; + auto loadMedia(Emulator::Interface& interface, Emulator::Interface::Media& media, const string& location) -> void; + + DSP dsp; + + vector emulators; + Emulator::Interface* activeEmulator = nullptr; + vector mediaPaths; +}; + +extern Program* program; diff --git a/target-tomoko/tomoko.cpp b/target-tomoko/tomoko.cpp new file mode 100644 index 00000000..4e822626 --- /dev/null +++ b/target-tomoko/tomoko.cpp @@ -0,0 +1,8 @@ +#include "tomoko.hpp" + +#include +auto nall::main(lstring args) -> void { + Application::setName("tomoko"); + new Program; + Application::run(); +} diff --git a/target-higan/higan.hpp b/target-tomoko/tomoko.hpp similarity index 62% rename from target-higan/higan.hpp rename to target-tomoko/tomoko.hpp index 97b63a1f..ad45ac1e 100644 --- a/target-higan/higan.hpp +++ b/target-tomoko/tomoko.hpp @@ -6,3 +6,7 @@ using namespace nall; using namespace ruby; using namespace hiro; + +#include "program/program.hpp" +#include "library/library.hpp" +#include "presentation/presentation.hpp"