diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 66276783..e57f0da6 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -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/"; diff --git a/nall/beat/archive.hpp b/nall/beat/archive.hpp index 0205f352..f8c6b6cf 100644 --- a/nall/beat/archive.hpp +++ b/nall/beat/archive.hpp @@ -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; diff --git a/nall/beat/multi.hpp b/nall/beat/multi.hpp index 13193c02..c99001c8 100644 --- a/nall/beat/multi.hpp +++ b/nall/beat/multi.hpp @@ -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); diff --git a/nall/directory.hpp b/nall/directory.hpp index b0c36f6d..d6068b37 100644 --- a/nall/directory.hpp +++ b/nall/directory.hpp @@ -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; diff --git a/nall/dl.hpp b/nall/dl.hpp index 91c7c073..cbd98d00 100644 --- a/nall/dl.hpp +++ b/nall/dl.hpp @@ -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; } diff --git a/nall/group.hpp b/nall/group.hpp index ba70185d..f4ae68a2 100644 --- a/nall/group.hpp +++ b/nall/group.hpp @@ -41,11 +41,11 @@ template struct group : protected vector { return false; } - struct iterator : protected vector::const_iterator { - T& operator*() const { return *vector::const_iterator::operator*(); } - bool operator!=(const iterator& source) const { return vector::const_iterator::operator!=(source); } - iterator& operator++() { vector::const_iterator::operator++(); return *this; } - iterator(const group& source, unsigned position) : vector::const_iterator(source, position) {} + struct iterator : protected vector::constIterator { + T& operator*() const { return *vector::constIterator::operator*(); } + bool operator!=(const iterator& source) const { return vector::constIterator::operator!=(source); } + iterator& operator++() { vector::constIterator::operator++(); return *this; } + iterator(const group& source, unsigned position) : vector::constIterator(source, position) {} }; const iterator begin() const { return iterator(*this, 0); } diff --git a/nall/http.hpp b/nall/http.hpp index 5fc4875f..55421867 100644 --- a/nall/http.hpp +++ b/nall/http.hpp @@ -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; } diff --git a/nall/image.hpp b/nall/image.hpp index f8221327..0bdd576c 100644 --- a/nall/image.hpp +++ b/nall/image.hpp @@ -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; diff --git a/nall/mosaic/context.hpp b/nall/mosaic/context.hpp index b48f343f..ee6b5d53 100644 --- a/nall/mosaic/context.hpp +++ b/nall/mosaic/context.hpp @@ -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(); } diff --git a/nall/mosaic/parser.hpp b/nall/mosaic/parser.hpp index 53301a66..93cfd5b0 100644 --- a/nall/mosaic/parser.hpp +++ b/nall/mosaic/parser.hpp @@ -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: diff --git a/nall/stream/auto.hpp b/nall/stream/auto.hpp index 635fd72a..0e4d8705 100644 --- a/nall/stream/auto.hpp +++ b/nall/stream/auto.hpp @@ -6,9 +6,9 @@ namespace nall { #define autostream(...) (*makestream(__VA_ARGS__)) inline std::unique_ptr makestream(const string& path) { - if(path.ibeginswith("http://")) return std::unique_ptr(new httpstream(path, 80)); - if(path.iendswith(".gz")) return std::unique_ptr(new gzipstream(filestream{path})); - if(path.iendswith(".zip")) return std::unique_ptr(new zipstream(filestream{path})); + if(path.ibeginsWith("http://")) return std::unique_ptr(new httpstream(path, 80)); + if(path.iendsWith(".gz")) return std::unique_ptr(new gzipstream(filestream{path})); + if(path.iendsWith(".zip")) return std::unique_ptr(new zipstream(filestream{path})); return std::unique_ptr(new mmapstream(path)); } diff --git a/nall/string/base.hpp b/nall/string/base.hpp index 0c6d93f9..284c2312 100644 --- a/nall/string/base.hpp +++ b/nall/string/base.hpp @@ -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; diff --git a/nall/string/eval/evaluator.hpp b/nall/string/eval/evaluator.hpp index 4632aa3a..49ac851e 100644 --- a/nall/string/eval/evaluator.hpp +++ b/nall/string/eval/evaluator.hpp @@ -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); } diff --git a/nall/string/markup/document.hpp b/nall/string/markup/document.hpp index 3204cb71..d9a8027e 100644 --- a/nall/string/markup/document.hpp +++ b/nall/string/markup/document.hpp @@ -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); } diff --git a/nall/string/markup/node.hpp b/nall/string/markup/node.hpp index 742ee307..ff7ea3d6 100644 --- a/nall/string/markup/node.hpp +++ b/nall/string/markup/node.hpp @@ -131,8 +131,8 @@ struct Node { vector::iterator begin() { return children.begin(); } vector::iterator end() { return children.end(); } - const vector::const_iterator begin() const { return children.begin(); } - const vector::const_iterator end() const { return children.end(); } + const vector::constIterator begin() const { return children.begin(); } + const vector::constIterator end() const { return children.end(); } Node() : attribute(false), level(0) {} diff --git a/nall/string/platform.hpp b/nall/string/platform.hpp index 76429e72..30b424f6 100644 --- a/nall/string/platform.hpp +++ b/nall/string/platform.hpp @@ -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; } diff --git a/nall/string/wrapper.hpp b/nall/string/wrapper.hpp index 6551b38d..0422ecdc 100644 --- a/nall/string/wrapper.hpp +++ b/nall/string/wrapper.hpp @@ -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; } diff --git a/nall/vector.hpp b/nall/vector.hpp index 5ac99753..7ccb53db 100644 --- a/nall/vector.hpp +++ b/nall/vector.hpp @@ -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) { diff --git a/phoenix/cocoa/utility.cpp b/phoenix/cocoa/utility.cpp index 22f8b26e..25cb06cb 100644 --- a/phoenix/cocoa/utility.cpp +++ b/phoenix/cocoa/utility.cpp @@ -33,7 +33,7 @@ lstring DropPaths(id 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); } } diff --git a/phoenix/gtk/browser-window.cpp b/phoenix/gtk/browser-window.cpp index d5b2fb8c..e2c9b5c5 100644 --- a/phoenix/gtk/browser-window.cpp +++ b/phoenix/gtk/browser-window.cpp @@ -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; } diff --git a/phoenix/gtk/utility.cpp b/phoenix/gtk/utility.cpp index 64f08dcf..1645ab76 100644 --- a/phoenix/gtk/utility.cpp +++ b/phoenix/gtk/utility.cpp @@ -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); } diff --git a/phoenix/gtk/widget/tab-frame.cpp b/phoenix/gtk/widget/tab-frame.cpp index ab9ef66b..0adb7100 100644 --- a/phoenix/gtk/widget/tab-frame.cpp +++ b/phoenix/gtk/widget/tab-frame.cpp @@ -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) { diff --git a/phoenix/qt/browser-window.cpp b/phoenix/qt/browser-window.cpp index bba3d624..0b25cf6b 100644 --- a/phoenix/qt/browser-window.cpp +++ b/phoenix/qt/browser-window.cpp @@ -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; } diff --git a/phoenix/qt/platform.moc b/phoenix/qt/platform.moc index 1ec403eb..a5fe50e5 100644 --- a/phoenix/qt/platform.moc +++ b/phoenix/qt/platform.moc @@ -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! diff --git a/phoenix/qt/utility.cpp b/phoenix/qt/utility.cpp index bea91884..5e4f34c8 100644 --- a/phoenix/qt/utility.cpp +++ b/phoenix/qt/utility.cpp @@ -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); } diff --git a/phoenix/qt/widget/tab-frame.cpp b/phoenix/qt/widget/tab-frame.cpp index 4eb5e289..6fb9396d 100644 --- a/phoenix/qt/widget/tab-frame.cpp +++ b/phoenix/qt/widget/tab-frame.cpp @@ -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(); } } diff --git a/phoenix/windows/browser-window.cpp b/phoenix/windows/browser-window.cpp index fd60d1ff..146c4c8e 100644 --- a/phoenix/windows/browser-window.cpp +++ b/phoenix/windows/browser-window.cpp @@ -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; } diff --git a/phoenix/windows/utility.cpp b/phoenix/windows/utility.cpp index 051c9de9..79ee7bd6 100644 --- a/phoenix/windows/utility.cpp +++ b/phoenix/windows/utility.cpp @@ -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); } diff --git a/ruby/video/opengl/program.hpp b/ruby/video/opengl/program.hpp index 53c89c68..87e1b4cf 100644 --- a/ruby/video/opengl/program.hpp +++ b/ruby/video/opengl/program.hpp @@ -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; diff --git a/target-ethos/configuration/configuration.cpp b/target-ethos/configuration/configuration.cpp index 48bd32a3..cfd0eece 100644 --- a/target-ethos/configuration/configuration.cpp +++ b/target-ethos/configuration/configuration.cpp @@ -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"); diff --git a/target-ethos/configuration/configuration.hpp b/target-ethos/configuration/configuration.hpp index 252f923c..b2326f9e 100644 --- a/target-ethos/configuration/configuration.hpp +++ b/target-ethos/configuration/configuration.hpp @@ -46,7 +46,7 @@ struct ConfigurationSettings : Configuration::Document { } server; struct Library : Configuration::Node { - unsigned selection; + signed selection; bool showOnStartup; } library; diff --git a/target-ethos/ethos.cpp b/target-ethos/ethos.cpp index 833861ee..fc34f1e0 100644 --- a/target-ethos/ethos.cpp +++ b/target-ethos/ethos.cpp @@ -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)) { diff --git a/target-ethos/general/library.cpp b/target-ethos/general/library.cpp index 5726cc01..af08ef47 100644 --- a/target-ethos/general/library.cpp +++ b/target-ethos/general/library.cpp @@ -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 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 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 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); + } } diff --git a/target-ethos/general/library.hpp b/target-ethos/general/library.hpp index e4a998f8..0a9d9348 100644 --- a/target-ethos/general/library.hpp +++ b/target-ethos/general/library.hpp @@ -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 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; }; diff --git a/target-ethos/general/presentation.cpp b/target-ethos/general/presentation.cpp index 6744b44a..b5263f9a 100644 --- a/target-ethos/general/presentation.cpp +++ b/target-ethos/general/presentation.cpp @@ -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(); }; diff --git a/target-ethos/input/input.cpp b/target-ethos/input/input.cpp index 39a1ec1a..e8d05ee2 100644 --- a/target-ethos/input/input.cpp +++ b/target-ethos/input/input.cpp @@ -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; diff --git a/target-ethos/utility/utility.cpp b/target-ethos/utility/utility.cpp index 2c449ea0..9041582d 100644 --- a/target-ethos/utility/utility.cpp +++ b/target-ethos/utility/utility.cpp @@ -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; }