Update to v094r02 release.

byuu says:

Changelog:
- ethos: use nall::programpath() instead of realpath(argv[0]) to get
  executable path
- loki: add presentation window
- loki: add terminal window
- loki: add interface to emulation core
- loki: add ruby
- loki: add enough support to run games and save data on exit
    - load game folders via command-line (or drop folder onto binary),
      use "r" to start, "p" to pause ... temporary command names

I'll probably have to say this several times, but for now, loki is only
available on Linux/GTK+, due to the use of the Console widget. Support
for other platforms can come later easily enough.
This commit is contained in:
Tim Allen 2014-01-29 23:57:02 +11:00
parent c54be74832
commit 187ba0eec6
16 changed files with 4204 additions and 9 deletions

View File

@ -14,8 +14,8 @@ target := ethos
# console := true # console := true
# compiler # compiler
flags += -I. -O3 -fomit-frame-pointer flags += -I. -O3 -fomit-frame-pointer
link += link +=
objects := libco objects := libco
# profile-guided optimization mode # profile-guided optimization mode

View File

@ -3,7 +3,7 @@
namespace Emulator { namespace Emulator {
static const char Name[] = "higan"; static const char Name[] = "higan";
static const char Version[] = "094.01"; static const char Version[] = "094.02";
static const char Author[] = "byuu"; static const char Author[] = "byuu";
static const char License[] = "GPLv3"; static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/"; static const char Website[] = "http://byuu.org/";

View File

@ -45,7 +45,7 @@ Program::Program(int argc, char** argv) {
pause = false; pause = false;
autopause = false; autopause = false;
basepath = dir(realpath(argv[0])); basepath = nall::programpath();
userpath = {nall::configpath(), "higan/"}; userpath = {nall::configpath(), "higan/"};
sharedpath = {nall::sharedpath(), "higan/"}; sharedpath = {nall::sharedpath(), "higan/"};
directory::create(userpath); directory::create(userpath);

View File

@ -6,10 +6,23 @@ include processor/Makefile
include sfc/Makefile include sfc/Makefile
include gb/Makefile include gb/Makefile
ui_objects := ui-loki ui_objects := ui-loki ui-interface ui-presentation ui-terminal
ui_objects += phoenix ui_objects += ruby phoenix
ui_objects += $(if $(call streq,$(platform),windows),resource) ui_objects += $(if $(call streq,$(platform),windows),resource)
ifeq ($(platform),windows)
ruby := video.wgl audio.xaudio2 input.windows
else ifeq ($(platform),macosx)
ruby := video.cgl audio.openal input.carbon
else ifeq ($(platform),linux)
ruby := video.glx audio.alsa input.udev
else ifeq ($(platform),bsd)
ruby := video.glx audio.openal input.x
endif
include ruby/Makefile
link += $(rubylink)
include phoenix/Makefile include phoenix/Makefile
link += $(phoenixlink) link += $(phoenixlink)
@ -17,6 +30,12 @@ objects := $(ui_objects) $(objects)
objects := $(patsubst %,obj/%.o,$(objects)) objects := $(patsubst %,obj/%.o,$(objects))
obj/ui-loki.o: $(ui)/loki.cpp $(call rwildcard,$(ui)/) obj/ui-loki.o: $(ui)/loki.cpp $(call rwildcard,$(ui)/)
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
obj/ui-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
obj/ui-terminal.o: $(ui)/terminal/terminal.cpp $(call rwildcard,$(ui)/)
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/)
$(compiler) $(rubyflags) -c $< -o $@
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/) obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/)
$(compiler) $(phoenixflags) -c $< -o $@ $(compiler) $(phoenixflags) -c $< -o $@
@ -44,6 +63,9 @@ else
$(strip $(compiler) -o out/$(name) $(objects) $(link)) $(strip $(compiler) -o out/$(name) $(objects) $(link))
endif endif
resource:
sourcery $(ui)/resource/resource.bml $(ui)/resource/resource.cpp $(ui)/resource/resource.hpp
install: install:
ifeq ($(platform),windows) ifeq ($(platform),windows)
else ifeq ($(platform),macosx) else ifeq ($(platform),macosx)

View File

