mirror of https://github.com/bsnes-emu/bsnes.git
Update to v085r02 release.
byuu says: Fixed NSS XML crashing issue. Improved folder-loading support. NES can now load game.fc/game.fc, or game.fc/game.prg+game.chr. Both types should have no iNES header at all. And both types require an XML file (until we have a built-in database.)
This commit is contained in:
parent
cc518dcc3c
commit
e4e50308d2
|
@ -45,8 +45,8 @@ void Cartridge::load(System::Revision revision, const string &markup, const uint
|
|||
info.rtc = document["cartridge"]["rtc"].data == "true";
|
||||
info.rumble = document["cartridge"]["rumble"].data == "true";
|
||||
|
||||
info.romsize = hex(document["cartridge"]["rom"]["size"].data);
|
||||
info.ramsize = hex(document["cartridge"]["ram"]["size"].data);
|
||||
info.romsize = numeral(document["cartridge"]["rom"]["size"].data);
|
||||
info.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
|
||||
info.battery = document["cartridge"]["ram"]["battery"].data == "true";
|
||||
|
||||
switch(info.mapper) { default:
|
||||
|
|
|
@ -2,17 +2,15 @@
|
|||
#define NALL_ANY_HPP
|
||||
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <nall/static.hpp>
|
||||
#include <nall/type_traits.hpp>
|
||||
|
||||
namespace nall {
|
||||
class any {
|
||||
public:
|
||||
struct any {
|
||||
bool empty() const { return container; }
|
||||
const std::type_info& type() const { return container ? container->type() : typeid(void); }
|
||||
|
||||
template<typename T> any& operator=(const T& value_) {
|
||||
typedef typename static_if<
|
||||
typedef typename type_if<
|
||||
std::is_array<T>::value,
|
||||
typename std::remove_extent<typename std::add_const<T>::type>::type*,
|
||||
T
|
||||
|
|
|
@ -4,143 +4,286 @@
|
|||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/type_traits.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
//dynamic vector array
|
||||
//neither constructor nor destructor is ever invoked;
|
||||
//thus, this should only be used for POD objects.
|
||||
template<typename T> class array {
|
||||
protected:
|
||||
T *pool;
|
||||
unsigned poolsize, buffersize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return buffersize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
template<typename T, typename Enable = void> struct array;
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
buffersize = 0;
|
||||
//non-reference array
|
||||
//===================
|
||||
|
||||
template<typename T> struct array<T, typename std::enable_if<!std::is_reference<T>::value>::type> {
|
||||
struct exception_out_of_bounds{};
|
||||
|
||||
protected:
|
||||
T *pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
objectsize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
if(newsize == poolsize) return;
|
||||
|
||||
pool = (T*)realloc(pool, newsize * sizeof(T));
|
||||
poolsize = newsize;
|
||||
objectsize = min(objectsize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
|
||||
objectsize = newsize;
|
||||
}
|
||||
|
||||
T* get(unsigned minsize = 0) {
|
||||
if(minsize > objectsize) resize(minsize);
|
||||
return pool;
|
||||
}
|
||||
|
||||
void append(const T data) {
|
||||
operator()(objectsize) = data;
|
||||
}
|
||||
|
||||
void append(const T data[], unsigned length) {
|
||||
for(unsigned n = 0; n < length; n++) operator()(objectsize) = data[n];
|
||||
}
|
||||
|
||||
void remove() {
|
||||
if(size > 0) resize(size - 1); //remove last element only
|
||||
}
|
||||
|
||||
void remove(unsigned index, unsigned count = 1) {
|
||||
for(unsigned i = index; count + i < objectsize; i++) {
|
||||
pool[i] = pool[count + i];
|
||||
}
|
||||
if(count + index >= objectsize) resize(index); //every element >= index was removed
|
||||
else resize(objectsize - count);
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
if(newsize == poolsize) return;
|
||||
void sort() {
|
||||
nall::sort(pool, objectsize);
|
||||
}
|
||||
|
||||
pool = (T*)realloc(pool, newsize * sizeof(T));
|
||||
poolsize = newsize;
|
||||
buffersize = min(buffersize, newsize);
|
||||
template<typename Comparator> void sort(const Comparator &lessthan) {
|
||||
nall::sort(pool, objectsize, lessthan);
|
||||
}
|
||||
|
||||
optional<unsigned> find(const T data) {
|
||||
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
|
||||
return { false, 0u };
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(pool, 0, objectsize * sizeof(T));
|
||||
}
|
||||
|
||||
array() : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
}
|
||||
|
||||
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
for(auto &data : list) append(data);
|
||||
}
|
||||
|
||||
~array() {
|
||||
reset();
|
||||
}
|
||||
|
||||
//copy
|
||||
array& operator=(const array &source) {
|
||||
if(pool) free(pool);
|
||||
objectsize = source.objectsize;
|
||||
poolsize = source.poolsize;
|
||||
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
|
||||
memcpy(pool, source.pool, sizeof(T) * objectsize); //... but only copy used pool objects
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(const array &source) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
//move
|
||||
array& operator=(array &&source) {
|
||||
if(pool) free(pool);
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
objectsize = source.objectsize;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(array &&source) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
//access
|
||||
inline T& operator[](unsigned position) {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline const T& operator[](unsigned position) const {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline T& operator()(unsigned position) {
|
||||
if(position >= objectsize) resize(position + 1);
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline const T& operator()(unsigned position, const T& data) {
|
||||
if(position >= objectsize) return data;
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
//iteration
|
||||
T* begin() { return &pool[0]; }
|
||||
T* end() { return &pool[objectsize]; }
|
||||
const T* begin() const { return &pool[0]; }
|
||||
const T* end() const { return &pool[objectsize]; }
|
||||
};
|
||||
|
||||
//reference array
|
||||
//===============
|
||||
|
||||
template<typename TR> struct array<TR, typename std::enable_if<std::is_reference<TR>::value>::type> {
|
||||
struct exception_out_of_bounds{};
|
||||
|
||||
protected:
|
||||
typedef typename std::remove_reference<TR>::type T;
|
||||
T **pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
objectsize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
if(newsize == poolsize) return;
|
||||
|
||||
pool = (T**)realloc(pool, sizeof(T*) * newsize);
|
||||
poolsize = newsize;
|
||||
objectsize = min(objectsize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(bit::round(newsize));
|
||||
objectsize = newsize;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool append(T& data, Args&&... args) {
|
||||
bool result = append(data);
|
||||
append(std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool append(T& data) {
|
||||
if(find(data)) return false;
|
||||
unsigned offset = objectsize++;
|
||||
if(offset >= poolsize) resize(offset + 1);
|
||||
pool[offset] = &data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(T& data) {
|
||||
if(auto position = find(data)) {
|
||||
for(signed i = position(); i < objectsize - 1; i++) pool[i] = pool[i + 1];
|
||||
resize(objectsize - 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
|
||||
buffersize = newsize;
|
||||
}
|
||||
optional<unsigned> find(const T& data) {
|
||||
for(unsigned n = 0; n < objectsize; n++) if(pool[n] == &data) return { true, n };
|
||||
return { false, 0u };
|
||||
}
|
||||
|
||||
T* get(unsigned minsize = 0) {
|
||||
if(minsize > buffersize) resize(minsize);
|
||||
if(minsize > buffersize) throw "array[] out of bounds";
|
||||
return pool;
|
||||
}
|
||||
template<typename... Args> array(Args&&... args) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
construct(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void append(const T data) {
|
||||
operator[](buffersize) = data;
|
||||
}
|
||||
~array() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void append(const T data[], unsigned length) {
|
||||
for(unsigned n = 0; n < length; n++) operator[](buffersize) = data[n];
|
||||
}
|
||||
array& operator=(const array &source) {
|
||||
if(pool) free(pool);
|
||||
objectsize = source.objectsize;
|
||||
poolsize = source.poolsize;
|
||||
pool = (T**)malloc(sizeof(T*) * poolsize);
|
||||
memcpy(pool, source.pool, sizeof(T*) * objectsize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove() {
|
||||
if(size > 0) resize(size - 1); //remove last element only
|
||||
}
|
||||
array& operator=(const array &&source) {
|
||||
if(pool) free(pool);
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
objectsize = source.objectsize;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove(unsigned index, unsigned count = 1) {
|
||||
for(unsigned i = index; count + i < buffersize; i++) {
|
||||
pool[i] = pool[count + i];
|
||||
}
|
||||
if(count + index >= buffersize) resize(index); //every element >= index was removed
|
||||
else resize(buffersize - count);
|
||||
}
|
||||
T& operator[](unsigned position) const {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
return *pool[position];
|
||||
}
|
||||
|
||||
optional<unsigned> find(const T data) {
|
||||
for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return { true, i };
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(pool, 0, buffersize * sizeof(T));
|
||||
}
|
||||
|
||||
array() : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
}
|
||||
|
||||
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
||||
}
|
||||
|
||||
~array() {
|
||||
reset();
|
||||
}
|
||||
|
||||
//copy
|
||||
array& operator=(const array &source) {
|
||||
if(pool) free(pool);
|
||||
buffersize = source.buffersize;
|
||||
poolsize = source.poolsize;
|
||||
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
|
||||
memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(const array &source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
//move
|
||||
array& operator=(array &&source) {
|
||||
if(pool) free(pool);
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
buffersize = source.buffersize;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(array &&source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
//access
|
||||
inline T& operator[](unsigned position) {
|
||||
if(position >= buffersize) resize(position + 1);
|
||||
if(position >= buffersize) throw "array[] out of bounds";
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline const T& operator[](unsigned position) const {
|
||||
if(position >= buffersize) throw "array[] out of bounds";
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline const T& operator()(unsigned position, const T& data) {
|
||||
if(position >= buffersize) return data;
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
//iteration
|
||||
T* begin() { return &pool[0]; }
|
||||
T* end() { return &pool[buffersize]; }
|
||||
const T* begin() const { return &pool[0]; }
|
||||
const T* end() const { return &pool[buffersize]; }
|
||||
//iteration
|
||||
struct iterator {
|
||||
bool operator!=(const iterator &source) const { return position != source.position; }
|
||||
T& operator*() { return source.operator[](position); }
|
||||
iterator& operator++() { position++; return *this; }
|
||||
iterator(const array &source, unsigned position) : source(source), position(position) {}
|
||||
private:
|
||||
const array &source;
|
||||
unsigned position;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(*this, 0); }
|
||||
iterator end() { return iterator(*this, objectsize); }
|
||||
const iterator begin() const { return iterator(*this, 0); }
|
||||
const iterator end() const { return iterator(*this, objectsize); }
|
||||
|
||||
private:
|
||||
void construct() {
|
||||
}
|
||||
|
||||
void construct(const array& source) { operator=(source); }
|
||||
void construct(const array&& source) { operator=(std::move(source)); }
|
||||
|
||||
template<typename... Args> void construct(T& data, Args&&... args) {
|
||||
append(data);
|
||||
construct(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -79,6 +79,19 @@ constexpr inline uintmax_t hex(const char *s) {
|
|||
);
|
||||
}
|
||||
|
||||
constexpr inline intmax_t numeral(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||
*s == '0' ? octal_(s + 1) :
|
||||
*s == '+' ? +decimal_(s + 1) :
|
||||
*s == '-' ? -decimal_(s + 1) :
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
inline double fp(const char *s) {
|
||||
return atof(s);
|
||||
}
|
||||
|
|
|
@ -53,21 +53,18 @@ namespace nall {
|
|||
}
|
||||
}
|
||||
};
|
||||
linear_vector<item_t> list;
|
||||
vector<item_t> list;
|
||||
|
||||
template<typename T>
|
||||
inline void append(T &data, const char *name, const char *desc = "") {
|
||||
unsigned n = list.size();
|
||||
list[n].data = (uintptr_t)&data;
|
||||
list[n].name = name;
|
||||
list[n].desc = desc;
|
||||
|
||||
if(configuration_traits::is_boolean<T>::value) list[n].type = boolean_t;
|
||||
else if(configuration_traits::is_signed<T>::value) list[n].type = signed_t;
|
||||
else if(configuration_traits::is_unsigned<T>::value) list[n].type = unsigned_t;
|
||||
else if(configuration_traits::is_double<T>::value) list[n].type = double_t;
|
||||
else if(configuration_traits::is_string<T>::value) list[n].type = string_t;
|
||||
else list[n].type = unknown_t;
|
||||
item_t item = { (uintptr_t)&data, name, desc };
|
||||
if(configuration_traits::is_boolean<T>::value) item.type = boolean_t;
|
||||
else if(configuration_traits::is_signed<T>::value) item.type = signed_t;
|
||||
else if(configuration_traits::is_unsigned<T>::value) item.type = unsigned_t;
|
||||
else if(configuration_traits::is_double<T>::value) item.type = double_t;
|
||||
else if(configuration_traits::is_string<T>::value) item.type = string_t;
|
||||
else item.type = unknown_t;
|
||||
list.append(item);
|
||||
}
|
||||
|
||||
//deprecated
|
||||
|
|
|
@ -56,7 +56,7 @@ struct directory {
|
|||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
if(list.size() > 0) list.sort();
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ struct directory {
|
|||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
if(list.size() > 0) list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ struct directory {
|
|||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
if(list.size() > 0) list.sort();
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ struct directory {
|
|||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
if(list.size() > 0) list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ struct image {
|
|||
inline image(const image &source);
|
||||
inline image(image &&source);
|
||||
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||
inline image();
|
||||
inline ~image();
|
||||
|
||||
inline uint64_t read(const uint8_t *data) const;
|
||||
|
@ -142,6 +143,20 @@ image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask,
|
|||
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||
}
|
||||
|
||||
image::image() : data(nullptr) {
|
||||
width = 0, height = 0, pitch = 0;
|
||||
|
||||
this->endian = 0;
|
||||
this->depth = 32;
|
||||
this->stride = 4;
|
||||
|
||||
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
|
||||
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||
}
|
||||
|
||||
image::~image() {
|
||||
free();
|
||||
}
|
||||
|
|
|
@ -12,51 +12,74 @@ struct map {
|
|||
RHS data;
|
||||
};
|
||||
|
||||
void reset() {
|
||||
inline void reset() {
|
||||
list.reset();
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
inline unsigned size() const {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
//O(n)
|
||||
void append(const LHS &name, const RHS &data) {
|
||||
//O(log n) find
|
||||
inline optional<unsigned> find(const LHS &name) const {
|
||||
signed first = 0, last = size() - 1;
|
||||
while(first <= last) {
|
||||
signed middle = (first + last) / 2;
|
||||
if(name < list[middle].name) last = middle - 1; //search lower half
|
||||
else if(list[middle].name < name) first = middle + 1; //search upper half
|
||||
else return { true, middle }; //match found
|
||||
}
|
||||
return { false, 0u };
|
||||
}
|
||||
|
||||
//O(n) insert + O(log n) find
|
||||
inline RHS& insert(const LHS &name, const RHS &data) {
|
||||
if(auto position = find(name)) {
|
||||
list[position()].data = data;
|
||||
return list[position()].data;
|
||||
}
|
||||
signed offset = size();
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(name < list[n].name) { offset = n; break; }
|
||||
}
|
||||
list.insert(offset, { name, data });
|
||||
return list[offset].data;
|
||||
}
|
||||
|
||||
//O(log n)
|
||||
RHS& operator[](const LHS &name) {
|
||||
signed first = 0, last = size() - 1;
|
||||
while(first <= last) {
|
||||
signed middle = (first + last) / 2;
|
||||
if(name < list[middle].name) last = middle - 1; //search lower half
|
||||
else if(name > list[middle].name) first = middle + 1; //search upper half
|
||||
else return list[middle].data; //match found
|
||||
}
|
||||
//O(log n) find
|
||||
inline void modify(const LHS &name, const RHS &data) {
|
||||
if(auto position = find(name)) list[position()].data = data;
|
||||
}
|
||||
|
||||
//O(n) remove + O(log n) find
|
||||
inline void remove(const LHS &name) {
|
||||
if(auto position = find(name)) list.remove(position());
|
||||
}
|
||||
|
||||
//O(log n) find
|
||||
inline RHS& operator[](const LHS &name) {
|
||||
if(auto position = find(name)) return list[position()].data;
|
||||
throw;
|
||||
}
|
||||
|
||||
//O(log n) nothrow
|
||||
const RHS& operator()(const LHS &name, const RHS &data) {
|
||||
signed first = 0, last = size() - 1;
|
||||
while(first <= last) {
|
||||
signed middle = (first + last) / 2;
|
||||
if(name < list[middle].name) last = middle - 1; //search lower half
|
||||
else if(name > list[middle].name) first = middle + 1; //search upper half
|
||||
else return list[middle].data; //match found
|
||||
}
|
||||
inline const RHS& operator[](const LHS &name) const {
|
||||
if(auto position = find(name)) return list[position()].data;
|
||||
throw;
|
||||
}
|
||||
|
||||
inline RHS& operator()(const LHS &name) {
|
||||
return insert(name, RHS());
|
||||
}
|
||||
|
||||
inline const RHS& operator()(const LHS &name, const RHS &data) const {
|
||||
if(auto position = find(name)) return list[position()].data;
|
||||
return data;
|
||||
}
|
||||
|
||||
pair* begin() { return list.begin(); }
|
||||
pair* end() { return list.end(); }
|
||||
const pair* begin() const { return list.begin(); }
|
||||
const pair* end() const { return list.end(); }
|
||||
inline pair* begin() { return list.begin(); }
|
||||
inline pair* end() { return list.end(); }
|
||||
inline const pair* begin() const { return list.begin(); }
|
||||
inline const pair* end() const { return list.end(); }
|
||||
|
||||
protected:
|
||||
vector<pair> list;
|
||||
|
@ -64,22 +87,28 @@ protected:
|
|||
|
||||
template<typename LHS, typename RHS>
|
||||
struct bidirectional_map {
|
||||
map<LHS, RHS> lhs;
|
||||
map<RHS, LHS> rhs;
|
||||
const map<LHS, RHS> &lhs;
|
||||
const map<RHS, LHS> &rhs;
|
||||
|
||||
void reset() {
|
||||
lhs.reset();
|
||||
rhs.reset();
|
||||
inline void reset() {
|
||||
llist.reset();
|
||||
rlist.reset();
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return lhs.size();
|
||||
inline unsigned size() const {
|
||||
return llist.size();
|
||||
}
|
||||
|
||||
void append(const LHS &ldata, const RHS &rdata) {
|
||||
lhs.append(ldata, rdata);
|
||||
rhs.append(rdata, ldata);
|
||||
inline void insert(const LHS &ldata, const RHS &rdata) {
|
||||
llist.insert(ldata, rdata);
|
||||
rlist.insert(rdata, ldata);
|
||||
}
|
||||
|
||||
inline bidirectional_map() : lhs(llist), rhs(rlist) {}
|
||||
|
||||
protected:
|
||||
map<LHS, RHS> llist;
|
||||
map<RHS, LHS> rlist;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
#ifndef NALL_RESOURCE_HPP
|
||||
#define NALL_RESOURCE_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/zip.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct resource {
|
||||
//create resource with "zip -9 resource.zip resource"
|
||||
static bool encode(const char *outputFilename, const char *inputFilename) {
|
||||
file fp;
|
||||
if(fp.open(inputFilename, file::mode::read) == false) return false;
|
||||
unsigned size = fp.size();
|
||||
uint8_t *data = new uint8_t[size];
|
||||
fp.read(data, size);
|
||||
fp.close();
|
||||
|
||||
fp.open(outputFilename, file::mode::write);
|
||||
fp.print("static const uint8_t data[", size, "] = {\n");
|
||||
uint8_t *p = data;
|
||||
while(size) {
|
||||
fp.print(" ");
|
||||
for(unsigned n = 0; n < 32 && size; n++, size--) {
|
||||
fp.print((unsigned)*p++, ",");
|
||||
}
|
||||
fp.print("\n");
|
||||
}
|
||||
fp.print("};\n");
|
||||
fp.close();
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
//extract first file from ZIP archive
|
||||
bool decode(const uint8_t *cdata, unsigned csize) {
|
||||
if(data) delete[] data;
|
||||
|
||||
zip archive;
|
||||
if(archive.open(cdata, csize) == false) return false;
|
||||
if(archive.file.size() == 0) return false;
|
||||
bool result = archive.extract(archive.file[0], data, size);
|
||||
archive.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
resource() : data(0), size(0) {
|
||||
}
|
||||
|
||||
~resource() {
|
||||
if(data) delete[] data;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -11,53 +11,67 @@
|
|||
//stack: O(log n)
|
||||
//stable?: yes
|
||||
|
||||
//notes:
|
||||
//there are two primary reasons for choosing merge sort
|
||||
//over the (usually) faster quick sort*:
|
||||
//1: it is a stable sort.
|
||||
//2: it lacks O(n^2) worst-case overhead.
|
||||
//(* which is also O(n log n) in the average case.)
|
||||
//note: merge sort was chosen over quick sort, because:
|
||||
//* it is a stable sort
|
||||
//* it lacks O(n^2) worst-case overhead
|
||||
|
||||
#define NALL_SORT_INSERTION
|
||||
//#define NALL_SORT_SELECTION
|
||||
|
||||
namespace nall {
|
||||
template<typename T>
|
||||
void sort(T list[], unsigned length) {
|
||||
if(length <= 1) return; //nothing to sort
|
||||
template<typename T, typename Comparator>
|
||||
void sort(T list[], unsigned size, const Comparator &lessthan) {
|
||||
if(size <= 1) return; //nothing to sort
|
||||
|
||||
//use insertion sort to quickly sort smaller blocks
|
||||
if(length < 64) {
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
if(size < 64) {
|
||||
#if defined(NALL_SORT_INSERTION)
|
||||
for(signed i = 1, j; i < size; i++) {
|
||||
T copy = std::move(list[i]);
|
||||
for(j = i - 1; j >= 0; j--) {
|
||||
if(lessthan(list[j], copy)) break;
|
||||
list[j + 1] = std::move(list[j]);
|
||||
}
|
||||
list[j + 1] = std::move(copy);
|
||||
}
|
||||
#elif defined(NALL_SORT_SELECTION)
|
||||
for(unsigned i = 0; i < size; i++) {
|
||||
unsigned min = i;
|
||||
for(unsigned j = i + 1; j < length; j++) {
|
||||
if(list[j] < list[min]) min = j;
|
||||
for(unsigned j = i + 1; j < size; j++) {
|
||||
if(lessthan(list[j], list[min])) min = j;
|
||||
}
|
||||
if(min != i) std::swap(list[i], list[min]);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
//split list in half and recursively sort both
|
||||
unsigned middle = length / 2;
|
||||
sort(list, middle);
|
||||
sort(list + middle, length - middle);
|
||||
unsigned middle = size / 2;
|
||||
sort(list, middle, lessthan);
|
||||
sort(list + middle, size - middle, lessthan);
|
||||
|
||||
//left and right are sorted here; perform merge sort
|
||||
T *buffer = new T[length];
|
||||
unsigned offset = 0;
|
||||
unsigned left = 0;
|
||||
unsigned right = middle;
|
||||
while(left < middle && right < length) {
|
||||
if(list[left] < list[right]) {
|
||||
buffer[offset++] = list[left++];
|
||||
T *buffer = new T[size];
|
||||
unsigned offset = 0, left = 0, right = middle;
|
||||
while(left < middle && right < size) {
|
||||
if(lessthan(list[left], list[right])) {
|
||||
buffer[offset++] = std::move(list[left++]);
|
||||
} else {
|
||||
buffer[offset++] = list[right++];
|
||||
buffer[offset++] = std::move(list[right++]);
|
||||
}
|
||||
}
|
||||
while(left < middle) buffer[offset++] = list[left++];
|
||||
while(right < length) buffer[offset++] = list[right++];
|
||||
while(left < middle) buffer[offset++] = std::move(list[left++]);
|
||||
while(right < size) buffer[offset++] = std::move(list[right++]);
|
||||
|
||||
for(unsigned i = 0; i < length; i++) list[i] = buffer[i];
|
||||
for(unsigned i = 0; i < size; i++) list[i] = std::move(buffer[i]);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void sort(T list[], unsigned size) {
|
||||
return sort(list, size, [](const T &l, const T &r) { return l < r; });
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef NALL_STACK_HPP
|
||||
#define NALL_STACK_HPP
|
||||
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<typename T> struct stack : public linear_vector<T> {
|
||||
void push(const T &value) {
|
||||
linear_vector<T>::append(value);
|
||||
}
|
||||
|
||||
T pull() {
|
||||
if(linear_vector<T>::size() == 0) throw;
|
||||
T value = linear_vector<T>::operator[](linear_vector<T>::size() - 1);
|
||||
linear_vector<T>::remove(linear_vector<T>::size() - 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
T& operator()() {
|
||||
if(linear_vector<T>::size() == 0) throw;
|
||||
return linear_vector<T>::operator[](linear_vector<T>::size() - 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef NALL_STATIC_HPP
|
||||
#define NALL_STATIC_HPP
|
||||
|
||||
namespace nall {
|
||||
template<bool C, typename T, typename F> struct static_if { typedef T type; };
|
||||
template<typename T, typename F> struct static_if<false, T, F> { typedef F type; };
|
||||
template<typename C, typename T, typename F> struct mp_static_if { typedef typename static_if<C::type, T, F>::type type; };
|
||||
|
||||
template<bool A, bool B> struct static_and { enum { value = false }; };
|
||||
template<> struct static_and<true, true> { enum { value = true }; };
|
||||
template<typename A, typename B> struct mp_static_and { enum { value = static_and<A::value, B::value>::value }; };
|
||||
|
||||
template<bool A, bool B> struct static_or { enum { value = false }; };
|
||||
template<> struct static_or<false, true> { enum { value = true }; };
|
||||
template<> struct static_or<true, false> { enum { value = true }; };
|
||||
template<> struct static_or<true, true> { enum { value = true }; };
|
||||
template<typename A, typename B> struct mp_static_or { enum { value = static_or<A::value, B::value>::value }; };
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef NALL_STDINT_HPP
|
||||
#define NALL_STDINT_HPP
|
||||
|
||||
#include <nall/static.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include <nall/string/wildcard.hpp>
|
||||
#include <nall/string/wrapper.hpp>
|
||||
#include <nall/string/xml.hpp>
|
||||
#include <nall/string/xml-legacy.hpp>
|
||||
#undef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,7 +20,7 @@ struct Node {
|
|||
cstring value;
|
||||
|
||||
private:
|
||||
linear_vector<Node> children;
|
||||
vector<Node> children;
|
||||
|
||||
inline bool valid(char p) const { //A-Za-z0-9-.
|
||||
return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef NALL_STATIC_HPP
|
||||
#define NALL_STATIC_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename T> class has_default_constructor {
|
||||
template<signed> class receive_size{};
|
||||
template<typename U> static signed sfinae(receive_size<sizeof U()>*);
|
||||
template<typename U> static char sfinae(...);
|
||||
|
||||
public:
|
||||
enum : bool { value = sizeof(sfinae<T>(0)) == sizeof(signed) };
|
||||
};
|
||||
|
||||
template<bool C, typename T = bool> struct enable_if { typedef T type; };
|
||||
template<typename T> struct enable_if<false, T> {};
|
||||
|
||||
template<bool C, typename T, typename F> struct type_if { typedef T type; };
|
||||
template<typename T, typename F> struct type_if<false, T, F> { typedef F type; };
|
||||
|
||||
template<bool A, bool B> struct static_and { enum { value = false }; };
|
||||
template<> struct static_and<true, true> { enum { value = true }; };
|
||||
|
||||
template<bool A, bool B> struct static_or { enum { value = false }; };
|
||||
template<> struct static_or<false, true> { enum { value = true }; };
|
||||
template<> struct static_or<true, false> { enum { value = true }; };
|
||||
template<> struct static_or<true, true> { enum { value = true }; };
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,10 +5,6 @@
|
|||
#include <utility>
|
||||
|
||||
namespace nall {
|
||||
template<bool C, typename T = bool> struct enable_if { typedef T type; };
|
||||
template<typename T> struct enable_if<false, T> {};
|
||||
template<typename C, typename T = bool> struct mp_enable_if : enable_if<C::value, T> {};
|
||||
|
||||
template<typename T> struct base_from_member {
|
||||
T value;
|
||||
base_from_member(T value_) : value(value_) {}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#ifndef NALL_VARINT_HPP
|
||||
#define NALL_VARINT_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/static.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<unsigned bits> class uint_t {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
@ -70,6 +71,19 @@ namespace nall {
|
|||
objectsize = (count + index >= objectsize) ? index : objectsize - count;
|
||||
}
|
||||
|
||||
void sort() {
|
||||
nall::sort(pool, objectsize);
|
||||
}
|
||||
|
||||
template<typename Comparator> void sort(const Comparator &lessthan) {
|
||||
nall::sort(pool, objectsize, lessthan);
|
||||
}
|
||||
|
||||
optional<unsigned> find(const T& data) {
|
||||
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
|
||||
return { false, 0u };
|
||||
}
|
||||
|
||||
//access
|
||||
inline T& operator[](unsigned position) {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
|
@ -81,6 +95,12 @@ namespace nall {
|
|||
return pool[position];
|
||||
}
|
||||
|
||||
inline T& operator()(unsigned position) {
|
||||
if(position >= poolsize) reserve(position + 1);
|
||||
while(position >= objectsize) append(T());
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline const T& operator()(unsigned position, const T& data) const {
|
||||
if(position >= objectsize) return data;
|
||||
return pool[position];
|
||||
|
@ -141,6 +161,8 @@ namespace nall {
|
|||
//if objects hold memory address references to themselves (introspection), a
|
||||
//valid copy constructor will be needed to keep pointers valid.
|
||||
|
||||
#define NALL_DEPRECATED
|
||||
#if defined(NALL_DEPRECATED)
|
||||
template<typename T> struct linear_vector {
|
||||
protected:
|
||||
T *pool;
|
||||
|
@ -412,6 +434,7 @@ namespace nall {
|
|||
const iterator begin() const { return iterator(*this, 0); }
|
||||
const iterator end() const { return iterator(*this, objectsize); }
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -116,7 +116,7 @@ protected:
|
|||
}
|
||||
|
||||
public:
|
||||
linear_vector<File> file;
|
||||
vector<File> file;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -90,10 +90,10 @@ Board::Board(XML::Node &board, const uint8_t *data, unsigned size) {
|
|||
information.type = board["type"].data;
|
||||
information.battery = board["prg"]["battery"].data == "true";
|
||||
|
||||
prgrom.size = hex(board["prg"]["rom"].data);
|
||||
prgram.size = hex(board["prg"]["ram"].data);
|
||||
chrrom.size = hex(board["chr"]["rom"].data);
|
||||
chrram.size = hex(board["chr"]["ram"].data);
|
||||
prgrom.size = numeral(board["prg"]["rom"].data);
|
||||
prgram.size = numeral(board["prg"]["ram"].data);
|
||||
chrrom.size = numeral(board["chr"]["rom"].data);
|
||||
chrram.size = numeral(board["chr"]["ram"].data);
|
||||
|
||||
if(prgrom.size) prgrom.data = new uint8[prgrom.size]();
|
||||
if(prgram.size) prgram.data = new uint8[prgram.size]();
|
||||
|
|
|
@ -23,7 +23,7 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
|
|||
//unsigned crc32 = crc32_calculate(data + 16, size - 16);
|
||||
//print(hex<8>(crc32), "\n");
|
||||
sha256 = nall::sha256(data + 16, size - 16);
|
||||
board = Board::load(markup != "" ? markup : iNES(data, size), data + 16, size - 16);
|
||||
board = Board::load(!markup.empty() ? markup : iNES(data, size), data + 16, size - 16);
|
||||
}
|
||||
if(board == nullptr) return;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||
unsigned prgram = 0;
|
||||
unsigned chrram = chrrom == 0 ? 8192 : 0;
|
||||
|
||||
//print("iNES mapper: ", mapper, "\n");
|
||||
print("iNES mapper: ", mapper, "\n");
|
||||
|
||||
output.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||
output.append("<cartridge>\n");
|
||||
|
@ -151,7 +151,7 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||
"</cartridge>\n"
|
||||
);
|
||||
|
||||
//print(output, "\n");
|
||||
print(output, "\n");
|
||||
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -412,7 +412,7 @@ Action::~Action() {
|
|||
//Menu
|
||||
//====
|
||||
|
||||
void Menu::append(const reference_array<Action&> &list) {
|
||||
void Menu::append(const array<Action&> &list) {
|
||||
for(auto &action : list) {
|
||||
if(state.action.append(action)) {
|
||||
action.state.menu = this;
|
||||
|
@ -421,7 +421,7 @@ void Menu::append(const reference_array<Action&> &list) {
|
|||
}
|
||||
}
|
||||
|
||||
void Menu::remove(const reference_array<Action&> &list) {
|
||||
void Menu::remove(const array<Action&> &list) {
|
||||
for(auto &action : list) {
|
||||
if(state.action.remove(action)) {
|
||||
action.state.menu = 0;
|
||||
|
@ -430,6 +430,11 @@ void Menu::remove(const reference_array<Action&> &list) {
|
|||
}
|
||||
}
|
||||
|
||||
void Menu::setImage(const image &image) {
|
||||
state.image = image;
|
||||
return p.setImage(image);
|
||||
}
|
||||
|
||||
void Menu::setText(const string &text) {
|
||||
state.text = text;
|
||||
return p.setText(text);
|
||||
|
@ -521,7 +526,7 @@ CheckItem::~CheckItem() {
|
|||
//RadioItem
|
||||
//=========
|
||||
|
||||
void RadioItem::group(const reference_array<RadioItem&> &list) {
|
||||
void RadioItem::group(const array<RadioItem&> &list) {
|
||||
for(auto &item : list) item.p.setGroup(item.state.group = list);
|
||||
if(list.size()) list[0].setChecked();
|
||||
}
|
||||
|
@ -1105,7 +1110,7 @@ ProgressBar::~ProgressBar() {
|
|||
//RadioBox
|
||||
//========
|
||||
|
||||
void RadioBox::group(const reference_array<RadioBox&> &list) {
|
||||
void RadioBox::group(const array<RadioBox&> &list) {
|
||||
for(auto &item : list) item.p.setGroup(item.state.group = list);
|
||||
if(list.size()) list[0].setChecked();
|
||||
}
|
||||
|
|
|
@ -231,8 +231,9 @@ struct Menu : private nall::base_from_member<pMenu&>, Action {
|
|||
template<typename... Args> void append(Args&... args) { append({ args... }); }
|
||||
template<typename... Args> void remove(Args&... args) { remove({ args... }); }
|
||||
|
||||
void append(const nall::reference_array<Action&> &list);
|
||||
void remove(const nall::reference_array<Action&> &list);
|
||||
void append(const nall::array<Action&> &list);
|
||||
void remove(const nall::array<Action&> &list);
|
||||
void setImage(const nall::image &image);
|
||||
void setText(const nall::string &text);
|
||||
|
||||
Menu();
|
||||
|
@ -277,7 +278,7 @@ struct CheckItem : private nall::base_from_member<pCheckItem&>, Action {
|
|||
|
||||
struct RadioItem : private nall::base_from_member<pRadioItem&>, Action {
|
||||
template<typename... Args> static void group(Args&... args) { group({ args... }); }
|
||||
static void group(const nall::reference_array<RadioItem&> &list);
|
||||
static void group(const nall::array<RadioItem&> &list);
|
||||
|
||||
nall::function<void ()> onActivate;
|
||||
|
||||
|
@ -357,6 +358,11 @@ struct Button : private nall::base_from_member<pButton&>, Widget {
|
|||
};
|
||||
|
||||
struct Canvas : private nall::base_from_member<pCanvas&>, Widget {
|
||||
nall::function<void ()> onMouseLeave;
|
||||
nall::function<void (Position)> onMouseMove;
|
||||
nall::function<void (Mouse::Button)> onMousePress;
|
||||
nall::function<void (Mouse::Button)> onMouseRelease;
|
||||
|
||||
uint32_t* data();
|
||||
bool setImage(const nall::image &image);
|
||||
void setSize(const Size &size);
|
||||
|
@ -515,7 +521,7 @@ struct ProgressBar : private nall::base_from_member<pProgressBar&>, Widget {
|
|||
|
||||
struct RadioBox : private nall::base_from_member<pRadioBox&>, Widget {
|
||||
template<typename... Args> static void group(Args&... args) { group({ args... }); }
|
||||
static void group(const nall::reference_array<RadioBox&> &list);
|
||||
static void group(const nall::array<RadioBox&> &list);
|
||||
|
||||
nall::function<void ()> onActivate;
|
||||
|
||||
|
@ -577,6 +583,11 @@ struct VerticalSlider : private nall::base_from_member<pVerticalSlider&>, Widget
|
|||
};
|
||||
|
||||
struct Viewport : private nall::base_from_member<pViewport&>, Widget {
|
||||
nall::function<void ()> onMouseLeave;
|
||||
nall::function<void (Position)> onMouseMove;
|
||||
nall::function<void (Mouse::Button)> onMousePress;
|
||||
nall::function<void (Mouse::Button)> onMouseRelease;
|
||||
|
||||
uintptr_t handle();
|
||||
|
||||
Viewport();
|
||||
|
|
|
@ -14,8 +14,8 @@ struct Window::State {
|
|||
bool fullScreen;
|
||||
Geometry geometry;
|
||||
bool ignore;
|
||||
reference_array<Layout&> layout;
|
||||
reference_array<Menu&> menu;
|
||||
array<Layout&> layout;
|
||||
array<Menu&> menu;
|
||||
string menuFont;
|
||||
bool menuVisible;
|
||||
bool resizable;
|
||||
|
@ -24,7 +24,7 @@ struct Window::State {
|
|||
bool statusVisible;
|
||||
string title;
|
||||
bool visible;
|
||||
reference_array<Widget&> widget;
|
||||
array<Widget&> widget;
|
||||
string widgetFont;
|
||||
|
||||
State() {
|
||||
|
@ -55,8 +55,12 @@ struct Action::State {
|
|||
};
|
||||
|
||||
struct Menu::State {
|
||||
reference_array<Action&> action;
|
||||
array<Action&> action;
|
||||
nall::image image;
|
||||
string text;
|
||||
|
||||
State() : image(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0) {
|
||||
}
|
||||
};
|
||||
|
||||
struct Item::State {
|
||||
|
@ -78,7 +82,7 @@ struct CheckItem::State {
|
|||
|
||||
struct RadioItem::State {
|
||||
bool checked;
|
||||
reference_array<RadioItem&> group;
|
||||
array<RadioItem&> group;
|
||||
string text;
|
||||
|
||||
State() {
|
||||
|
@ -229,7 +233,7 @@ struct ProgressBar::State {
|
|||
|
||||
struct RadioBox::State {
|
||||
bool checked;
|
||||
reference_array<RadioBox&> group;
|
||||
array<RadioBox&> group;
|
||||
string text;
|
||||
|
||||
State() {
|
||||
|
|
|
@ -13,13 +13,18 @@ void pMenu::remove(Action &action) {
|
|||
action.state.window = 0;
|
||||
}
|
||||
|
||||
void pMenu::setImage(const image &image) {
|
||||
GtkImage *gtkImage = CreateImage(image, /* menuIcon = */ true);
|
||||
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), (GtkWidget*)gtkImage);
|
||||
}
|
||||
|
||||
void pMenu::setText(const string &text) {
|
||||
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
|
||||
}
|
||||
|
||||
void pMenu::constructor() {
|
||||
gtkMenu = gtk_menu_new();
|
||||
widget = gtk_menu_item_new_with_label(menu.state.text);
|
||||
widget = gtk_image_menu_item_new_with_label(menu.state.text);
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), gtkMenu);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ void pRadioItem::setChecked() {
|
|||
locked = false;
|
||||
}
|
||||
|
||||
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
|
||||
void pRadioItem::setGroup(const array<RadioItem&> &group) {
|
||||
for(unsigned n = 0; n < group.size(); n++) {
|
||||
if(n == 0) continue;
|
||||
GSList *currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(group[0].p.widget));
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
bidirectional_map<Keyboard::Scancode, unsigned> pKeyboard::keymap;
|
||||
|
||||
void pKeyboard::initialize() {
|
||||
auto append = [](Keyboard::Scancode scancode, unsigned keysym) {
|
||||
keymap.append(scancode, XKeysymToKeycode(pOS::display, keysym));
|
||||
settings->keymap.insert(scancode, XKeysymToKeycode(pOS::display, keysym));
|
||||
};
|
||||
|
||||
append(Keyboard::Scancode::Escape, XK_Escape);
|
||||
|
@ -123,18 +121,18 @@ void pKeyboard::initialize() {
|
|||
bool pKeyboard::pressed(Keyboard::Scancode scancode) {
|
||||
char state[256];
|
||||
XQueryKeymap(pOS::display, state);
|
||||
unsigned id = keymap.lhs[scancode];
|
||||
unsigned id = settings->keymap.lhs[scancode];
|
||||
return state[id >> 3] & (1 << (id & 7));
|
||||
}
|
||||
|
||||
array<bool> pKeyboard::state() {
|
||||
array<bool> output;
|
||||
output.reserve((unsigned)Keyboard::Scancode::Limit);
|
||||
output.resize((unsigned)Keyboard::Scancode::Limit);
|
||||
for(auto &n : output) n = false;
|
||||
|
||||
char state[256];
|
||||
XQueryKeymap(pOS::display, state);
|
||||
for(auto &n : keymap.rhs) {
|
||||
for(auto &n : settings->keymap.rhs) {
|
||||
if(state[n.name >> 3] & (1 << (n.name & 7))) {
|
||||
output[(unsigned)n.data] = true;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "platform.hpp"
|
||||
#include "utility.cpp"
|
||||
#include "settings.cpp"
|
||||
|
||||
#include "desktop.cpp"
|
||||
#include "keyboard.cpp"
|
||||
|
@ -7,7 +8,6 @@
|
|||
#include "dialog-window.cpp"
|
||||
#include "message-window.cpp"
|
||||
|
||||
#include "settings.cpp"
|
||||
#include "font.cpp"
|
||||
#include "timer.cpp"
|
||||
#include "window.cpp"
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct Settings : public configuration {
|
||||
bidirectional_map<Keyboard::Scancode, unsigned> keymap;
|
||||
|
||||
unsigned frameGeometryX;
|
||||
unsigned frameGeometryY;
|
||||
unsigned frameGeometryWidth;
|
||||
|
@ -32,8 +34,6 @@ struct pDesktop {
|
|||
};
|
||||
|
||||
struct pKeyboard {
|
||||
static bidirectional_map<Keyboard::Scancode, unsigned> keymap;
|
||||
|
||||
static bool pressed(Keyboard::Scancode scancode);
|
||||
static array<bool> state();
|
||||
|
||||
|
@ -150,6 +150,7 @@ struct pMenu : public pAction {
|
|||
|
||||
void append(Action &action);
|
||||
void remove(Action &action);
|
||||
void setImage(const image &image);
|
||||
void setText(const string &text);
|
||||
|
||||
pMenu(Menu &menu) : pAction(menu), menu(menu) {}
|
||||
|
@ -198,7 +199,7 @@ struct pRadioItem : public pAction {
|
|||
|
||||
bool checked();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioItem&> &group);
|
||||
void setGroup(const array<RadioItem&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioItem(RadioItem &radioItem) : pAction(radioItem), radioItem(radioItem) {}
|
||||
|
@ -424,7 +425,7 @@ struct pRadioBox : public pWidget {
|
|||
bool checked();
|
||||
Geometry minimumGeometry();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioBox&> &group);
|
||||
void setGroup(const array<RadioBox&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioBox(RadioBox &radioBox) : pWidget(radioBox), radioBox(radioBox) {}
|
||||
|
|
|
@ -6,6 +6,34 @@ static gboolean Canvas_expose(GtkWidget *widget, GdkEvent *event, pCanvas *self)
|
|||
return true;
|
||||
}
|
||||
|
||||
static gboolean Canvas_mouseLeave(GtkWidget *widget, GdkEventButton *event, pCanvas *self) {
|
||||
if(self->canvas.onMouseLeave) self->canvas.onMouseLeave();
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean Canvas_mouseMove(GtkWidget *widget, GdkEventButton *event, pCanvas *self) {
|
||||
if(self->canvas.onMouseMove) self->canvas.onMouseMove({ (signed)event->x, (signed)event->y });
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean Canvas_mousePress(GtkWidget *widget, GdkEventButton *event, pCanvas *self) {
|
||||
if(self->canvas.onMousePress) switch(event->button) {
|
||||
case 1: self->canvas.onMousePress(Mouse::Button::Left); break;
|
||||
case 2: self->canvas.onMousePress(Mouse::Button::Middle); break;
|
||||
case 3: self->canvas.onMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean Canvas_mouseRelease(GtkWidget *widget, GdkEventButton *event, pCanvas *self) {
|
||||
if(self->canvas.onMouseRelease) switch(event->button) {
|
||||
case 1: self->canvas.onMouseRelease(Mouse::Button::Left); break;
|
||||
case 2: self->canvas.onMouseRelease(Mouse::Button::Middle); break;
|
||||
case 3: self->canvas.onMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void pCanvas::setSize(const Size &size) {
|
||||
cairo_surface_destroy(surface);
|
||||
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, canvas.state.width, canvas.state.height);
|
||||
|
@ -22,8 +50,13 @@ void pCanvas::constructor() {
|
|||
memcpy(cairo_image_surface_get_data(surface), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
|
||||
gtkWidget = gtk_drawing_area_new();
|
||||
gtk_widget_set_double_buffered(gtkWidget, false);
|
||||
gtk_widget_add_events(gtkWidget, GDK_EXPOSURE_MASK);
|
||||
gtk_widget_add_events(gtkWidget,
|
||||
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button_press_event", G_CALLBACK(Canvas_mousePress), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button_release_event", G_CALLBACK(Canvas_mouseRelease), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "leave_notify_event", G_CALLBACK(Canvas_mouseLeave), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "motion_notify_event", G_CALLBACK(Canvas_mouseMove), (gpointer)this);
|
||||
}
|
||||
|
||||
void pCanvas::destructor() {
|
||||
|
|
|
@ -19,7 +19,7 @@ void pRadioBox::setChecked() {
|
|||
locked = false;
|
||||
}
|
||||
|
||||
void pRadioBox::setGroup(const reference_array<RadioBox&> &group) {
|
||||
void pRadioBox::setGroup(const array<RadioBox&> &group) {
|
||||
for(unsigned n = 0; n < group.size(); n++) {
|
||||
if(n == 0) continue;
|
||||
GSList *currentGroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(group[0].p.gtkWidget));
|
||||
|
|
|
@ -1,3 +1,31 @@
|
|||
static gboolean Viewport_mouseLeave(GtkWidget *widget, GdkEventButton *event, pViewport *self) {
|
||||
if(self->viewport.onMouseLeave) self->viewport.onMouseLeave();
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean Viewport_mouseMove(GtkWidget *widget, GdkEventButton *event, pViewport *self) {
|
||||
if(self->viewport.onMouseMove) self->viewport.onMouseMove({ (signed)event->x, (signed)event->y });
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean Viewport_mousePress(GtkWidget *widget, GdkEventButton *event, pViewport *self) {
|
||||
if(self->viewport.onMousePress) switch(event->button) {
|
||||
case 1: self->viewport.onMousePress(Mouse::Button::Left); break;
|
||||
case 2: self->viewport.onMousePress(Mouse::Button::Middle); break;
|
||||
case 3: self->viewport.onMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean Viewport_mouseRelease(GtkWidget *widget, GdkEventButton *event, pViewport *self) {
|
||||
if(self->viewport.onMouseRelease) switch(event->button) {
|
||||
case 1: self->viewport.onMouseRelease(Mouse::Button::Left); break;
|
||||
case 2: self->viewport.onMouseRelease(Mouse::Button::Middle); break;
|
||||
case 3: self->viewport.onMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t pViewport::handle() {
|
||||
return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget));
|
||||
}
|
||||
|
@ -5,6 +33,12 @@ uintptr_t pViewport::handle() {
|
|||
void pViewport::constructor() {
|
||||
gtkWidget = gtk_drawing_area_new();
|
||||
//gtk_widget_set_double_buffered(gtkWidget, false);
|
||||
gtk_widget_add_events(gtkWidget,
|
||||
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button_press_event", G_CALLBACK(Viewport_mousePress), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button_release_event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "leave_notify_event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "motion_notify_event", G_CALLBACK(Viewport_mouseMove), (gpointer)this);
|
||||
|
||||
GdkColor color;
|
||||
color.pixel = 0;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <nall/function.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/map.hpp>
|
||||
#include <nall/reference_array.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
|
|
@ -33,6 +33,15 @@ void pMenu::setFont(const string &font) {
|
|||
for(auto &item : menu.state.action) item.p.setFont(font);
|
||||
}
|
||||
|
||||
void pMenu::setImage(const image &image) {
|
||||
nall::image qtBuffer = image;
|
||||
qtBuffer.transform(0, 32u, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
|
||||
|
||||
QImage qtImage(qtBuffer.data, qtBuffer.width, qtBuffer.height, QImage::Format_ARGB32);
|
||||
QIcon qtIcon(QPixmap::fromImage(qtImage));
|
||||
qtMenu->setIcon(qtIcon);
|
||||
}
|
||||
|
||||
void pMenu::setText(const string &text) {
|
||||
qtMenu->setTitle(QString::fromUtf8(text));
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ void pRadioItem::setChecked() {
|
|||
locked = false;
|
||||
}
|
||||
|
||||
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
|
||||
void pRadioItem::setGroup(const array<RadioItem&> &group) {
|
||||
}
|
||||
|
||||
void pRadioItem::setText(const string &text) {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
bidirectional_map<Keyboard::Scancode, unsigned> pKeyboard::keymap;
|
||||
|
||||
void pKeyboard::initialize() {
|
||||
auto append = [](Keyboard::Scancode scancode, unsigned keysym) {
|
||||
keymap.append(scancode, XKeysymToKeycode(pOS::display, keysym));
|
||||
settings->keymap.insert(scancode, XKeysymToKeycode(pOS::display, keysym));
|
||||
};
|
||||
|
||||
append(Keyboard::Scancode::Escape, XK_Escape);
|
||||
|
@ -123,7 +121,7 @@ void pKeyboard::initialize() {
|
|||
bool pKeyboard::pressed(Keyboard::Scancode scancode) {
|
||||
char state[256];
|
||||
XQueryKeymap(pOS::display, state);
|
||||
unsigned id = keymap.lhs[scancode];
|
||||
unsigned id = settings->keymap.lhs[scancode];
|
||||
return state[id >> 3] & (1 << (id & 7));
|
||||
}
|
||||
|
||||
|
@ -134,7 +132,7 @@ array<bool> pKeyboard::state() {
|
|||
|
||||
char state[256];
|
||||
XQueryKeymap(pOS::display, state);
|
||||
for(auto &n : keymap.rhs) {
|
||||
for(auto &n : settings->keymap.rhs) {
|
||||
if(state[n.name >> 3] & (1 << (n.name & 7))) {
|
||||
output[(unsigned)n.data] = true;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "platform.moc.hpp"
|
||||
#include "platform.moc"
|
||||
#include "utility.cpp"
|
||||
#include "settings.cpp"
|
||||
|
||||
#include "desktop.cpp"
|
||||
#include "keyboard.cpp"
|
||||
|
@ -8,7 +9,6 @@
|
|||
#include "dialog-window.cpp"
|
||||
#include "message-window.cpp"
|
||||
|
||||
#include "settings.cpp"
|
||||
#include "font.cpp"
|
||||
#include "timer.cpp"
|
||||
#include "window.cpp"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
** Meta object code from reading C++ file 'platform.moc.hpp'
|
||||
**
|
||||
** Created: Sat Jan 14 09:18:07 2012
|
||||
** Created: Mon Jan 23 13:07:40 2012
|
||||
** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
static QApplication *qtApplication = 0;
|
||||
|
||||
struct Settings : public configuration {
|
||||
bidirectional_map<Keyboard::Scancode, unsigned> keymap;
|
||||
|
||||
unsigned frameGeometryX;
|
||||
unsigned frameGeometryY;
|
||||
unsigned frameGeometryWidth;
|
||||
|
@ -31,8 +33,6 @@ struct pDesktop {
|
|||
};
|
||||
|
||||
struct pKeyboard {
|
||||
static bidirectional_map<Keyboard::Scancode, unsigned> keymap;
|
||||
|
||||
static bool pressed(Keyboard::Scancode scancode);
|
||||
static array<bool> state();
|
||||
|
||||
|
@ -166,6 +166,7 @@ struct pMenu : public pAction {
|
|||
void append(Action &action);
|
||||
void remove(Action &action);
|
||||
void setFont(const string &font);
|
||||
void setImage(const image &image);
|
||||
void setText(const string &text);
|
||||
|
||||
pMenu(Menu &menu) : pAction(menu), menu(menu) {}
|
||||
|
@ -229,7 +230,7 @@ public:
|
|||
|
||||
bool checked();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioItem&> &group);
|
||||
void setGroup(const array<RadioItem&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioItem(RadioItem &radioItem) : pAction(radioItem), radioItem(radioItem) {}
|
||||
|
@ -304,6 +305,10 @@ public:
|
|||
QImage *qtImage;
|
||||
struct QtCanvas : public QWidget {
|
||||
pCanvas &self;
|
||||
void leaveEvent(QEvent*);
|
||||
void mouseMoveEvent(QMouseEvent*);
|
||||
void mousePressEvent(QMouseEvent*);
|
||||
void mouseReleaseEvent(QMouseEvent*);
|
||||
void paintEvent(QPaintEvent*);
|
||||
QtCanvas(pCanvas &self);
|
||||
} *qtCanvas;
|
||||
|
@ -525,7 +530,7 @@ public:
|
|||
bool checked();
|
||||
Geometry minimumGeometry();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioBox&> &group);
|
||||
void setGroup(const array<RadioBox&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioBox(RadioBox &radioBox) : pWidget(radioBox), radioBox(radioBox) {}
|
||||
|
@ -603,6 +608,14 @@ public slots:
|
|||
|
||||
struct pViewport : public pWidget {
|
||||
Viewport &viewport;
|
||||
struct QtViewport : public QWidget {
|
||||
pViewport &self;
|
||||
void leaveEvent(QEvent*);
|
||||
void mouseMoveEvent(QMouseEvent*);
|
||||
void mousePressEvent(QMouseEvent*);
|
||||
void mouseReleaseEvent(QMouseEvent*);
|
||||
QtViewport(pViewport &self);
|
||||
} *qtViewport;
|
||||
|
||||
uintptr_t handle();
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ void pButton::setImage(const image &image, Orientation orientation) {
|
|||
QIcon qtIcon(QPixmap::fromImage(qtImage));
|
||||
qtButton->setIconSize(QSize(qtBuffer.width, qtBuffer.height));
|
||||
qtButton->setIcon(qtIcon);
|
||||
qtButton->setStyleSheet("text-align: top;");
|
||||
switch(orientation) {
|
||||
case Orientation::Horizontal: qtButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); break;
|
||||
case Orientation::Vertical: qtButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); break;
|
||||
|
|
|
@ -11,6 +11,7 @@ void pCanvas::update() {
|
|||
|
||||
void pCanvas::constructor() {
|
||||
qtWidget = qtCanvas = new QtCanvas(*this);
|
||||
qtCanvas->setMouseTracking(true);
|
||||
qtImage = new QImage(canvas.state.width, canvas.state.height, QImage::Format_ARGB32);
|
||||
memcpy(qtImage->bits(), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
|
||||
|
||||
|
@ -30,6 +31,32 @@ void pCanvas::orphan() {
|
|||
constructor();
|
||||
}
|
||||
|
||||
void pCanvas::QtCanvas::leaveEvent(QEvent *event) {
|
||||
if(self.canvas.onMouseLeave) self.canvas.onMouseLeave();
|
||||
}
|
||||
|
||||
void pCanvas::QtCanvas::mouseMoveEvent(QMouseEvent *event) {
|
||||
if(self.canvas.onMouseMove) self.canvas.onMouseMove({ event->pos().x(), event->pos().y() });
|
||||
}
|
||||
|
||||
void pCanvas::QtCanvas::mousePressEvent(QMouseEvent *event) {
|
||||
if(self.canvas.onMousePress == false) return;
|
||||
switch(event->button()) {
|
||||
case Qt::LeftButton: self.canvas.onMousePress(Mouse::Button::Left); break;
|
||||
case Qt::MidButton: self.canvas.onMousePress(Mouse::Button::Middle); break;
|
||||
case Qt::RightButton: self.canvas.onMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
void pCanvas::QtCanvas::mouseReleaseEvent(QMouseEvent *event) {
|
||||
if(self.canvas.onMouseRelease == false) return;
|
||||
switch(event->button()) {
|
||||
case Qt::LeftButton: self.canvas.onMouseRelease(Mouse::Button::Left); break;
|
||||
case Qt::MidButton: self.canvas.onMouseRelease(Mouse::Button::Middle); break;
|
||||
case Qt::RightButton: self.canvas.onMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
void pCanvas::QtCanvas::paintEvent(QPaintEvent *event) {
|
||||
QPainter painter(self.qtCanvas);
|
||||
painter.drawImage(0, 0, *self.qtImage);
|
||||
|
|
|
@ -17,7 +17,7 @@ void pRadioBox::setChecked() {
|
|||
locked = false;
|
||||
}
|
||||
|
||||
void pRadioBox::setGroup(const reference_array<RadioBox&> &group) {
|
||||
void pRadioBox::setGroup(const array<RadioBox&> &group) {
|
||||
locked = true;
|
||||
if(qtGroup) {
|
||||
delete qtGroup;
|
||||
|
|
|
@ -1,21 +1,51 @@
|
|||
uintptr_t pViewport::handle() {
|
||||
return (uintptr_t)qtWidget->winId();
|
||||
return (uintptr_t)qtViewport->winId();
|
||||
}
|
||||
|
||||
void pViewport::constructor() {
|
||||
qtWidget = new QWidget;
|
||||
qtWidget->setAttribute(Qt::WA_PaintOnScreen, true);
|
||||
qtWidget->setStyleSheet("background: #000000");
|
||||
qtWidget = qtViewport = new QtViewport(*this);
|
||||
qtViewport->setMouseTracking(true);
|
||||
qtViewport->setAttribute(Qt::WA_PaintOnScreen, true);
|
||||
qtViewport->setStyleSheet("background: #000000");
|
||||
|
||||
pWidget::synchronizeState();
|
||||
}
|
||||
|
||||
void pViewport::destructor() {
|
||||
delete qtWidget;
|
||||
qtWidget = 0;
|
||||
delete qtViewport;
|
||||
qtWidget = qtViewport = nullptr;
|
||||
}
|
||||
|
||||
void pViewport::orphan() {
|
||||
destructor();
|
||||
constructor();
|
||||
}
|
||||
|
||||
void pViewport::QtViewport::leaveEvent(QEvent *event) {
|
||||
if(self.viewport.onMouseLeave) self.viewport.onMouseLeave();
|
||||
}
|
||||
|
||||
void pViewport::QtViewport::mouseMoveEvent(QMouseEvent *event) {
|
||||
if(self.viewport.onMouseMove) self.viewport.onMouseMove({ event->pos().x(), event->pos().y() });
|
||||
}
|
||||
|
||||
void pViewport::QtViewport::mousePressEvent(QMouseEvent *event) {
|
||||
if(self.viewport.onMousePress == false) return;
|
||||
switch(event->button()) {
|
||||
case Qt::LeftButton: self.viewport.onMousePress(Mouse::Button::Left); break;
|
||||
case Qt::MidButton: self.viewport.onMousePress(Mouse::Button::Middle); break;
|
||||
case Qt::RightButton: self.viewport.onMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
void pViewport::QtViewport::mouseReleaseEvent(QMouseEvent *event) {
|
||||
if(self.viewport.onMouseRelease == false) return;
|
||||
switch(event->button()) {
|
||||
case Qt::LeftButton: self.viewport.onMouseRelease(Mouse::Button::Left); break;
|
||||
case Qt::MidButton: self.viewport.onMouseRelease(Mouse::Button::Middle); break;
|
||||
case Qt::RightButton: self.viewport.onMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
pViewport::QtViewport::QtViewport(pViewport &self) : self(self) {
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ void pMenu::append(Action &action) {
|
|||
void pMenu::remove(Action &action) {
|
||||
}
|
||||
|
||||
void pMenu::setImage(const image &image) {
|
||||
}
|
||||
|
||||
void pMenu::setText(const string &text) {
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ bool pRadioItem::checked() {
|
|||
void pRadioItem::setChecked() {
|
||||
}
|
||||
|
||||
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
|
||||
void pRadioItem::setGroup(const array<RadioItem&> &group) {
|
||||
}
|
||||
|
||||
void pRadioItem::setText(const string &text) {
|
||||
|
|
|
@ -112,6 +112,7 @@ struct pMenu : public pAction {
|
|||
|
||||
void append(Action &action);
|
||||
void remove(Action &action);
|
||||
void setImage(const image &image);
|
||||
void setText(const string &text);
|
||||
|
||||
pMenu(Menu &menu) : pAction(menu), menu(menu) {}
|
||||
|
@ -155,7 +156,7 @@ struct pRadioItem : public pAction {
|
|||
|
||||
bool checked();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioItem&> &group);
|
||||
void setGroup(const array<RadioItem&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioItem(RadioItem &radioItem) : pAction(radioItem), radioItem(radioItem) {}
|
||||
|
@ -323,7 +324,7 @@ struct pRadioBox : public pWidget {
|
|||
|
||||
bool checked();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioBox&> &group);
|
||||
void setGroup(const array<RadioBox&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioBox(RadioBox &radioBox) : pWidget(radioBox), radioBox(radioBox) {}
|
||||
|
|
|
@ -5,7 +5,7 @@ bool pRadioBox::checked() {
|
|||
void pRadioBox::setChecked() {
|
||||
}
|
||||
|
||||
void pRadioBox::setGroup(const reference_array<RadioBox&> &group) {
|
||||
void pRadioBox::setGroup(const array<RadioBox&> &group) {
|
||||
}
|
||||
|
||||
void pRadioBox::setText(const string &text) {
|
||||
|
|
|
@ -26,4 +26,4 @@ void pItem::createBitmap() {
|
|||
nallImage.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear);
|
||||
hbitmap = CreateBitmap(nallImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,40 @@ void pMenu::remove(Action &action) {
|
|||
action.p.parentMenu = 0;
|
||||
}
|
||||
|
||||
void pMenu::setImage(const image &image) {
|
||||
createBitmap();
|
||||
if(parentWindow) parentWindow->p.updateMenu();
|
||||
}
|
||||
|
||||
void pMenu::setText(const string &text) {
|
||||
if(parentWindow) parentWindow->p.updateMenu();
|
||||
}
|
||||
|
||||
void pMenu::constructor() {
|
||||
hmenu = 0;
|
||||
createBitmap();
|
||||
}
|
||||
|
||||
void pMenu::destructor() {
|
||||
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
|
||||
if(parentMenu) {
|
||||
parentMenu->remove(menu);
|
||||
} else if(parentWindow) {
|
||||
//belongs to window's main menubar
|
||||
parentWindow->remove(menu);
|
||||
}
|
||||
}
|
||||
|
||||
void pMenu::createBitmap() {
|
||||
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
|
||||
|
||||
if(menu.state.image.width && menu.state.image.height) {
|
||||
nall::image nallImage = menu.state.image;
|
||||
nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
|
||||
nallImage.alphaBlend(GetSysColor(COLOR_MENU)); //Windows does not alpha blend menu icons properly (leaves black outline)
|
||||
nallImage.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear);
|
||||
hbitmap = CreateBitmap(nallImage);
|
||||
}
|
||||
}
|
||||
|
||||
//Windows actions lack the ability to toggle visibility.
|
||||
|
@ -32,11 +60,24 @@ void pMenu::update(Window &parentWindow, Menu *parentMenu) {
|
|||
unsigned enabled = action.state.enabled ? 0 : MF_GRAYED;
|
||||
if(dynamic_cast<Menu*>(&action)) {
|
||||
Menu &item = (Menu&)action;
|
||||
item.p.update(parentWindow, &menu);
|
||||
AppendMenu(hmenu, MF_STRING | MF_POPUP | enabled, (UINT_PTR)item.p.hmenu, utf16_t(item.state.text));
|
||||
if(action.state.visible) {
|
||||
item.p.update(parentWindow, &menu);
|
||||
AppendMenu(hmenu, MF_STRING | MF_POPUP | enabled, (UINT_PTR)item.p.hmenu, utf16_t(item.state.text));
|
||||
|
||||
if(item.state.image.width && item.state.image.height) {
|
||||
MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
|
||||
//Windows XP and below displays MIIM_BITMAP + hbmpItem in its own column (separate from check/radio marks)
|
||||
//this causes too much spacing, so use a custom checkmark image instead
|
||||
mii.fMask = MIIM_CHECKMARKS;
|
||||
mii.hbmpUnchecked = item.p.hbitmap;
|
||||
SetMenuItemInfo(hmenu, (UINT_PTR)item.p.hmenu, FALSE, &mii);
|
||||
}
|
||||
}
|
||||
} else if(dynamic_cast<Separator*>(&action)) {
|
||||
Separator &item = (Separator&)action;
|
||||
if(action.state.visible) AppendMenu(hmenu, MF_SEPARATOR | enabled, item.p.id, L"");
|
||||
if(action.state.visible) {
|
||||
AppendMenu(hmenu, MF_SEPARATOR | enabled, item.p.id, L"");
|
||||
}
|
||||
} else if(dynamic_cast<Item*>(&action)) {
|
||||
Item &item = (Item&)action;
|
||||
if(action.state.visible) {
|
||||
|
@ -53,21 +94,16 @@ void pMenu::update(Window &parentWindow, Menu *parentMenu) {
|
|||
}
|
||||
} else if(dynamic_cast<CheckItem*>(&action)) {
|
||||
CheckItem &item = (CheckItem&)action;
|
||||
if(action.state.visible) AppendMenu(hmenu, MF_STRING | enabled, item.p.id, utf16_t(item.state.text));
|
||||
if(action.state.visible) {
|
||||
AppendMenu(hmenu, MF_STRING | enabled, item.p.id, utf16_t(item.state.text));
|
||||
}
|
||||
if(item.state.checked) item.setChecked();
|
||||
} else if(dynamic_cast<RadioItem*>(&action)) {
|
||||
RadioItem &item = (RadioItem&)action;
|
||||
if(action.state.visible) AppendMenu(hmenu, MF_STRING | enabled, item.p.id, utf16_t(item.state.text));
|
||||
if(action.state.visible) {
|
||||
AppendMenu(hmenu, MF_STRING | enabled, item.p.id, utf16_t(item.state.text));
|
||||
}
|
||||
if(item.state.checked) item.setChecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pMenu::destructor() {
|
||||
if(parentMenu) {
|
||||
parentMenu->remove(menu);
|
||||
} else if(parentWindow) {
|
||||
//belongs to window's main menubar
|
||||
parentWindow->remove(menu);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ void pRadioItem::setChecked() {
|
|||
}
|
||||
}
|
||||
|
||||
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
|
||||
void pRadioItem::setGroup(const array<RadioItem&> &group) {
|
||||
}
|
||||
|
||||
void pRadioItem::setText(const string &text) {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
bidirectional_map<Keyboard::Scancode, unsigned> pKeyboard::keymap;
|
||||
|
||||
void pKeyboard::initialize() {
|
||||
auto append = [](Keyboard::Scancode scancode, unsigned keysym) {
|
||||
keymap.append(scancode, keysym);
|
||||
settings->keymap.insert(scancode, keysym);
|
||||
};
|
||||
|
||||
append(Keyboard::Scancode::Escape, VK_ESCAPE);
|
||||
|
@ -121,7 +119,7 @@ void pKeyboard::initialize() {
|
|||
}
|
||||
|
||||
bool pKeyboard::pressed(Keyboard::Scancode scancode) {
|
||||
return GetAsyncKeyState(keymap.lhs[scancode]) & 0x8000;
|
||||
return GetAsyncKeyState(settings->keymap.lhs[scancode]) & 0x8000;
|
||||
}
|
||||
|
||||
array<bool> pKeyboard::state() {
|
||||
|
@ -129,7 +127,7 @@ array<bool> pKeyboard::state() {
|
|||
output.resize((unsigned)Keyboard::Scancode::Limit);
|
||||
for(auto &n : output) n = false;
|
||||
|
||||
for(auto &n : keymap.rhs) {
|
||||
for(auto &n : settings->keymap.rhs) {
|
||||
if(GetAsyncKeyState(n.name) & 0x8000) {
|
||||
output[(unsigned)n.data] = true;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "platform.hpp"
|
||||
#include "utility.cpp"
|
||||
#include "settings.cpp"
|
||||
|
||||
#include "desktop.cpp"
|
||||
#include "keyboard.cpp"
|
||||
|
@ -135,6 +136,7 @@ void pOS::initialize() {
|
|||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
RegisterClass(&wc);
|
||||
|
||||
settings = new Settings;
|
||||
pKeyboard::initialize();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
struct Settings {
|
||||
bidirectional_map<Keyboard::Scancode, unsigned> keymap;
|
||||
};
|
||||
|
||||
struct pFont;
|
||||
struct pWindow;
|
||||
struct pMenu;
|
||||
|
@ -20,8 +24,6 @@ struct pDesktop {
|
|||
};
|
||||
|
||||
struct pKeyboard {
|
||||
static bidirectional_map<Keyboard::Scancode, unsigned> keymap;
|
||||
|
||||
static bool pressed(Keyboard::Scancode scancode);
|
||||
static array<bool> state();
|
||||
|
||||
|
@ -134,14 +136,17 @@ struct pAction : public pObject {
|
|||
struct pMenu : public pAction {
|
||||
Menu &menu;
|
||||
HMENU hmenu;
|
||||
HBITMAP hbitmap;
|
||||
|
||||
void append(Action &action);
|
||||
void remove(Action &action);
|
||||
void setImage(const image &image);
|
||||
void setText(const string &text);
|
||||
|
||||
pMenu(Menu &menu) : pAction(menu), menu(menu) {}
|
||||
pMenu(Menu &menu) : pAction(menu), menu(menu), hbitmap(0) {}
|
||||
void constructor();
|
||||
void destructor();
|
||||
void createBitmap();
|
||||
void update(Window &parentWindow, Menu *parentMenu = 0);
|
||||
};
|
||||
|
||||
|
@ -183,7 +188,7 @@ struct pRadioItem : public pAction {
|
|||
|
||||
bool checked();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioItem&> &group);
|
||||
void setGroup(const array<RadioItem&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioItem(RadioItem &radioItem) : pAction(radioItem), radioItem(radioItem) {}
|
||||
|
@ -398,7 +403,7 @@ struct pRadioBox : public pWidget {
|
|||
bool checked();
|
||||
Geometry minimumGeometry();
|
||||
void setChecked();
|
||||
void setGroup(const reference_array<RadioBox&> &group);
|
||||
void setGroup(const array<RadioBox&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
pRadioBox(RadioBox &radioBox) : pWidget(radioBox), radioBox(radioBox) {}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
static Settings *settings = nullptr;
|
|
@ -1,17 +1,44 @@
|
|||
static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
Object *object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
if(!dynamic_cast<Canvas*>(object)) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
Canvas &canvas = (Canvas&)*object;
|
||||
|
||||
if(msg == WM_GETDLGCODE) {
|
||||
return DLGC_STATIC | DLGC_WANTCHARS;
|
||||
}
|
||||
|
||||
if(msg == WM_PAINT) {
|
||||
Object *object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
if(object && dynamic_cast<Canvas*>(object)) {
|
||||
Canvas &canvas = (Canvas&)*object;
|
||||
canvas.p.paint();
|
||||
}
|
||||
canvas.p.paint();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(msg == WM_MOUSEMOVE) {
|
||||
TRACKMOUSEEVENT tracker = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd };
|
||||
TrackMouseEvent(&tracker);
|
||||
if(canvas.onMouseMove) canvas.onMouseMove({ (int16_t)LOWORD(lparam), (int16_t)HIWORD(lparam) });
|
||||
}
|
||||
|
||||
if(msg == WM_MOUSELEAVE) {
|
||||
if(canvas.onMouseLeave) canvas.onMouseLeave();
|
||||
}
|
||||
|
||||
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
|
||||
if(canvas.onMousePress) switch(msg) {
|
||||
case WM_LBUTTONDOWN: canvas.onMousePress(Mouse::Button::Left); break;
|
||||
case WM_MBUTTONDOWN: canvas.onMousePress(Mouse::Button::Middle); break;
|
||||
case WM_RBUTTONDOWN: canvas.onMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
|
||||
if(canvas.onMouseRelease) switch(msg) {
|
||||
case WM_LBUTTONUP: canvas.onMouseRelease(Mouse::Button::Left); break;
|
||||
case WM_MBUTTONUP: canvas.onMouseRelease(Mouse::Button::Middle); break;
|
||||
case WM_RBUTTONUP: canvas.onMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ void pRadioBox::setChecked() {
|
|||
}
|
||||
}
|
||||
|
||||
void pRadioBox::setGroup(const reference_array<RadioBox&> &group) {
|
||||
void pRadioBox::setGroup(const array<RadioBox&> &group) {
|
||||
}
|
||||
|
||||
void pRadioBox::setText(const string &text) {
|
||||
|
|
|
@ -1,3 +1,42 @@
|
|||
static LRESULT CALLBACK Viewport_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
Object *object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
if(!dynamic_cast<Viewport*>(object)) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
Viewport &viewport = (Viewport&)*object;
|
||||
|
||||
if(msg == WM_GETDLGCODE) {
|
||||
return DLGC_STATIC | DLGC_WANTCHARS;
|
||||
}
|
||||
|
||||
if(msg == WM_MOUSEMOVE) {
|
||||
TRACKMOUSEEVENT tracker = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd };
|
||||
TrackMouseEvent(&tracker);
|
||||
if(viewport.onMouseMove) viewport.onMouseMove({ (int16_t)LOWORD(lparam), (int16_t)HIWORD(lparam) });
|
||||
}
|
||||
|
||||
if(msg == WM_MOUSELEAVE) {
|
||||
if(viewport.onMouseLeave) viewport.onMouseLeave();
|
||||
}
|
||||
|
||||
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
|
||||
if(viewport.onMousePress) switch(msg) {
|
||||
case WM_LBUTTONDOWN: viewport.onMousePress(Mouse::Button::Left); break;
|
||||
case WM_MBUTTONDOWN: viewport.onMousePress(Mouse::Button::Middle); break;
|
||||
case WM_RBUTTONDOWN: viewport.onMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
|
||||
if(viewport.onMouseRelease) switch(msg) {
|
||||
case WM_LBUTTONUP: viewport.onMouseRelease(Mouse::Button::Left); break;
|
||||
case WM_MBUTTONUP: viewport.onMouseRelease(Mouse::Button::Middle); break;
|
||||
case WM_RBUTTONUP: viewport.onMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
uintptr_t pViewport::handle() {
|
||||
return (uintptr_t)hwnd;
|
||||
}
|
||||
|
@ -15,9 +54,4 @@ void pViewport::destructor() {
|
|||
void pViewport::orphan() {
|
||||
destructor();
|
||||
constructor();
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK Viewport_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
if(msg == WM_GETDLGCODE) return DLGC_STATIC | DLGC_WANTCHARS;
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
}
|
|
@ -91,7 +91,6 @@ struct Cartridge : property<Cartridge> {
|
|||
|
||||
private:
|
||||
void parse_markup(const char*);
|
||||
unsigned parse_markup_integer(string&);
|
||||
void parse_markup_map(Mapping&, XML::Node&);
|
||||
|
||||
void parse_markup_rom(XML::Node&);
|
||||
|
|
|
@ -29,14 +29,9 @@ void Cartridge::parse_markup(const char *markup) {
|
|||
|
||||
//
|
||||
|
||||
unsigned Cartridge::parse_markup_integer(string &data) {
|
||||
if(strbegin(data, "0x")) return hex(data);
|
||||
return decimal(data);
|
||||
}
|
||||
|
||||
void Cartridge::parse_markup_map(Mapping &m, XML::Node &map) {
|
||||
m.offset = parse_markup_integer(map["offset"].data);
|
||||
m.size = parse_markup_integer(map["size"].data);
|
||||
m.offset = numeral(map["offset"].data);
|
||||
m.size = numeral(map["size"].data);
|
||||
|
||||
string data = map["mode"].data;
|
||||
if(data == "direct") m.mode = Bus::MapMode::Direct;
|
||||
|
@ -82,7 +77,7 @@ void Cartridge::parse_markup_rom(XML::Node &root) {
|
|||
|
||||
void Cartridge::parse_markup_ram(XML::Node &root) {
|
||||
if(root.exists() == false) return;
|
||||
ram_size = parse_markup_integer(root["size"].data);
|
||||
ram_size = numeral(root["size"].data);
|
||||
for(auto &node : root) {
|
||||
Mapping m(ram);
|
||||
parse_markup_map(m, node);
|
||||
|
@ -100,11 +95,11 @@ void Cartridge::parse_markup_nss(XML::Node &root) {
|
|||
if(number >= 16) break; //more than 16 DIP switches is not physically possible
|
||||
|
||||
information.nss.option[number].reset();
|
||||
information.nss.setting[number] = node["name"].data;
|
||||
information.nss.setting.append(node["name"].data);
|
||||
for(auto &leaf : node) {
|
||||
if(leaf.name != "option") continue;
|
||||
string name = leaf["name"].data;
|
||||
unsigned value = parse_markup_integer(leaf["value"].data);
|
||||
unsigned value = numeral(leaf["value"].data);
|
||||
information.nss.option[number].append({ hex<4>(value), ":", name });
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +109,7 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
|
|||
if(root.exists() == false) return;
|
||||
if(mode != Mode::SuperGameBoy) return;
|
||||
|
||||
icd2.revision = max(1, parse_markup_integer(root["revision"].data));
|
||||
icd2.revision = max(1, numeral(root["revision"].data));
|
||||
|
||||
for(auto &node : root) {
|
||||
if(node.name != "map") continue;
|
||||
|
@ -140,7 +135,7 @@ void Cartridge::parse_markup_superfx(XML::Node &root) {
|
|||
if(node.name == "ram") {
|
||||
for(auto &leaf : node) {
|
||||
if(leaf.name == "size") {
|
||||
ram_size = parse_markup_integer(leaf.data);
|
||||
ram_size = numeral(leaf.data);
|
||||
continue;
|
||||
}
|
||||
if(leaf.name != "map") continue;
|
||||
|
@ -193,7 +188,7 @@ void Cartridge::parse_markup_sa1(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
ram_size = parse_markup_integer(bwram["size"].data);
|
||||
ram_size = numeral(bwram["size"].data);
|
||||
for(auto &node : bwram) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m(sa1.cpubwram);
|
||||
|
@ -217,7 +212,7 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
|
|||
for(unsigned n = 0; n < 16384; n++) necdsp.programROM[n] = 0x000000;
|
||||
for(unsigned n = 0; n < 2048; n++) necdsp.dataROM[n] = 0x0000;
|
||||
|
||||
necdsp.frequency = parse_markup_integer(root["frequency"].data);
|
||||
necdsp.frequency = numeral(root["frequency"].data);
|
||||
if(necdsp.frequency == 0) necdsp.frequency = 8000000;
|
||||
necdsp.revision
|
||||
= root["model"].data == "uPD7725" ? NECDSP::Revision::uPD7725
|
||||
|
@ -286,7 +281,7 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
|
|||
|
||||
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = 0x000000;
|
||||
|
||||
hitachidsp.frequency = parse_markup_integer(root["frequency"].data);
|
||||
hitachidsp.frequency = numeral(root["frequency"].data);
|
||||
if(hitachidsp.frequency == 0) hitachidsp.frequency = 20000000;
|
||||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
|
@ -379,7 +374,7 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
|
|||
}
|
||||
}
|
||||
if(node.name == "ram") {
|
||||
unsigned ram_size = parse_markup_integer(node["size"].data);
|
||||
unsigned ram_size = numeral(node["size"].data);
|
||||
for(auto &leaf : node) {
|
||||
if(leaf.name != "map") continue;
|
||||
Memory &memory = slotid == 0 ? sufamiturbo.slotA.ram : sufamiturbo.slotB.ram;
|
||||
|
@ -434,7 +429,7 @@ void Cartridge::parse_markup_spc7110(XML::Node &root) {
|
|||
auto &dcu = root["dcu"];
|
||||
auto &rtc = root["rtc"];
|
||||
|
||||
ram_size = parse_markup_integer(ram["size"].data);
|
||||
ram_size = numeral(ram["size"].data);
|
||||
for(auto &node : ram) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({ &SPC7110::ram_read, &spc7110 }, { &SPC7110::ram_write, &spc7110 });
|
||||
|
@ -449,7 +444,7 @@ void Cartridge::parse_markup_spc7110(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
spc7110.data_rom_offset = parse_markup_integer(mcu["offset"].data);
|
||||
spc7110.data_rom_offset = numeral(mcu["offset"].data);
|
||||
if(spc7110.data_rom_offset == 0) spc7110.data_rom_offset = 0x100000;
|
||||
for(auto &node : mcu) {
|
||||
if(node.name != "map") continue;
|
||||
|
@ -524,7 +519,7 @@ void Cartridge::parse_markup_link(XML::Node &root) {
|
|||
if(root.exists() == false) return;
|
||||
has_link = true;
|
||||
|
||||
link.frequency = max(1, parse_markup_integer(root["frequency"].data));
|
||||
link.frequency = max(1, numeral(root["frequency"].data));
|
||||
link.program = root["program"].data;
|
||||
|
||||
for(auto &node : root) {
|
||||
|
|
|
@ -9,6 +9,7 @@ unsigned Video::palette30(unsigned color) {
|
|||
unsigned r = (color >> 0) & 31;
|
||||
|
||||
double L = (1.0 + l) / 16.0;
|
||||
if(l == 0) L *= 0.5;
|
||||
unsigned R = L * ((r << 5) + (r << 0));
|
||||
unsigned G = L * ((g << 5) + (g << 0));
|
||||
unsigned B = L * ((b << 5) + (b << 0));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DipSwitches *dipSwitches = 0;
|
||||
DipSwitches *dipSwitches = nullptr;
|
||||
|
||||
DipSwitch::DipSwitch() {
|
||||
append(name, { ~0, 0 }, 5);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FileBrowser *fileBrowser = 0;
|
||||
FileBrowser *fileBrowser = nullptr;
|
||||
|
||||
FileBrowser::FileBrowser() {
|
||||
setGeometry({ 128, 128, 640, 400 });
|
||||
|
@ -130,21 +130,23 @@ bool FileBrowser::loadFolder(const string &requestedPath) {
|
|||
if(path.wildcard(filter)) accept = true;
|
||||
}
|
||||
if(accept == false) return false;
|
||||
|
||||
lstring contentsList = directory::contents(requestedPath);
|
||||
lstring fileNameList;
|
||||
for(auto &fileName : contentsList) {
|
||||
for(auto &filter : mode->filter) {
|
||||
if(fileName.wildcard(filter)) {
|
||||
fileNameList.append(fileName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(fileNameList.size() != 1) return false;
|
||||
loadFile({ requestedPath, fileNameList[0] });
|
||||
loadFile(requestedPath);
|
||||
return true;
|
||||
|
||||
// lstring contentsList = directory::contents(requestedPath);
|
||||
// lstring fileNameList;
|
||||
// for(auto &fileName : contentsList) {
|
||||
// for(auto &filter : mode->filter) {
|
||||
// if(fileName.wildcard(filter)) {
|
||||
// fileNameList.append(fileName);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if(fileNameList.size() != 1) return false;
|
||||
// loadFile({ requestedPath, fileNameList[0] });
|
||||
// return true;
|
||||
}
|
||||
|
||||
void FileBrowser::loadFile(const string &filename) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
MainWindow *mainWindow = 0;
|
||||
MainWindow *mainWindow = nullptr;
|
||||
|
||||
MainWindow::MainWindow() {
|
||||
setTitle(application->title);
|
||||
|
@ -338,7 +338,7 @@ void MainWindow::setupVideoFilters() {
|
|||
path = { application->userpath, "filters/" };
|
||||
files = directory::files(path, "*.filter");
|
||||
}
|
||||
reference_array<RadioItem&> group;
|
||||
array<RadioItem&> group;
|
||||
|
||||
settingsVideoFilterList = new RadioItem[files.size()];
|
||||
for(unsigned n = 0; n < files.size(); n++) {
|
||||
|
@ -369,7 +369,7 @@ void MainWindow::setupVideoShaders() {
|
|||
path = { application->userpath, "shaders/" };
|
||||
files = directory::files(path, { "*.", config->video.driver, ".shader" });
|
||||
}
|
||||
reference_array<RadioItem&> group;
|
||||
array<RadioItem&> group;
|
||||
|
||||
settingsVideoShaderList = new RadioItem[files.size()];
|
||||
for(unsigned n = 0; n < files.size(); n++) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
SlotLoader *slotLoader = 0;
|
||||
SlotLoader *slotLoader = nullptr;
|
||||
|
||||
SlotLoaderPath::SlotLoaderPath() {
|
||||
browse.setText("Browse ...");
|
||||
|
|
|
@ -27,7 +27,7 @@ struct TurboInput : DigitalInput {
|
|||
TurboInput();
|
||||
};
|
||||
|
||||
struct TertiaryInput : reference_array<AbstractInput&> {
|
||||
struct TertiaryInput : array<AbstractInput&> {
|
||||
string name;
|
||||
|
||||
virtual void attach(const string &primaryName, const string &secondaryName);
|
||||
|
@ -35,14 +35,14 @@ struct TertiaryInput : reference_array<AbstractInput&> {
|
|||
virtual int16_t poll(unsigned n);
|
||||
};
|
||||
|
||||
struct SecondaryInput : reference_array<TertiaryInput&> {
|
||||
struct SecondaryInput : array<TertiaryInput&> {
|
||||
string name;
|
||||
|
||||
virtual void attach(const string &primaryName);
|
||||
virtual void bind();
|
||||
};
|
||||
|
||||
struct PrimaryInput : reference_array<SecondaryInput&> {
|
||||
struct PrimaryInput : array<SecondaryInput&> {
|
||||
string name;
|
||||
|
||||
virtual void attach();
|
||||
|
@ -59,7 +59,7 @@ struct InputManager {
|
|||
int16_t scancode[2][Scancode::Limit];
|
||||
bool activeScancode;
|
||||
|
||||
reference_array<PrimaryInput&> inputList;
|
||||
array<PrimaryInput&> inputList;
|
||||
NesInput nes;
|
||||
SnesInput snes;
|
||||
GameBoyInput gameBoy;
|
||||
|
|
|
@ -10,10 +10,20 @@ bool InterfaceGameBoy::cartridgeLoaded() {
|
|||
bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(interface->loadFile(filename, data, size) == false) return false;
|
||||
|
||||
if(filename.endswith("/")) {
|
||||
string basename = filename;
|
||||
basename.rtrim<1>("/");
|
||||
basename.append("/", notdir(basename));
|
||||
if(file::read(basename, data, size) == false) return false;
|
||||
interface->baseName = nall::basename(basename);
|
||||
} else {
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
interface->baseName = nall::basename(filename);
|
||||
}
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = nall::basename(filename);
|
||||
interface->applyPatch(interface->baseName, data, size);
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
|
|
|
@ -188,11 +188,9 @@ Interface::Interface() : core(nullptr) {
|
|||
|
||||
//internal
|
||||
|
||||
bool Interface::loadFile(const string &filename, uint8_t *&data, unsigned &size) {
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
|
||||
bool Interface::applyPatch(const string &filename, uint8_t *&data, unsigned &size) {
|
||||
string patchname = { nall::basename(filename), ".bps" };
|
||||
if(file::exists(patchname) == false) return true;
|
||||
if(file::exists(patchname) == false) return false;
|
||||
|
||||
bpspatch bps;
|
||||
bps.modify(patchname);
|
||||
|
@ -202,7 +200,7 @@ bool Interface::loadFile(const string &filename, uint8_t *&data, unsigned &size)
|
|||
bps.target(targetData, targetSize);
|
||||
if(bps.apply() != bpspatch::result::success) {
|
||||
delete[] targetData;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
|
|
|
@ -55,7 +55,7 @@ struct Interface : property<Interface> {
|
|||
|
||||
Interface();
|
||||
|
||||
bool loadFile(const string &filename, uint8_t *&data, unsigned &size);
|
||||
bool applyPatch(const string &filename, uint8_t *&data, unsigned &size);
|
||||
void videoRefresh(const uint32_t *input, unsigned inputPitch, unsigned width, unsigned height);
|
||||
|
||||
string baseName; // = "/path/to/cartridge" (no extension)
|
||||
|
|
|
@ -25,10 +25,37 @@ bool InterfaceNES::cartridgeLoaded() {
|
|||
bool InterfaceNES::loadCartridge(const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(interface->loadFile(filename, data, size) == false) return false;
|
||||
|
||||
if(filename.endswith("/")) {
|
||||
string basename = filename;
|
||||
basename.rtrim<1>("/");
|
||||
basename.append("/", notdir(nall::basename(basename)));
|
||||
|
||||
if(file::exists({ basename, ".prg" }) && file::exists({ basename, ".chr" })) {
|
||||
unsigned prgsize = file::size({ basename, ".prg" });
|
||||
unsigned chrsize = file::size({ basename, ".chr" });
|
||||
data = new uint8_t[size = prgsize + chrsize];
|
||||
nall::file fp;
|
||||
fp.open({ basename, ".prg" }, file::mode::read);
|
||||
fp.read(data, fp.size());
|
||||
fp.close();
|
||||
fp.open({ basename, ".chr" }, file::mode::read);
|
||||
fp.read(data + prgsize, fp.size());
|
||||
fp.close();
|
||||
} else if(file::exists({ basename, ".fc" })) {
|
||||
file::read({ basename, ".fc" }, data, size);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
interface->baseName = basename;
|
||||
} else {
|
||||
file::read(filename, data, size);
|
||||
interface->baseName = nall::basename(filename);
|
||||
}
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = nall::basename(filename);
|
||||
interface->applyPatch(interface->baseName, data, size);
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
|
|
|
@ -30,14 +30,25 @@ bool InterfaceSNES::cartridgeLoaded() {
|
|||
return SNES::cartridge.loaded();
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadCartridge(const string &basename) {
|
||||
bool InterfaceSNES::loadFile(string &filename, uint8_t *&data, unsigned &size) {
|
||||
if(filename.endswith("/")) {
|
||||
filename.rtrim<1>("/");
|
||||
filename.append("/", notdir(filename));
|
||||
}
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
filename = nall::basename(filename);
|
||||
interface->applyPatch(filename, data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadCartridge(string basename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(interface->loadFile(basename, data, size) == false) return false;
|
||||
if(loadFile(basename, data, size) == false) return false;
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = nall::basename(basename);
|
||||
interface->slotName = { nall::basename(basename) };
|
||||
interface->baseName = basename;
|
||||
interface->slotName = { basename };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
|
@ -55,16 +66,16 @@ bool InterfaceSNES::loadCartridge(const string &basename) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, const string &slotname) {
|
||||
bool InterfaceSNES::loadSatellaviewSlottedCartridge(string basename, string slotname) {
|
||||
uint8_t *data[2];
|
||||
unsigned size[2];
|
||||
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
||||
interface->loadFile(slotname, data[1], size[1]);
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotname, data[1], size[1]);
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = nall::basename(basename);
|
||||
if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname)));
|
||||
interface->slotName = { nall::basename(basename), nall::basename(slotname) };
|
||||
interface->baseName = basename;
|
||||
if(data[1]) interface->baseName.append("+", notdir(slotname));
|
||||
interface->slotName = { basename, slotname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
|
@ -84,16 +95,16 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, cons
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const string &slotname) {
|
||||
bool InterfaceSNES::loadSatellaviewCartridge(string basename, string slotname) {
|
||||
uint8_t *data[2];
|
||||
unsigned size[2];
|
||||
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
||||
interface->loadFile(slotname, data[1], size[1]);
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotname, data[1], size[1]);
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = nall::basename(basename);
|
||||
if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname)));
|
||||
interface->slotName = { nall::basename(basename), nall::basename(slotname) };
|
||||
interface->baseName = basename;
|
||||
if(data[1]) interface->baseName.append("+", notdir(slotname));
|
||||
interface->slotName = { basename, slotname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
|
@ -113,19 +124,19 @@ bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const strin
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const string &slotAname, const string &slotBname) {
|
||||
bool InterfaceSNES::loadSufamiTurboCartridge(string basename, string slotAname, string slotBname) {
|
||||
uint8_t *data[3];
|
||||
unsigned size[3];
|
||||
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
||||
interface->loadFile(slotAname, data[1], size[1]);
|
||||
interface->loadFile(slotBname, data[2], size[2]);
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotAname, data[1], size[1]);
|
||||
loadFile(slotBname, data[2], size[2]);
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = nall::basename(basename);
|
||||
if(data[1] && data[2]) interface->baseName = { nall::basename(slotAname), "+", nall::basename(notdir(slotBname)) };
|
||||
else if(data[1]) interface->baseName = nall::basename(slotAname);
|
||||
else if(data[2]) interface->baseName = nall::basename(slotBname);
|
||||
interface->slotName = { nall::basename(basename), nall::basename(slotAname), nall::basename(slotBname) };
|
||||
interface->baseName = basename;
|
||||
if(data[1] && data[2]) interface->baseName = { slotAname, "+", notdir(slotBname) };
|
||||
else if(data[1]) interface->baseName = slotAname;
|
||||
else if(data[2]) interface->baseName = slotBname;
|
||||
interface->slotName = { basename, slotAname, slotBname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
|
@ -147,16 +158,16 @@ bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const strin
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const string &slotname) {
|
||||
bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname) {
|
||||
uint8_t *data[2];
|
||||
unsigned size[2];
|
||||
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
||||
interface->loadFile(slotname, data[1], size[1]);
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotname, data[1], size[1]);
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = nall::basename(basename);
|
||||
if(data[1]) interface->baseName = nall::basename(slotname);
|
||||
interface->slotName = { nall::basename(basename), nall::basename(slotname) };
|
||||
interface->baseName = basename;
|
||||
if(data[1]) interface->baseName = slotname;
|
||||
interface->slotName = { basename, slotname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
|
|
|
@ -4,11 +4,12 @@ struct InterfaceSNES : InterfaceCore, SNES::Interface {
|
|||
void setController(bool port, unsigned device);
|
||||
|
||||
bool cartridgeLoaded();
|
||||
bool loadCartridge(const string &filename);
|
||||
bool loadSatellaviewSlottedCartridge(const string &basename, const string &slotname);
|
||||
bool loadSatellaviewCartridge(const string &basename, const string &slotname);
|
||||
bool loadSufamiTurboCartridge(const string &basename, const string &slotAname, const string &slotBname);
|
||||
bool loadSuperGameBoyCartridge(const string &basename, const string &slotname);
|
||||
bool loadFile(string &filename, uint8_t *&data, unsigned &size);
|
||||
bool loadCartridge(string basename);
|
||||
bool loadSatellaviewSlottedCartridge(string basename, string slotname);
|
||||
bool loadSatellaviewCartridge(string basename, string slotname);
|
||||
bool loadSufamiTurboCartridge(string basename, string slotAname, string slotBname);
|
||||
bool loadSuperGameBoyCartridge(string basename, string slotname);
|
||||
void unloadCartridge();
|
||||
|
||||
void power();
|
||||
|
|
|
@ -27,7 +27,7 @@ void Application::run() {
|
|||
}
|
||||
|
||||
Application::Application(int argc, char **argv) {
|
||||
title = "bsnes v085.01";
|
||||
title = "bsnes v085.02";
|
||||
|
||||
application = this;
|
||||
quit = false;
|
||||
|
|
Loading…
Reference in New Issue