Update to v093r05 release.

byuu says:

Library concept has been refined as per the general forum discussion.
This commit is contained in:
Tim Allen 2013-12-03 21:01:59 +11:00
parent b4f18c3b47
commit ed4e87f65e
37 changed files with 303 additions and 212 deletions

View File

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

View File

@ -8,7 +8,7 @@ namespace nall {
struct beatArchive : beatBase {
bool create(const string& beatname, string pathname, const string& metadata = "") {
if(fp.open(beatname, file::mode::write) == false) return false;
if(pathname.endswith("/") == false) pathname.append("/");
if(pathname.endsWith("/") == false) pathname.append("/");
checksum = ~0;
writeString("BPA1");
@ -18,7 +18,7 @@ struct beatArchive : beatBase {
lstring list;
ls(list, pathname, pathname);
for(auto &name : list) {
if(name.endswith("/")) {
if(name.endsWith("/")) {
name.rtrim<1>("/");
writeNumber(0 | ((name.length() - 1) << 1));
writeString(name);
@ -46,7 +46,7 @@ struct beatArchive : beatBase {
bool unpack(const string& beatname, string pathname) {
if(fp.open(beatname, file::mode::read) == false) return false;
if(pathname.endswith("/") == false) pathname.append("/");
if(pathname.endsWith("/") == false) pathname.append("/");
checksum = ~0;
if(readString(4) != "BPA1") return false;

View File

@ -34,7 +34,7 @@ struct bpsmulti {
ls(targetList, targetPath, targetPath);
for(auto& targetName : targetList) {
if(targetName.endswith("/")) {
if(targetName.endsWith("/")) {
targetName.rtrim<1>("/");
writeNumber(CreatePath | ((targetName.length() - 1) << 2));
writeString(targetName);

View File

@ -168,7 +168,7 @@ private:
inline bool directory::remove(const string& pathname) {
lstring list = directory::contents(pathname);
for(auto& name : list) {
if(name.endswith("/")) directory::remove({pathname, name});
if(name.endsWith("/")) directory::remove({pathname, name});
else file::remove({pathname, name});
}
return rmdir(pathname) == 0;

View File

@ -38,7 +38,7 @@ private:
#if defined(PLATFORM_X)
inline bool library::open(const string& name, const string& path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endswith("/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY);
handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endsWith("/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY);
return handle;
}
@ -62,7 +62,7 @@ inline void library::close() {
#elif defined(PLATFORM_MACOSX)
inline bool library::open(const string& name, const string& path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endswith("/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY);
handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endsWith("/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY);
return handle;
}
@ -86,7 +86,7 @@ inline void library::close() {
#elif defined(PLATFORM_WINDOWS)
inline bool library::open(const string& name, const string& path) {
if(handle) close();
string filepath(path, !path.empty() && !path.endswith("/") && !path.endswith("\\") ? "/" : "", name, ".dll");
string filepath(path, !path.empty() && !path.endswith("/") && !path.endsWith("\\") ? "/" : "", name, ".dll");
handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
return handle;
}

View File

@ -41,11 +41,11 @@ template<typename T> struct group : protected vector<T*> {
return false;
}
struct iterator : protected vector<T*>::const_iterator {
T& operator*() const { return *vector<T*>::const_iterator::operator*(); }
bool operator!=(const iterator& source) const { return vector<T*>::const_iterator::operator!=(source); }
iterator& operator++() { vector<T*>::const_iterator::operator++(); return *this; }
iterator(const group& source, unsigned position) : vector<T*>::const_iterator(source, position) {}
struct iterator : protected vector<T*>::constIterator {
T& operator*() const { return *vector<T*>::constIterator::operator*(); }
bool operator!=(const iterator& source) const { return vector<T*>::constIterator::operator!=(source); }
iterator& operator++() { vector<T*>::constIterator::operator++(); return *this; }
iterator(const group& source, unsigned position) : vector<T*>::constIterator(source, position) {}
};
const iterator begin() const { return iterator(*this, 0); }

View File

@ -81,7 +81,7 @@ struct http {
if(length <= 0) return output;
buffer[1] = 0;
output.append(buffer);
} while(output.endswith("\r\n\r\n") == false);
} while(output.endsWith("\r\n\r\n") == false);
return output;
}
@ -93,7 +93,7 @@ struct http {
if(length <= 0) return output;
buffer[1] = 0;
output.append(buffer);
} while(output.endswith("\r\n") == false);
} while(output.endsWith("\r\n") == false);
return output;
}

View File

@ -113,6 +113,7 @@ unsigned image::bitShift(uint64_t color) {
}
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
if(sourceDepth == 0 || targetDepth == 0) return 0;
while(sourceDepth < targetDepth) {
color = (color << sourceDepth) | color;
sourceDepth += sourceDepth;

View File

@ -176,6 +176,10 @@ struct context {
if(mosaicWidth < 1) mosaicWidth = 1;
if(mosaicHeight < 1) mosaicHeight = 1;
//set alpha to full opacity
paddingColor |= 255u << 24;
for(auto& color : palette) color |= 255u << 24;
}
void reset() {
@ -208,7 +212,7 @@ struct context {
paddingWidth = 0;
paddingHeight = 0;
paddingColor = 0x000000;
paddingColor = 0;
palette.reset();
}

View File

@ -9,7 +9,7 @@ struct parser {
//export from bitstream to canvas
void load(bitstream& stream, uint64_t offset, context& ctx, unsigned width, unsigned height) {
canvas.allocate(width, height);
canvas.clear(ctx.paddingColor);
canvas.fill(ctx.paddingColor);
parse(1, stream, offset, ctx, width, height);
}
@ -20,7 +20,7 @@ struct parser {
return true;
}
inline parser() : canvas(0, 32, 0u, 255u << 16, 255u << 8, 255u << 0) {
inline parser() : canvas(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0) {
}
private:

View File

@ -6,9 +6,9 @@ namespace nall {
#define autostream(...) (*makestream(__VA_ARGS__))
inline std::unique_ptr<stream> makestream(const string& path) {
if(path.ibeginswith("http://")) return std::unique_ptr<stream>(new httpstream(path, 80));
if(path.iendswith(".gz")) return std::unique_ptr<stream>(new gzipstream(filestream{path}));
if(path.iendswith(".zip")) return std::unique_ptr<stream>(new zipstream(filestream{path}));
if(path.ibeginsWith("http://")) return std::unique_ptr<stream>(new httpstream(path, 80));
if(path.iendsWith(".gz")) return std::unique_ptr<stream>(new gzipstream(filestream{path}));
if(path.iendsWith(".zip")) return std::unique_ptr<stream>(new zipstream(filestream{path}));
return std::unique_ptr<stream>(new mmapstream(path));
}

View File

@ -81,10 +81,10 @@ public:
inline bool match(rstring) const;
inline bool imatch(rstring) const;
inline bool beginswith(rstring) const;
inline bool ibeginswith(rstring) const;
inline bool endswith(rstring) const;
inline bool iendswith(rstring) const;
inline bool beginsWith(rstring) const;
inline bool ibeginsWith(rstring) const;
inline bool endsWith(rstring) const;
inline bool iendsWith(rstring) const;
inline string slice(unsigned offset, unsigned length = ~0u) const;

View File

@ -39,11 +39,11 @@ inline string evaluateExpression(Node* node) {
inline int64_t evaluateInteger(Node* node) {
if(node->type == Node::Type::Literal) {
if(node->literal.beginswith("0b")) return nall::binary(node->literal);
if(node->literal.beginswith("0o")) return nall::octal(node->literal);
if(node->literal.beginswith("0x")) return nall::hex(node->literal);
if(node->literal.beginswith("%")) return nall::binary(node->literal);
if(node->literal.beginswith("$")) return nall::hex(node->literal);
if(node->literal.beginsWith("0b")) return nall::binary(node->literal);
if(node->literal.beginsWith("0o")) return nall::octal(node->literal);
if(node->literal.beginsWith("0x")) return nall::hex(node->literal);
if(node->literal.beginsWith("%")) return nall::binary(node->literal);
if(node->literal.beginsWith("$")) return nall::hex(node->literal);
return nall::integer(node->literal);
}

View File

@ -4,7 +4,7 @@ namespace nall {
namespace Markup {
inline Node Document(const string& markup) {
if(markup.beginswith("<")) return XML::Document(markup);
if(markup.beginsWith("<")) return XML::Document(markup);
return BML::Document(markup);
}

View File

@ -131,8 +131,8 @@ struct Node {
vector<Node>::iterator begin() { return children.begin(); }
vector<Node>::iterator end() { return children.end(); }
const vector<Node>::const_iterator begin() const { return children.begin(); }
const vector<Node>::const_iterator end() const { return children.end(); }
const vector<Node>::constIterator begin() const { return children.begin(); }
const vector<Node>::constIterator end() const { return children.end(); }
Node() : attribute(false), level(0) {}

View File

@ -8,7 +8,7 @@ string activepath() {
string result = path;
if(result.empty()) result = ".";
result.transform("\\", "/");
if(result.endswith("/") == false) result.append("/");
if(result.endsWith("/") == false) result.append("/");
return result;
}
@ -18,7 +18,7 @@ string realpath(const string& name) {
if(::realpath(name, path)) result = path;
if(result.empty()) result = {activepath(), name};
result.transform("\\", "/");
if(result.endswith("/") == false) result.append("/");
if(result.endsWith("/") == false) result.append("/");
return result;
}
@ -36,7 +36,7 @@ string userpath() {
result = userinfo->pw_dir;
#endif
if(result.empty()) result = ".";
if(result.endswith("/") == false) result.append("/");
if(result.endsWith("/") == false) result.append("/");
return result;
}
@ -55,7 +55,7 @@ string configpath() {
result = {userpath(), ".config/"};
#endif
if(result.empty()) result = ".";
if(result.endswith("/") == false) result.append("/");
if(result.endsWith("/") == false) result.append("/");
return result;
}
@ -72,7 +72,7 @@ string sharedpath() {
result = "/usr/share/";
#endif
if(result.empty()) result = ".";
if(result.endswith("/") == false) result.append("/");
if(result.endsWith("/") == false) result.append("/");
return result;
}

View File

@ -28,22 +28,22 @@ bool string::iequals(rstring source) const {
return icompare(source) == 0;
}
bool string::beginswith(rstring source) const {
bool string::beginsWith(rstring source) const {
if(source.size() > size()) return false;
return memcmp(data(), source.data(), source.size()) == 0;
}
bool string::ibeginswith(rstring source) const {
bool string::ibeginsWith(rstring source) const {
if(source.size() > size()) return false;
return imemcmp(data(), source.data(), source.size()) == 0;
}
bool string::endswith(rstring source) const {
bool string::endsWith(rstring source) const {
if(source.size() > size()) return false;
return memcmp(data() + size() - source.size(), source.data(), source.size()) == 0;
}
bool string::iendswith(rstring source) const {
bool string::iendsWith(rstring source) const {
if(source.size() > size()) return false;
return imemcmp(data() + size() - source.size(), source.data(), source.size()) == 0;
}

View File

@ -102,7 +102,7 @@ public:
return last();
}
bool appendonce(const T& data) {
bool appendOnce(const T& data) {
if(find(data)) return false;
return append(data), true;
}
@ -136,8 +136,8 @@ public:
objectsize -= length;
}
void removefirst() { return remove(0); }
void removelast() { return remove(~0u); }
void removeFirst() { return remove(0); }
void removeLast() { return remove(~0u); }
T take(unsigned position = ~0u) {
if(position == ~0u) position = objectsize - 1;
@ -146,8 +146,8 @@ public:
return object;
}
T takefirst() { return take(0); }
T takelast() { return take(~0u); }
T takeFirst() { return take(0); }
T takeLast() { return take(~0u); }
void reverse() {
unsigned pivot = size() / 2;
@ -226,19 +226,19 @@ public:
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, size()); }
struct const_iterator {
struct constIterator {
const T& operator*() const { return source.operator[](position); }
bool operator!=(const const_iterator& source) const { return position != source.position; }
const_iterator& operator++() { position++; return *this; }
const_iterator(const vector& source, unsigned position) : source(source), position(position) {}
bool operator!=(const constIterator& source) const { return position != source.position; }
constIterator& operator++() { position++; return *this; }
constIterator(const vector& source, unsigned position) : source(source), position(position) {}
private:
const vector& source;
unsigned position;
};
const const_iterator begin() const { return const_iterator(*this, 0); }
const const_iterator end() const { return const_iterator(*this, size()); }
const constIterator begin() const { return constIterator(*this, 0); }
const constIterator end() const { return constIterator(*this, size()); }
//copy
inline vector& operator=(const vector& source) {

View File

@ -33,7 +33,7 @@ lstring DropPaths(id<NSDraggingInfo> sender) {
NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
for(unsigned n = 0; n < [files count]; n++) {
string path = [[files objectAtIndex:n] UTF8String];
if(directory::exists(path) && !path.endswith("/")) path.append("/");
if(directory::exists(path) && !path.endsWith("/")) path.append("/");
paths.append(path);
}
}

View File

@ -31,7 +31,7 @@ string pBrowserWindow::directory(BrowserWindow::State& state) {
}
gtk_widget_destroy(dialog);
if(name && !name.endswith("/")) name.append("/");
if(name && !name.endsWith("/")) name.append("/");
return name;
}

View File

@ -38,7 +38,7 @@ static lstring DropPaths(GtkSelectionData* data) {
string path = pathname;
g_free(pathname);
if(directory::exists(path) && !path.endswith("/")) path.append("/");
if(directory::exists(path) && !path.endsWith("/")) path.append("/");
paths.append(path);
}

View File

@ -3,7 +3,7 @@ namespace phoenix {
static void TabFrame_change(GtkNotebook* notebook, GtkWidget* page, unsigned selection, TabFrame* self) {
self->state.selection = selection;
self->p.synchronizeLayout();
if(self->onChange) self->onChange();
if(!self->p.locked && self->onChange) self->onChange();
}
void pTabFrame::append(string text, const image& image) {
@ -73,7 +73,9 @@ void pTabFrame::setImage(unsigned selection, const image& image) {
}
void pTabFrame::setSelection(unsigned selection) {
locked = true;
gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkWidget), selection);
locked = false;
}
void pTabFrame::setText(unsigned selection, string text) {

View File

@ -7,7 +7,7 @@ string pBrowserWindow::directory(BrowserWindow::State& state) {
QString::fromUtf8(state.path), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
);
string name = directory.toUtf8().constData();
if(name && name.endswith("/") == false) name.append("/");
if(name && name.endsWith("/") == false) name.append("/");
return name;
}

View File

@ -1,7 +1,7 @@
/****************************************************************************
** Meta object code from reading C++ file 'platform.moc.hpp'
**
** Created: Sun Nov 24 07:06:37 2013
** Created: Fri Nov 29 09:24:08 2013
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
**
** WARNING! All changes made in this file will be lost!

View File

@ -16,7 +16,7 @@ static lstring DropPaths(QDropEvent* event) {
for(unsigned n = 0; n < urls.size(); n++) {
string path = urls[n].path().toUtf8().constData();
if(path.empty()) continue;
if(directory::exists(path) && !path.endswith("/")) path.append("/");
if(directory::exists(path) && !path.endsWith("/")) path.append("/");
paths.append(path);
}

View File

@ -46,8 +46,10 @@ void pTabFrame::setImage(unsigned selection, const image& image) {
}
void pTabFrame::setSelection(unsigned selection) {
locked = true;
qtTabFrame->setCurrentIndex(selection);
synchronizeLayout();
locked = false;
}
void pTabFrame::setText(unsigned selection, string text) {
@ -89,7 +91,7 @@ void pTabFrame::synchronizeLayout() {
void pTabFrame::onChange(int selection) {
tabFrame.state.selection = selection;
synchronizeLayout();
if(tabFrame.onChange) tabFrame.onChange();
if(!locked && tabFrame.onChange) tabFrame.onChange();
}
}

View File

@ -90,7 +90,7 @@ string pBrowserWindow::directory(BrowserWindow::State& state) {
string name = (const char*)utf8_t(wname);
if(!name) return "";
name.transform("\\", "/");
if(name.endswith("/") == false) name.append("/");
if(name.endsWith("/") == false) name.append("/");
return name;
}

View File

@ -42,7 +42,7 @@ static lstring DropPaths(WPARAM wparam) {
if(DragQueryFile(dropList, n, buffer, length + 1)) {
string path = (const char*)utf8_t(buffer);
path.transform("\\", "/");
if(directory::exists(path) && !path.endswith("/")) path.append("/");
if(directory::exists(path) && !path.endsWith("/")) path.append("/");
paths.append(path);
}

View File

@ -4,9 +4,9 @@ void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin
modulo = glrModulo(node["modulo"].integer());
string w = node["width"].text(), h = node["height"].text();
if(w.endswith("%")) relativeWidth = real(w.rtrim<1>("%")) / 100.0;
if(w.endsWith("%")) relativeWidth = real(w.rtrim<1>("%")) / 100.0;
else absoluteWidth = decimal(w);
if(h.endswith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0;
if(h.endsWith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0;
else absoluteHeight = decimal(h);
if(node.name != "program") return;

View File

@ -41,7 +41,7 @@ ConfigurationSettings::ConfigurationSettings() {
server.append(server.password = "", "Password");
append(server, "Server");
library.append(library.selection = 0, "Selection");
library.append(library.selection = -1, "Selection");
library.append(library.showOnStartup = true, "ShowOnStartup");
append(library, "Library");

View File

@ -46,7 +46,7 @@ struct ConfigurationSettings : Configuration::Document {
} server;
struct Library : Configuration::Node {
unsigned selection;
signed selection;
bool showOnStartup;
} library;

View File

@ -85,7 +85,7 @@ Program::Program(int argc, char** argv) {
presentation->setVisible();
utility->resize();
if(config->library.showOnStartup) libraryManager->setVisible();
if(argc == 1 && config->library.showOnStartup) libraryManager->show();
video.set(Video::Handle, presentation->viewport.handle());
if(!video.cap(Video::Depth) || !video.set(Video::Depth, depth = 30u)) {

View File

@ -1,185 +1,251 @@
LibraryManager* libraryManager = nullptr;
LibraryBrowser::LibraryBrowser() {
LibraryBrowser::LibraryBrowser(Emulator::Interface& emulator) : emulator(emulator) {
setMargin(5);
informationType.setText({
"Title:\n",
"Revision:\n",
"Region:\n",
"Serial:"
});
for(auto& media : emulator.media) {
mediaMode.append(media.name);
}
unsigned height = Font::size(program->normalFont, " ").height;
append(folders, {~0, ~0}, 5);
append(informationLayout, {~0, Font::size(program->normalFont, " ").height * 4});
informationLayout.append(informationType, {0, ~0}, 5);
informationLayout.append(information, {~0, ~0});
append(informationLayout, {~0, 0});
informationLayout.append(informationType, {0, height * 2}, 5);
informationLayout.append(information, {~0, height * 2}, 5);
informationLayout.append(mediaMode, {0, 0});
folders.onActivate = {&LibraryBrowser::onActivate, this};
folders.onChange = {&LibraryBrowser::setInformation, this};
folders.onChange = {&LibraryBrowser::onChange, this};
mediaMode.onChange = {&LibraryBrowser::setMode, this};
}
void LibraryBrowser::onActivate() {
if(folders.selected() == false) return;
if(libraryManager->loadButton.enabled() == false) return;
unsigned selection = folders.selection();
string pathname = {this->pathname, folders.text(selection, 0), filterSuffix};
string pathname = {this->pathname, folders.text(selection, 0), typeSuffix};
libraryManager->loaded.append(folders.text(selection, 0));
libraryManager->setInformation(false);
if(libraryManager->slotLoad == false) {
libraryManager->setStatusText(folders.text(selection, 0));
utility->loadMedia(pathname);
} else {
libraryManager->setStatusText({libraryManager->statusText(), " + ", folders.text(selection, 0)});
libraryManager->setModal(false);
libraryManager->loadPathname = pathname;
libraryManager->setModal(false);
}
}
void LibraryBrowser::onChange() {
if(folders.selected() == false) return information.setText("");
string manifest = {pathname, folders.text(folders.selection(), 0), typeSuffix, "manifest.bml"};
auto document = Markup::Document(file::read(manifest));
information.setText({
document["information/title"].text(), "\n",
document["information/serial"].text()
});
}
void LibraryBrowser::refresh() {
folders.reset();
lstring pathnames = directory::ifolders(pathname, filterMask);
lstring pathnames = directory::ifolders(pathname, typeMask);
unsigned selection = 0;
for(auto& pathname : pathnames) {
folders.append(string{pathname}.rtrim<1>(filterSuffix));
folders.append(string{pathname}.rtrim<1>(typeSuffix));
folders.setImage(selection++, 0, {resource::game, sizeof resource::game});
}
folders.setSelection(0);
onChange();
}
void LibraryBrowser::setFilter(const string& filter) {
this->filter = filter;
filterMask = {"*.", filter};
filterSuffix = {".", filter, "/"};
}
void LibraryBrowser::setMode() {
auto& media = emulator.media[mediaMode.selection()];
void LibraryBrowser::setInformation() {
if(folders.selected() == false) {
information.setText("");
} else {
string manifest = {pathname, folders.text(folders.selection(), 0), filterSuffix, "manifest.bml"};
auto document = Markup::Document(file::read(manifest));
information.setText({
document["information/title"].text(), "\n",
document["information/revision"].text(), "\n",
document["information/region"].text(), "\n",
document["information/serial"].text(), "\n"
});
}
}
pathname = {utility->libraryPath(), media.name, "/"};
type = media.type;
typeMask = {"*.", type};
typeSuffix = {".", type, "/"};
void LibraryBrowser::setPath(const string& pathname) {
this->pathname = pathname;
refresh();
folders.setFocused();
libraryManager->synchronize();
}
LibraryManager::LibraryManager() {
setTitle("Game Library");
setStatusVisible();
setGeometry({128, 128, 960, 640});
windowManager->append(this, "LibraryManager");
layout.setMargin(5);
libraryFrame.append("Import Games");
importLayout.setMargin(5);
importInformation.setText({
LibraryImport::LibraryImport() {
setMargin(5);
information.setText({
"higan manages games in a library. To play a game, you must first import the game.\n"
"After doing so, the game will appear inside your library, and can then be loaded and played."
});
importButton.setText("Import Game ...");
libraryFrame.setLayout(0, importLayout);
append(information, {~0, 0}, 5);
append(importButton, {0, 0});
importButton.onActivate = {&LibraryImport::onImportActivate, this};
}
void LibraryImport::onImportActivate() {
if(program->ananke.open() == false) {
MessageWindow().setText("ananke must be installed to use this feature").warning();
return;
}
function<string ()> browse = program->ananke.sym("ananke_browse");
if(!browse) return;
string pathname = browse();
if(pathname.empty()) return;
MessageWindow().setText({"Successfully imported ", notdir(pathname.rtrim<1>("/"))}).information();
//after importing game, take user to the relevant game list to show the newly imported title
string type = extension(pathname);
for(signed bootable = 1; bootable >= 0; bootable--) {
unsigned selection = 0;
for(auto& browser : libraryManager->browsers) {
unsigned mode = 0;
for(auto& media : browser->emulator.media) {
if(type == media.type && media.bootable == bootable) {
browser->mediaMode.setSelection(mode);
libraryManager->libraryFrame.setSelection(selection);
libraryManager->onChange();
return;
}
mode++;
}
selection++;
}
}
}
LibraryManager::LibraryManager() {
setTitle("Game Library");
setGeometry({128, 128, 640, 680});
windowManager->append(this, "LibraryManager");
layout.setMargin(5);
bootstrap();
libraryFrame.setSelection(config->library.selection);
libraryFrame.append("Import");
libraryFrame.setLayout(browsers.size(), libraryImport);
loadButton.setText("Load");
unsigned height = Font::size(program->normalFont, " ").height;
append(layout);
layout.append(libraryFrame, {~0, ~0});
importLayout.append(importInformation, {0, 0}, 5);
importLayout.append(importButton, {0, 0});
layout.append(libraryFrame, {~0, ~0}, 5);
layout.append(informationLayout, {~0, 0});
informationLayout.append(information, {~0, height * 3}, 5);
informationLayout.append(skipButton, {80, 0}, 5);
informationLayout.append(loadButton, {80, 0});
onClose = [&] {
onClose = skipButton.onActivate = [&] {
setModal(false);
setVisible(false);
};
libraryFrame.onChange = [&] {
config->library.selection = libraryFrame.selection();
};
libraryFrame.onChange = {&LibraryManager::onChange, this};
importButton.onActivate = [&] {
if(program->ananke.open() == false) {
MessageWindow().setText("ananke must be installed to use this feature").warning();
return;
}
function<string ()> browse = program->ananke.sym("ananke_browse");
if(!browse) return;
string pathname = browse();
if(pathname.empty()) return;
MessageWindow().setText({"Successfully imported ", notdir(pathname.rtrim<1>("/"))}).information();
string type = extension(pathname);
unsigned selection = 1;
for(auto& browser : browsers) {
if(browser->filter == type) {
browser->refresh();
libraryFrame.setSelection(selection);
break;
}
selection++;
}
};
//initial config value of -1 defaults to import tab on first launch of higan
if(config->library.selection < 0) config->library.selection = browsers.size();
libraryFrame.setSelection(config->library.selection);
}
void LibraryManager::bootstrap() {
unsigned selection = 1;
string basepath = utility->libraryPath();
vector<string> names;
unsigned selection = 0;
for(auto& emulator : program->emulator) {
for(auto& media : emulator->media) {
if(media.bootable == false) continue;
if(names.find(media.name)) continue;
names.append(media.name);
LibraryBrowser* browser = new LibraryBrowser;
browser->setFilter(media.type);
browser->setPath({basepath, media.name, "/"});
libraryFrame.append(media.name);
libraryFrame.setLayout(selection++, *browser);
browsers.append(browser);
}
}
for(auto& emulator : program->emulator) {
for(auto& media : emulator->media) {
if(media.bootable == true) continue;
if(names.find(media.name)) continue;
names.append(media.name);
LibraryBrowser* browser = new LibraryBrowser;
browser->setFilter(media.type);
browser->setPath({basepath, media.name, "/"});
libraryFrame.append(media.name);
libraryFrame.setLayout(selection++, *browser);
browsers.append(browser);
}
LibraryBrowser* browser = new LibraryBrowser(*emulator);
libraryFrame.append(emulator->information.name);
libraryFrame.setLayout(selection++, *browser);
browsers.append(browser);
}
}
string LibraryManager::load(const string& type) {
setFocused();
unsigned selection = 1;
requestedLoadType = type;
unsigned selection = 0;
for(auto& browser : browsers) {
if(browser->filter == type) {
libraryFrame.setSelection(selection);
break;
unsigned mode = 0;
for(auto& media : browser->emulator.media) {
if(type == media.type && media.bootable == false) {
libraryFrame.setSelection(selection);
browser->mediaMode.setSelection(mode);
browser->setMode();
slotLoad = true;
loadPathname = "";
show();
setModal();
slotLoad = false;
browser->mediaMode.setSelection(0);
return loadPathname;
}
mode++;
}
selection++;
}
slotLoad = true;
loadPathname = "";
setModal(true);
slotLoad = false;
return loadPathname;
return ""; //should never occur
}
void LibraryManager::setVisible(bool visible) {
setStatusText("");
Window::setVisible(visible);
void LibraryManager::onChange() {
unsigned selection = libraryFrame.selection();
config->library.selection = selection;
if(selection < browsers.size()) {
browsers[selection]->setMode();
} else {
loadButton.setEnabled(false);
}
}
void LibraryManager::setInformation(bool load) {
string text;
if(loaded.size() == 0) {
text = {" \nPlease select a game to load ...\n "};
} else if(loaded.size() == 1 && load == false) {
text = {" \n", loaded[0], "\n "};
} else if(loaded.size() == 1 && load == true) {
text = {loaded[0], " \nPlease select a slot game to load ...\n "};
} else if(loaded.size() == 2 && load == false) {
text = {loaded[0], "\n", loaded[1], "\n "};
} else if(loaded.size() == 2 && load == true) {
text = {loaded[0], "\n", loaded[1], "\nPlease select a slot game to load ..."};
} else if(loaded.size() == 3) {
text = {loaded[0], "\n", loaded[1], "\n", loaded[2]};
}
information.setText(text);
}
void LibraryManager::show() {
if(slotLoad == false) {
loaded.reset();
requestedLoadType.reset();
skipButton.setText("Cancel");
} else {
skipButton.setText("Skip");
}
setInformation(true);
setVisible();
setFocused();
onChange();
}
void LibraryManager::synchronize() {
if(libraryFrame.selection() < browsers.size()) {
auto& emulator = browsers[libraryFrame.selection()]->emulator;
auto& media = emulator.media[browsers[libraryFrame.selection()]->mediaMode.selection()];
if(requestedLoadType.empty()) {
loadButton.setEnabled(media.bootable);
} else {
loadButton.setEnabled(requestedLoadType == media.type);
}
} else {
loadButton.setEnabled(false);
}
}

View File

@ -3,33 +3,49 @@ struct LibraryBrowser : VerticalLayout {
HorizontalLayout informationLayout;
Label informationType;
Label information;
ComboButton mediaMode;
LibraryBrowser();
LibraryBrowser(Emulator::Interface& emulator);
void onActivate();
void onChange();
void refresh();
void setFilter(const string& filter);
void setInformation();
void setPath(const string& pathname);
void setMode();
string filter;
string filterMask;
string filterSuffix;
Emulator::Interface& emulator;
string pathname;
string type;
string typeMask;
string typeSuffix;
};
struct LibraryImport : VerticalLayout {
Label information;
Button importButton;
LibraryImport();
void onImportActivate();
};
struct LibraryManager : Window {
VerticalLayout layout;
TabFrame libraryFrame;
VerticalLayout importLayout;
Label importInformation;
Button importButton;
vector<LibraryBrowser*> browsers;
LibraryImport libraryImport;
HorizontalLayout informationLayout;
Label information;
Button skipButton;
Button loadButton;
LibraryManager();
void bootstrap();
string load(const string& type);
void setVisible(bool visible = true);
void onChange();
void setInformation(bool load);
void show();
void synchronize();
lstring loaded;
string requestedLoadType;
bool slotLoad = false;
string loadPathname;
};

View File

@ -150,7 +150,7 @@ Presentation::Presentation() {
}
};
loadGame.onActivate = [&] { libraryManager->setVisible(); };
loadGame.onActivate = [&] { libraryManager->show(); };
shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); };
shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); };
shaderEmulation.onActivate = [&] { config->video.shader = "Display Emulation"; utility->updateShader(); };

View File

@ -8,15 +8,15 @@ void AbstractInput::bind() {
for(auto& mapping : list) {
Input::Type type;
if(mapping.endswith(".Up")) type = Input::Type::HatUp;
else if(mapping.endswith(".Down")) type = Input::Type::HatDown;
else if(mapping.endswith(".Left")) type = Input::Type::HatLeft;
else if(mapping.endswith(".Right")) type = Input::Type::HatRight;
else if(mapping.endswith(".Lo")) type = Input::Type::AxisLo;
else if(mapping.endswith(".Hi")) type = Input::Type::AxisHi;
else if(mapping.beginswith("JP") && mapping.find("Axis")) type = Input::Type::Axis;
else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Input::Type::MouseAxis;
else if(mapping.beginswith("MS")) type = Input::Type::MouseButton;
if(mapping.endsWith(".Up")) type = Input::Type::HatUp;
else if(mapping.endsWith(".Down")) type = Input::Type::HatDown;
else if(mapping.endsWith(".Left")) type = Input::Type::HatLeft;
else if(mapping.endsWith(".Right")) type = Input::Type::HatRight;
else if(mapping.endsWith(".Lo")) type = Input::Type::AxisLo;
else if(mapping.endsWith(".Hi")) type = Input::Type::AxisHi;
else if(mapping.beginsWith("JP") && mapping.find("Axis")) type = Input::Type::Axis;
else if(mapping.beginsWith("MS") && mapping.endsWith("axis")) type = Input::Type::MouseAxis;
else if(mapping.beginsWith("MS")) type = Input::Type::MouseButton;
else type = Input::Type::Button;
string decode = mapping;

View File

@ -10,7 +10,7 @@ void Utility::setInterface(Emulator::Interface* emulator) {
//load from command-line, etc
void Utility::loadMedia(string pathname) {
pathname.transform("\\", "/");
if(pathname.endswith("/")) pathname.rtrim("/");
if(pathname.endsWith("/")) pathname.rtrim("/");
if(!directory::exists(pathname)) return;
string type = extension(pathname);
@ -47,7 +47,7 @@ void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Medi
//request from emulation core to load non-volatile media folder
void Utility::loadRequest(unsigned id, string name, string type) {
string pathname = libraryManager->load(type); //browser->select({"Load ", name}, type);
string pathname = libraryManager->load(type);
if(pathname.empty()) return;
path(id) = pathname;
this->pathname.append(pathname);
@ -306,7 +306,7 @@ void Utility::showMessage(string message) {
string Utility::libraryPath() {
string path = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").transform("\\", "/");
if(path.empty()) path = {userpath(), "Emulation/"};
if(path.endswith("/") == false) path.append("/");
if(path.endsWith("/") == false) path.append("/");
return path;
}