@ -0,0 +1,150 @@
#include "../loki.hpp"
Interface* interface = nullptr;
SuperFamicom::Interface* emulator = nullptr;
Interface::Interface() {
interface = this;
emulator = new SuperFamicom::Interface;
emulator->bind = this;
}
bool Interface::load(string pathname) {
pathname.transform("\\", "/").rtrim("/");
if(!directory::exists(pathname)) return false;
string type = extension(pathname);
pathname.append("/");
for(auto& media : emulator->media) {
if(media.bootable == false) continue;
if(type != media.type) continue;
pathnames.reset();
pathnames(0) = program->path({media.name, ".sys/"});
pathnames(media.id) = pathname;
emulator->load(media.id);
emulator->paletteUpdate(Emulator::Interface::PaletteMode::Standard);
emulator->power();
return true;
}
return false;
}
void Interface::unload() {
emulator->unload();
}
void Interface::inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
if(device.isKeyboard() == false) return;
switch(input) {
case 84: gamepad.up = newValue; break;
case 85: gamepad.down = newValue; break;
case 86: gamepad.left = newValue; break;
case 87: gamepad.right = newValue; break;
case 60: gamepad.b = newValue; break;
case 58: gamepad.a = newValue; break;
case 35: gamepad.y = newValue; break;
case 53: gamepad.x = newValue; break;
case 38: gamepad.l = newValue; break;
case 37: gamepad.r = newValue; break;
case 65: gamepad.select = newValue; break;
case 89: gamepad.start = newValue; break;
}
}
//bindings
void Interface::loadRequest(unsigned id, string name, string type) {
}
void Interface::loadRequest(unsigned id, string path) {
string pathname = {pathnames(emulator->group(id)), path};
if(file::exists(pathname) == false) return;
mmapstream stream(pathname);
emulator->load(id, stream);
}
void Interface::saveRequest(unsigned id, string path) {
string pathname = {pathnames(emulator->group(id)), path};
filestream stream(pathname, file::mode::write);
emulator->save(id, stream);
}
uint32_t Interface::videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue) {
return ((alpha >> 8) << 24) | ((red >> 8) << 16) | ((green >> 8) << 8) | ((blue >> 8) << 0);
}
void Interface::videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) {
uint32_t* output;
unsigned outputPitch;
if(video.lock(output, outputPitch, width, height)) {
pitch >>= 2, outputPitch >>= 2;
for(unsigned y = 0; y < height; y++) {
const uint32_t* sp = data + y * pitch;
uint32_t* dp = output + y * outputPitch;
for(unsigned x = 0; x < width; x++) {
*dp++ = palette[*sp++];
}
}
video.unlock();
video.refresh();
}
input.poll();
}
void Interface::audioSample(int16_t lsample, int16_t rsample) {
signed samples[] = {lsample, rsample};
dspaudio.sample(samples);
while(dspaudio.pending()) {
dspaudio.read(samples);
audio.sample(samples[0], samples[1]);
}
}
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
if(presentation->focused() == false) return 0;
if(port != 0 || device != 0) return 0;
switch(input) {
case 0: return gamepad.b;
case 1: return gamepad.y;
case 2: return gamepad.select;
case 3: return gamepad.start;
case 4: return gamepad.up;
case 5: return gamepad.down;
case 6: return gamepad.left;
case 7: return gamepad.right;
case 8: return gamepad.a;
case 9: return gamepad.x;
case 10: return gamepad.l;
case 11: return gamepad.r;
}
return 0;
}
void Interface::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) {
}
unsigned Interface::dipSettings(const Markup::Node& node) {
return 0;
}
string Interface::path(unsigned group) {
return pathnames(group);
}
string Interface::server() {
return "";
}
void Interface::notify(string text) {
}

View File

@ -0,0 +1,39 @@
struct Interface : Emulator::Interface::Bind {
Interface();
bool load(string pathname);
void unload();
void inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
//bindings
void loadRequest(unsigned id, string name, string type);
void loadRequest(unsigned id, string path);
void saveRequest(unsigned id, string path);
uint32_t videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height);
void audioSample(int16_t lsample, int16_t rsample);
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
void inputRumble(unsigned port, unsigned device, unsigned input, bool enable);
unsigned dipSettings(const Markup::Node& node);
string path(unsigned group);
string server();
void notify(string text);
lstring pathnames;
struct Gamepad {
bool up = false;
bool down = false;
bool left = false;
bool right = false;
bool b = false;
bool a = false;
bool y = false;
bool x = false;
bool l = false;
bool r = false;
bool select = false;
bool start = false;
} gamepad;
};
extern Interface* interface;
extern SuperFamicom::Interface* emulator;

View File

