mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-27 02:50:32 +00:00
Update to v093r05 release.
byuu says: Library concept has been refined as per the general forum discussion.
This commit is contained in:
parent
b4f18c3b47
commit
ed4e87f65e
@ -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/";
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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); }
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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!
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
||||
|
@ -46,7 +46,7 @@ struct ConfigurationSettings : Configuration::Document {
|
||||
} server;
|
||||
|
||||
struct Library : Configuration::Node {
|
||||
unsigned selection;
|
||||
signed selection;
|
||||
bool showOnStartup;
|
||||
} library;
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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(); };
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user