@ -1,4 +1,5 @@
#include "loki.hpp" #include "loki.hpp"
#include "resource/resource.cpp"
Program* program = nullptr; Program* program = nullptr;
DSP dspaudio; DSP dspaudio;
@ -14,15 +15,61 @@ string Program::path(string name) {
} }
void Program::main() { void Program::main() {
if(pause) {
usleep(20 * 1000);
return;
}
emulator->run();
} }
Program::Program(int argc, char** argv) { Program::Program(string pathname) {
program = this; program = this;
basepath = nall::programpath(); basepath = nall::programpath();
userpath = {nall::configpath(), "loki/"}; userpath = {nall::configpath(), "loki/"};
sharedpath = {nall::sharedpath(), "loki/"}; sharedpath = {nall::sharedpath(), "loki/"};
directory::create(userpath); directory::create(userpath);
new Interface;
new Presentation;
new Terminal;
presentation->setVisible();
terminal->setVisible();
Application::processEvents();
video.driver(video.optimalDriver());
video.set(Video::Handle, presentation->viewport.handle());
video.set(Video::Synchronize, false);
video.set(Video::Filter, Video::FilterNearest);
if(video.init() == false) { video.driver("None"); video.init(); }
audio.driver(audio.optimalDriver());
audio.set(Audio::Handle, presentation->viewport.handle());
audio.set(Audio::Synchronize, true);
audio.set(Audio::Frequency, 48000u);
if(audio.init() == false) { audio.driver("None"); audio.init(); }
input.driver(input.optimalDriver());
input.set(Input::Handle, presentation->viewport.handle());
if(input.init() == false) { input.driver("None"); input.init(); }
input.onChange = {&Interface::inputEvent, interface};
dspaudio.setPrecision(16);
dspaudio.setBalance(0.0);
dspaudio.setFrequency(32000);
dspaudio.setResampler(DSP::ResampleEngine::Hermite);
dspaudio.setResamplerFrequency(48000);
presentation->showSplash();
interface->load(pathname);
Application::main = {&Program::main, this};
Application::run();
interface->unload();
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
@ -30,8 +77,14 @@ int main(int argc, char** argv) {
utf8_args(argc, argv); utf8_args(argc, argv);
#endif #endif
if(argc != 2 || !directory::exists(argv[1])) {
print("loki v", Emulator::Version, "\n");
print("usage: loki /path/game.sfc/\n");
return 0;
}
Application::setName("loki"); Application::setName("loki");
new Program(argc, argv); new Program(argv[1]);
delete program; delete program;
return 0; return 0;

View File

@ -1,4 +1,5 @@
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <sfc/interface/interface.hpp>
#include <nall/platform.hpp> #include <nall/platform.hpp>
#include <nall/config.hpp> #include <nall/config.hpp>
@ -12,17 +13,27 @@
#include <nall/stream/vector.hpp> #include <nall/stream/vector.hpp>
using namespace nall; using namespace nall;
#include <ruby/ruby.hpp>
using namespace ruby;
#include <phoenix/phoenix.hpp> #include <phoenix/phoenix.hpp>
using namespace phoenix; using namespace phoenix;
#include "interface/interface.hpp"
#include "presentation/presentation.hpp"
#include "terminal/terminal.hpp"
#include "resource/resource.hpp"
struct Program { struct Program {
string basepath; string basepath;
string userpath; string userpath;
string sharedpath; string sharedpath;
bool pause = true;
string path(string name); string path(string name);
void main(); void main();
Program(int argc, char** argv); Program(string pathname);
}; };
extern Program* program; extern Program* program;

View File

@ -0,0 +1,36 @@
#include "../loki.hpp"
Presentation* presentation = nullptr;
Presentation::Presentation() {
presentation = this;
setTitle({"loki v", Emulator::Version});
setWindowGeometry({0, 0, 512, 480});
setResizable(false);
layout.append(viewport, {0, 0, 512, 480});
append(layout);
onClose = &Application::quit;
splash.allocate(512, 480);
splash.verticalGradient(0xff00003f, 0xff000000, 512, 480, 256, 0);
nall::image floor;
floor.allocate(512, 480);
floor.radialGradient(0xffff0000, 0x00000000, 384, 240, 256, 415);
splash.impose(image::blend::sourceAlpha, 0, 0, floor, 0, 0, floor.width, floor.height);
nall::image loki(resource::loki, sizeof resource::loki);
splash.impose(image::blend::sourceAlpha, (512 - loki.width) / 2, (480 - loki.height) / 2, loki, 0, 0, loki.width, loki.height);
}
void Presentation::showSplash() {
uint32_t* data;
unsigned pitch;
if(video.lock(data, pitch, 512, 480)) {
for(unsigned y = 0; y < 480; y++) {
memcpy((uint8_t*)data + y * pitch, splash.data + y * splash.pitch, 512 * sizeof(uint32_t));
}
video.unlock();
video.refresh();
}
}

View File

@ -0,0 +1,10 @@
struct Presentation : Window {
FixedLayout layout;
Viewport viewport;
nall::image splash;
Presentation();
void showSplash();
};
extern Presentation* presentation;

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -0,0 +1,2 @@
resource name=resource
binary id=loki name=loki.png

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
namespace resource {
extern const uint8_t loki[121905];
};

View File

@ -0,0 +1,42 @@
#include "../loki.hpp"
Terminal* terminal = nullptr;
Terminal::Terminal() {
terminal = this;
setWindowGeometry({0, 480 + frameMargin().height, 800, 480});
console.setFont(Font::monospace(8));
print("loki v", Emulator::Version, "\n\n");
print("$ ");
layout.append(console, {~0, ~0});
append(layout);
onClose = &Application::quit;
console.onActivate = {&Terminal::command, this};
}
void Terminal::command(string s) {
if(s.empty()) {
} else if(s == "quit" || s == "exit") {
Application::quit();
} else if(s == "clear" || s == "reset") {
reset();
} else if(s == "r") {
program->pause = false;
} else if(s == "p") {
program->pause = true;
} else {
print("unrecognized command\n");
}
print("$ ");
}
void Terminal::reset() {
console.reset();
}
template<typename... Args> void Terminal::print(Args&&... args) {
console.print(std::forward<Args>(args)...);
}

View File

@ -0,0 +1,11 @@
struct Terminal : Window {
VerticalLayout layout;
Console console;
Terminal();
void command(string s);
void reset();
template<typename... Args> void print(Args&&... args);
};
extern Terminal* terminal;