mirror of https://github.com/bsnes-emu/bsnes.git
Update to v074r06 release.
byuu says: This WIP uses the 16MB+64MB memory tables for 1:1 mapping of the S-CPU bus. Minimum RAM requirement goes up to 128MB, dare I be bold here and recommend you have 256MB. I also hooked up the basic bindings for making pseudo-fullscreen windows in phoenix/Windows and phoenix/GTK+, but both have some serious issues. - GTK+ won't resize my form container, even though it's the same code I used successfully in bsnes v039 and prior with hiro - Windows scale selection breaks the faux-fullscreen effects - I am intending to write off the menu/status bars and just auto-size the video to fill the screen, nice and simple
This commit is contained in:
parent
ea077a7d96
commit
1a065bafb1
|
@ -107,6 +107,8 @@ struct Window : Widget {
|
|||
void setStatusText(const nall::string &text);
|
||||
void setMenuVisible(bool visible = true);
|
||||
void setStatusVisible(bool visible = true);
|
||||
bool fullscreen();
|
||||
void setFullscreen(bool fullscreen = true);
|
||||
Window();
|
||||
//private:
|
||||
struct Data;
|
||||
|
|
|
@ -27,6 +27,11 @@ struct Widget::Data {
|
|||
|
||||
struct Window::Data {
|
||||
Font *defaultFont;
|
||||
bool isFullscreen;
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
};
|
||||
|
||||
struct Canvas::Data {
|
||||
|
|
|
@ -8,6 +8,11 @@ static gint Window_close(Window *window) {
|
|||
}
|
||||
|
||||
void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
|
||||
window->x = x;
|
||||
window->y = y;
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
|
||||
object->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_move(GTK_WINDOW(object->widget), x, y);
|
||||
|
||||
|
@ -55,8 +60,8 @@ Geometry Window::geometry() {
|
|||
}
|
||||
|
||||
void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
|
||||
gtk_window_move(GTK_WINDOW(object->widget), x, y);
|
||||
gtk_widget_set_size_request(object->formContainer, width, height);
|
||||
gtk_window_move(GTK_WINDOW(object->widget), window->x = x, window->y = y);
|
||||
gtk_widget_set_size_request(object->formContainer, window->width = width, window->height = height);
|
||||
}
|
||||
|
||||
void Window::setDefaultFont(Font &font) {
|
||||
|
@ -93,7 +98,29 @@ void Window::setStatusVisible(bool visible) {
|
|||
gtk_widget_set_visible(object->status, visible);
|
||||
}
|
||||
|
||||
bool Window::fullscreen() {
|
||||
return window->isFullscreen;
|
||||
}
|
||||
|
||||
void Window::setFullscreen(bool fullscreen) {
|
||||
window->isFullscreen = fullscreen;
|
||||
if(fullscreen == true) {
|
||||
gtk_window_fullscreen(GTK_WINDOW(object->widget));
|
||||
gtk_window_set_decorated(GTK_WINDOW(object->widget), false);
|
||||
gtk_widget_set_size_request(object->widget, gdk_screen_width(), gdk_screen_height());
|
||||
} else {
|
||||
gtk_widget_set_size_request(object->widget, -1, -1);
|
||||
gtk_window_set_decorated(GTK_WINDOW(object->widget), true);
|
||||
gtk_window_unfullscreen(GTK_WINDOW(object->widget));
|
||||
}
|
||||
}
|
||||
|
||||
Window::Window() {
|
||||
window = new Window::Data;
|
||||
window->defaultFont = 0;
|
||||
window->isFullscreen = false;
|
||||
window->x = 0;
|
||||
window->y = 0;
|
||||
window->width = 0;
|
||||
window->height = 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
# Makefile
|
||||
# author: byuu
|
||||
# license: public domain
|
||||
|
||||
[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
|
||||
[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||
[0-9] = 0 1 2 3 4 5 6 7 8 9
|
||||
[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ?
|
||||
[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup])
|
||||
[space] :=
|
||||
[space] +=
|
||||
|
||||
#####
|
||||
# platform detection
|
||||
#####
|
||||
|
||||
ifeq ($(platform),)
|
||||
uname := $(shell uname -a)
|
||||
ifeq ($(uname),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
else ifneq ($(findstring Darwin,$(uname)),)
|
||||
platform := osx
|
||||
delete = rm -f $1
|
||||
else
|
||||
platform := x
|
||||
delete = rm -f $1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(compiler),)
|
||||
ifeq ($(platform),win)
|
||||
compiler := gcc
|
||||
else ifeq ($(platform),osx)
|
||||
compiler := gcc-mp-4.5
|
||||
else
|
||||
compiler := gcc-4.5
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(prefix),)
|
||||
prefix := /usr/local
|
||||
endif
|
||||
|
||||
#####
|
||||
# function rwildcard(directory, pattern)
|
||||
#####
|
||||
rwildcard = \
|
||||
$(strip \
|
||||
$(filter $(if $2,$2,%), \
|
||||
$(foreach f, \
|
||||
$(wildcard $1*), \
|
||||
$(eval t = $(call rwildcard,$f/)) \
|
||||
$(if $t,$t,$f) \
|
||||
) \
|
||||
) \
|
||||
)
|
||||
|
||||
#####
|
||||
# function strtr(source, from, to)
|
||||
#####
|
||||
strtr = \
|
||||
$(eval __temp := $1) \
|
||||
$(strip \
|
||||
$(foreach c, \
|
||||
$(join $(addsuffix :,$2),$3), \
|
||||
$(eval __temp := \
|
||||
$(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \
|
||||
) \
|
||||
) \
|
||||
$(__temp) \
|
||||
)
|
||||
|
||||
#####
|
||||
# function strupper(source)
|
||||
#####
|
||||
strupper = $(call strtr,$1,$([a-z]),$([A-Z]))
|
||||
|
||||
#####
|
||||
# function strlower(source)
|
||||
#####
|
||||
strlower = $(call strtr,$1,$([A-Z]),$([a-z]))
|
||||
|
||||
#####
|
||||
# function strlen(source)
|
||||
#####
|
||||
strlen = \
|
||||
$(eval __temp := $(subst $([space]),_,$1)) \
|
||||
$(words \
|
||||
$(strip \
|
||||
$(foreach c, \
|
||||
$([all]), \
|
||||
$(eval __temp := \
|
||||
$(subst $c,$c ,$(__temp)) \
|
||||
) \
|
||||
) \
|
||||
$(__temp) \
|
||||
) \
|
||||
)
|
||||
|
||||
#####
|
||||
# function streq(source)
|
||||
#####
|
||||
streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1)
|
||||
|
||||
#####
|
||||
# function strne(source)
|
||||
#####
|
||||
strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,)
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef NALL_ALGORITHM_HPP
|
||||
#define NALL_ALGORITHM_HPP
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
namespace nall {
|
||||
template<typename T, typename U> T min(const T &t, const U &u) {
|
||||
return t < u ? t : u;
|
||||
}
|
||||
|
||||
template<typename T, typename U> T max(const T &t, const U &u) {
|
||||
return t > u ? t : u;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef NALL_ANY_HPP
|
||||
#define NALL_ANY_HPP
|
||||
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <nall/static.hpp>
|
||||
|
||||
namespace nall {
|
||||
class any {
|
||||
public:
|
||||
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<
|
||||
std::is_array<T>::value,
|
||||
typename std::remove_extent<typename std::add_const<T>::type>::type*,
|
||||
T
|
||||
>::type auto_t;
|
||||
|
||||
if(type() == typeid(auto_t)) {
|
||||
static_cast<holder<auto_t>*>(container)->value = (auto_t)value_;
|
||||
} else {
|
||||
if(container) delete container;
|
||||
container = new holder<auto_t>((auto_t)value_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
any() : container(0) {}
|
||||
template<typename T> any(const T& value_) : container(0) { operator=(value_); }
|
||||
|
||||
private:
|
||||
struct placeholder {
|
||||
virtual const std::type_info& type() const = 0;
|
||||
} *container;
|
||||
|
||||
template<typename T> struct holder : placeholder {
|
||||
T value;
|
||||
const std::type_info& type() const { return typeid(T); }
|
||||
holder(const T& value_) : value(value_) {}
|
||||
};
|
||||
|
||||
template<typename T> friend T any_cast(any&);
|
||||
template<typename T> friend T any_cast(const any&);
|
||||
template<typename T> friend T* any_cast(any*);
|
||||
template<typename T> friend const T* any_cast(const any*);
|
||||
};
|
||||
|
||||
template<typename T> T any_cast(any &value) {
|
||||
typedef typename std::remove_reference<T>::type nonref;
|
||||
if(value.type() != typeid(nonref)) throw;
|
||||
return static_cast<any::holder<nonref>*>(value.container)->value;
|
||||
}
|
||||
|
||||
template<typename T> T any_cast(const any &value) {
|
||||
typedef const typename std::remove_reference<T>::type nonref;
|
||||
if(value.type() != typeid(nonref)) throw;
|
||||
return static_cast<any::holder<nonref>*>(value.container)->value;
|
||||
}
|
||||
|
||||
template<typename T> T* any_cast(any *value) {
|
||||
if(!value || value->type() != typeid(T)) return 0;
|
||||
return &static_cast<any::holder<T>*>(value->container)->value;
|
||||
}
|
||||
|
||||
template<typename T> const T* any_cast(const any *value) {
|
||||
if(!value || value->type() != typeid(T)) return 0;
|
||||
return &static_cast<any::holder<T>*>(value->container)->value;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,141 @@
|
|||
#ifndef NALL_ARRAY_HPP
|
||||
#define NALL_ARRAY_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/concept.hpp>
|
||||
#include <nall/foreach.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; }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = 0;
|
||||
poolsize = 0;
|
||||
buffersize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
if(newsize == poolsize) return;
|
||||
|
||||
pool = (T*)realloc(pool, newsize * sizeof(T));
|
||||
poolsize = newsize;
|
||||
buffersize = min(buffersize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
|
||||
buffersize = newsize;
|
||||
}
|
||||
|
||||
T* get(unsigned minsize = 0) {
|
||||
if(minsize > buffersize) resize(minsize);
|
||||
if(minsize > buffersize) throw "array[] out of bounds";
|
||||
return pool;
|
||||
}
|
||||
|
||||
void append(const T data) {
|
||||
operator[](buffersize) = data;
|
||||
}
|
||||
|
||||
template<typename U> void insert(unsigned index, const U list) {
|
||||
unsigned listsize = container_size(list);
|
||||
resize(buffersize + listsize);
|
||||
memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T));
|
||||
foreach(item, list) pool[index++] = item;
|
||||
}
|
||||
|
||||
void insert(unsigned index, const T item) {
|
||||
insert(index, array<T>{ item });
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(0), poolsize(0), buffersize(0) {
|
||||
}
|
||||
|
||||
array(std::initializer_list<T> list) : pool(0), 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(0), 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 = 0;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(array &&source) : pool(0), poolsize(0), buffersize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
//index
|
||||
inline T& operator[](unsigned index) {
|
||||
if(index >= buffersize) resize(index + 1);
|
||||
if(index >= buffersize) throw "array[] out of bounds";
|
||||
return pool[index];
|
||||
}
|
||||
|
||||
inline const T& operator[](unsigned index) const {
|
||||
if(index >= buffersize) throw "array[] out of bounds";
|
||||
return pool[index];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct has_size<array<T>> { enum { value = true }; };
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef NALL_BASE64_HPP
|
||||
#define NALL_BASE64_HPP
|
||||
|
||||
#include <string.h>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class base64 {
|
||||
public:
|
||||
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||
output = new char[inlength * 8 / 6 + 6]();
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
switch(i % 3) {
|
||||
case 0: {
|
||||
output[o++] = enc(input[i] >> 2);
|
||||
output[o] = enc((input[i] & 3) << 4);
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
uint8_t prev = dec(output[o]);
|
||||
output[o++] = enc(prev + (input[i] >> 4));
|
||||
output[o] = enc((input[i] & 15) << 2);
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
uint8_t prev = dec(output[o]);
|
||||
output[o++] = enc(prev + (input[i] >> 6));
|
||||
output[o++] = enc(input[i] & 63);
|
||||
} break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
|
||||
unsigned inlength = strlen(input), infix = 0;
|
||||
output = new uint8_t[inlength]();
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
uint8_t x = dec(input[i]);
|
||||
|
||||
switch(i++ & 3) {
|
||||
case 0: {
|
||||
output[o] = x << 2;
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
output[o++] |= x >> 4;
|
||||
output[o] = (x & 15) << 4;
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
output[o++] |= x >> 2;
|
||||
output[o] = (x & 3) << 6;
|
||||
} break;
|
||||
|
||||
case 3: {
|
||||
output[o++] |= x;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
outlength = o;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static char enc(uint8_t n) {
|
||||
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
return lookup_table[n & 63];
|
||||
}
|
||||
|
||||
static uint8_t dec(char n) {
|
||||
if(n >= 'A' && n <= 'Z') return n - 'A';
|
||||
if(n >= 'a' && n <= 'z') return n - 'a' + 26;
|
||||
if(n >= '0' && n <= '9') return n - '0' + 52;
|
||||
if(n == '-') return 62;
|
||||
if(n == '_') return 63;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef NALL_BIT_HPP
|
||||
#define NALL_BIT_HPP
|
||||
|
||||
namespace nall {
|
||||
template<int bits> inline unsigned uclamp(const unsigned x) {
|
||||
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||
return y + ((x - y) & -(x < y)); //min(x, y);
|
||||
}
|
||||
|
||||
template<int bits> inline unsigned uclip(const unsigned x) {
|
||||
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||
return (x & m);
|
||||
}
|
||||
|
||||
template<int bits> inline signed sclamp(const signed x) {
|
||||
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
|
||||
return (x > m) ? m : (x < -b) ? -b : x;
|
||||
}
|
||||
|
||||
template<int bits> inline signed sclip(const signed x) {
|
||||
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
|
||||
return ((x & m) ^ b) - b;
|
||||
}
|
||||
|
||||
namespace bit {
|
||||
//lowest(0b1110) == 0b0010
|
||||
template<typename T> inline T lowest(const T x) {
|
||||
return x & -x;
|
||||
}
|
||||
|
||||
//clear_lowest(0b1110) == 0b1100
|
||||
template<typename T> inline T clear_lowest(const T x) {
|
||||
return x & (x - 1);
|
||||
}
|
||||
|
||||
//set_lowest(0b0101) == 0b0111
|
||||
template<typename T> inline T set_lowest(const T x) {
|
||||
return x | (x + 1);
|
||||
}
|
||||
|
||||
//round up to next highest single bit:
|
||||
//round(15) == 16, round(16) == 16, round(17) == 32
|
||||
inline unsigned round(unsigned x) {
|
||||
if((x & (x - 1)) == 0) return x;
|
||||
while(x & (x - 1)) x &= x - 1;
|
||||
return x << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef NALL_CONCEPT_HPP
|
||||
#define NALL_CONCEPT_HPP
|
||||
|
||||
#include <nall/static.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
//unsigned count() const;
|
||||
template<typename T> struct has_count { enum { value = false }; };
|
||||
|
||||
//unsigned length() const;
|
||||
template<typename T> struct has_length { enum { value = false }; };
|
||||
|
||||
//unsigned size() const;
|
||||
template<typename T> struct has_size { enum { value = false }; };
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_count<T>>::type = 0) {
|
||||
return object.count();
|
||||
}
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_length<T>>::type = 0) {
|
||||
return object.length();
|
||||
}
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_size<T>>::type = 0) {
|
||||
return object.size();
|
||||
}
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<std::is_array<T>>::type = 0) {
|
||||
return sizeof(T) / sizeof(typename std::remove_extent<T>::type);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,123 @@
|
|||
#ifndef NALL_CONFIG_HPP
|
||||
#define NALL_CONFIG_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
namespace configuration_traits {
|
||||
template<typename T> struct is_boolean { enum { value = false }; };
|
||||
template<> struct is_boolean<bool> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_signed { enum { value = false }; };
|
||||
template<> struct is_signed<signed> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_unsigned { enum { value = false }; };
|
||||
template<> struct is_unsigned<unsigned> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_double { enum { value = false }; };
|
||||
template<> struct is_double<double> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_string { enum { value = false }; };
|
||||
template<> struct is_string<string> { enum { value = true }; };
|
||||
}
|
||||
|
||||
class configuration {
|
||||
public:
|
||||
enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t };
|
||||
struct item_t {
|
||||
uintptr_t data;
|
||||
string name;
|
||||
string desc;
|
||||
type_t type;
|
||||
|
||||
string get() const {
|
||||
switch(type) {
|
||||
case boolean_t: return string() << *(bool*)data;
|
||||
case signed_t: return string() << *(signed*)data;
|
||||
case unsigned_t: return string() << *(unsigned*)data;
|
||||
case double_t: return string() << *(double*)data;
|
||||
case string_t: return string() << "\"" << *(string*)data << "\"";
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
void set(string s) {
|
||||
switch(type) {
|
||||
case boolean_t: *(bool*)data = (s == "true"); break;
|
||||
case signed_t: *(signed*)data = integer(s); break;
|
||||
case unsigned_t: *(unsigned*)data = decimal(s); break;
|
||||
case double_t: *(double*)data = fp(s); break;
|
||||
case string_t: s.trim("\""); *(string*)data = s; break;
|
||||
}
|
||||
}
|
||||
};
|
||||
linear_vector<item_t> list;
|
||||
|
||||
template<typename T>
|
||||
void attach(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;
|
||||
}
|
||||
|
||||
virtual bool load(const char *filename) {
|
||||
string data;
|
||||
if(data.readfile(filename) == true) {
|
||||
data.replace("\r", "");
|
||||
lstring line;
|
||||
line.split("\n", data);
|
||||
|
||||
for(unsigned i = 0; i < line.size(); i++) {
|
||||
if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0;
|
||||
if(!qstrpos(line[i], " = ")) continue;
|
||||
|
||||
lstring part;
|
||||
part.qsplit(" = ", line[i]);
|
||||
part[0].trim();
|
||||
part[1].trim();
|
||||
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
if(part[0] == list[n].name) {
|
||||
list[n].set(part[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool save(const char *filename) const {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write)) {
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
string output;
|
||||
output << list[i].name << " = " << list[i].get();
|
||||
if(list[i].desc != "") output << " # " << list[i].desc;
|
||||
output << "\r\n";
|
||||
fp.print(output);
|
||||
}
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef NALL_CRC32_HPP
|
||||
#define NALL_CRC32_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
const uint32_t crc32_table[256] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) {
|
||||
return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff];
|
||||
}
|
||||
|
||||
inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) {
|
||||
uint32_t crc32 = ~0;
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
crc32 = crc32_adjust(crc32, data[i]);
|
||||
}
|
||||
return ~crc32;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef NALL_DETECT_HPP
|
||||
#define NALL_DETECT_HPP
|
||||
|
||||
/* Compiler detection */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define COMPILER_GCC
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_VISUALC
|
||||
#endif
|
||||
|
||||
/* Platform detection */
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define PLATFORM_WIN
|
||||
#elif defined(__APPLE__)
|
||||
#define PLATFORM_OSX
|
||||
#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define PLATFORM_X
|
||||
#endif
|
||||
|
||||
/* Endian detection */
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
|
||||
#define ARCH_LSB
|
||||
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
|
||||
#define ARCH_MSB
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef NALL_DICTIONARY_HPP
|
||||
#define NALL_DICTIONARY_HPP
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
class dictionary {
|
||||
public:
|
||||
string operator[](const char *input) {
|
||||
for(unsigned i = 0; i < index_input.size(); i++) {
|
||||
if(index_input[i] == input) return index_output[i];
|
||||
}
|
||||
|
||||
//no match, use input; remove input identifier, if one exists
|
||||
if(strbegin(input, "{{")) {
|
||||
if(auto pos = strpos(input, "}}")) {
|
||||
string temp = substr(input, pos() + 2);
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
bool import(const char *filename) {
|
||||
string data;
|
||||
if(data.readfile(filename) == false) return false;
|
||||
data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
|
||||
data.replace("\r", "");
|
||||
|
||||
lstring line;
|
||||
line.split("\n", data);
|
||||
for(unsigned i = 0; i < line.size(); i++) {
|
||||
lstring part;
|
||||
//format: "Input" = "Output"
|
||||
part.qsplit("=", line[i]);
|
||||
if(part.size() != 2) continue;
|
||||
|
||||
//remove whitespace
|
||||
part[0].trim();
|
||||
part[1].trim();
|
||||
|
||||
//remove quotes
|
||||
part[0].trim<1>("\"");
|
||||
part[1].trim<1>("\"");
|
||||
|
||||
unsigned n = index_input.size();
|
||||
index_input[n] = part[0];
|
||||
index_output[n] = part[1];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
index_input.reset();
|
||||
index_output.reset();
|
||||
}
|
||||
|
||||
~dictionary() {
|
||||
reset();
|
||||
}
|
||||
|
||||
dictionary& operator=(const dictionary&) = delete;
|
||||
dictionary(const dictionary&) = delete;
|
||||
|
||||
protected:
|
||||
lstring index_input;
|
||||
lstring index_output;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,151 @@
|
|||
#ifndef NALL_DIRECTORY_HPP
|
||||
#define NALL_DIRECTORY_HPP
|
||||
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <nall/utf8.hpp>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct directory {
|
||||
static bool exists(const string &pathname);
|
||||
static lstring folders(const string &pathname, const string &pattern = "*");
|
||||
static lstring files(const string &pathname, const string &pattern = "*");
|
||||
static lstring contents(const string &pathname, const string &pattern = "*");
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
inline bool directory::exists(const string &pathname) {
|
||||
DWORD result = GetFileAttributes(utf16_t(pathname));
|
||||
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
||||
return (result & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
inline lstring directory::folders(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
if(!strend(path, "\\")) path.append("\\");
|
||||
path.append("*");
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATA data;
|
||||
handle = FindFirstFile(utf16_t(path), &data);
|
||||
if(handle != INVALID_HANDLE_VALUE) {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = utf8_t(data.cFileName);
|
||||
if(wildcard(name, pattern)) list.append(string(name, "/"));
|
||||
}
|
||||
}
|
||||
while(FindNextFile(handle, &data) != false) {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = utf8_t(data.cFileName);
|
||||
if(wildcard(name, pattern)) list.append(string(name, "/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::files(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
if(!strend(path, "\\")) path.append("\\");
|
||||
path.append("*");
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATA data;
|
||||
handle = FindFirstFile(utf16_t(path), &data);
|
||||
if(handle != INVALID_HANDLE_VALUE) {
|
||||
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
string name = utf8_t(data.cFileName);
|
||||
if(wildcard(name, pattern)) list.append(name);
|
||||
}
|
||||
while(FindNextFile(handle, &data) != false) {
|
||||
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
string name = utf8_t(data.cFileName);
|
||||
if(wildcard(name, pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#else
|
||||
inline bool directory::exists(const string &pathname) {
|
||||
DIR *dp = opendir(pathname);
|
||||
if(!dp) return false;
|
||||
closedir(dp);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline lstring directory::folders(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
dp = opendir(pathname);
|
||||
if(dp) {
|
||||
while(ep = readdir(dp)) {
|
||||
if(!strcmp(ep->d_name, ".")) continue;
|
||||
if(!strcmp(ep->d_name, "..")) continue;
|
||||
if(ep->d_type & DT_DIR) {
|
||||
if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/"));
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
inline lstring directory::files(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
dp = opendir(pathname);
|
||||
if(dp) {
|
||||
while(ep = readdir(dp)) {
|
||||
if(!strcmp(ep->d_name, ".")) continue;
|
||||
if(!strcmp(ep->d_name, "..")) continue;
|
||||
if((ep->d_type & DT_DIR) == 0) {
|
||||
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef NALL_DL_HPP
|
||||
#define NALL_DL_HPP
|
||||
|
||||
//dynamic linking support
|
||||
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
|
||||
#include <dlfcn.h>
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#include <windows.h>
|
||||
#include <nall/utf8.hpp>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
struct library {
|
||||
bool opened() const { return handle; }
|
||||
bool open(const char*, const char* = "");
|
||||
bool open_absolute(const char*);
|
||||
void* sym(const char*);
|
||||
void close();
|
||||
|
||||
library() : handle(0) {}
|
||||
~library() { close(); }
|
||||
|
||||
library& operator=(const library&) = delete;
|
||||
library(const library&) = delete;
|
||||
|
||||
private:
|
||||
uintptr_t handle;
|
||||
};
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
inline bool library::open(const char *name, const char *path) {
|
||||
if(handle) close();
|
||||
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY);
|
||||
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY);
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline bool library::open_absolute(const char *name) {
|
||||
if(handle) close();
|
||||
handle = (uintptr_t)dlopen(name, RTLD_LAZY);
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline void* library::sym(const char *name) {
|
||||
if(!handle) return 0;
|
||||
return dlsym((void*)handle, name);
|
||||
}
|
||||
|
||||
inline void library::close() {
|
||||
if(!handle) return;
|
||||
dlclose((void*)handle);
|
||||
handle = 0;
|
||||
}
|
||||
#elif defined(PLATFORM_OSX)
|
||||
inline bool library::open(const char *name, const char *path) {
|
||||
if(handle) close();
|
||||
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY);
|
||||
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY);
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline bool library::open_absolute(const char *name) {
|
||||
if(handle) close();
|
||||
handle = (uintptr_t)dlopen(name, RTLD_LAZY);
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline void* library::sym(const char *name) {
|
||||
if(!handle) return 0;
|
||||
return dlsym((void*)handle, name);
|
||||
}
|
||||
|
||||
inline void library::close() {
|
||||
if(!handle) return;
|
||||
dlclose((void*)handle);
|
||||
handle = 0;
|
||||
}
|
||||
#elif defined(PLATFORM_WIN)
|
||||
inline bool library::open(const char *name, const char *path) {
|
||||
if(handle) close();
|
||||
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
|
||||
handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline bool library::open_absolute(const char *name) {
|
||||
if(handle) close();
|
||||
handle = (uintptr_t)LoadLibraryW(utf16_t(name));
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline void* library::sym(const char *name) {
|
||||
if(!handle) return 0;
|
||||
return (void*)GetProcAddress((HMODULE)handle, name);
|
||||
}
|
||||
|
||||
inline void library::close() {
|
||||
if(!handle) return;
|
||||
FreeLibrary((HMODULE)handle);
|
||||
handle = 0;
|
||||
}
|
||||
#else
|
||||
inline bool library::open(const char*, const char*) { return false; }
|
||||
inline void* library::sym(const char*) { return 0; }
|
||||
inline void library::close() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef NALL_ENDIAN_HPP
|
||||
#define NALL_ENDIAN_HPP
|
||||
|
||||
#if !defined(ARCH_MSB)
|
||||
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
||||
#define order_lsb2(a,b) a,b
|
||||
#define order_lsb3(a,b,c) a,b,c
|
||||
#define order_lsb4(a,b,c,d) a,b,c,d
|
||||
#define order_lsb5(a,b,c,d,e) a,b,c,d,e
|
||||
#define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||
#define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||
#define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||
#define order_msb2(a,b) b,a
|
||||
#define order_msb3(a,b,c) c,b,a
|
||||
#define order_msb4(a,b,c,d) d,c,b,a
|
||||
#define order_msb5(a,b,c,d,e) e,d,c,b,a
|
||||
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||
#else
|
||||
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
||||
#define order_lsb2(a,b) b,a
|
||||
#define order_lsb3(a,b,c) c,b,a
|
||||
#define order_lsb4(a,b,c,d) d,c,b,a
|
||||
#define order_lsb5(a,b,c,d,e) e,d,c,b,a
|
||||
#define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||
#define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||
#define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||
#define order_msb2(a,b) a,b
|
||||
#define order_msb3(a,b,c) a,b,c
|
||||
#define order_msb4(a,b,c,d) a,b,c,d
|
||||
#define order_msb5(a,b,c,d,e) a,b,c,d,e
|
||||
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,261 @@
|
|||
#ifndef NALL_FILE_HPP
|
||||
#define NALL_FILE_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
|
||||
#if !defined(_WIN32)
|
||||
return fopen(utf8_filename, mode);
|
||||
#else
|
||||
return _wfopen(utf16_t(utf8_filename), utf16_t(mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
class file {
|
||||
public:
|
||||
enum class mode : unsigned { read, write, readwrite, writeread };
|
||||
enum class index : unsigned { absolute, relative };
|
||||
|
||||
uint8_t read() {
|
||||
if(!fp) return 0xff; //file not open
|
||||
if(file_mode == mode::write) return 0xff; //reads not permitted
|
||||
if(file_offset >= file_size) return 0xff; //cannot read past end of file
|
||||
buffer_sync();
|
||||
return buffer[(file_offset++) & buffer_mask];
|
||||
}
|
||||
|
||||
uintmax_t readl(unsigned length = 1) {
|
||||
uintmax_t data = 0;
|
||||
for(int i = 0; i < length; i++) {
|
||||
data |= (uintmax_t)read() << (i << 3);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
uintmax_t readm(unsigned length = 1) {
|
||||
uintmax_t data = 0;
|
||||
while(length--) {
|
||||
data <<= 8;
|
||||
data |= read();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void read(uint8_t *buffer, unsigned length) {
|
||||
while(length--) *buffer++ = read();
|
||||
}
|
||||
|
||||
void write(uint8_t data) {
|
||||
if(!fp) return; //file not open
|
||||
if(file_mode == mode::read) return; //writes not permitted
|
||||
buffer_sync();
|
||||
buffer[(file_offset++) & buffer_mask] = data;
|
||||
buffer_dirty = true;
|
||||
if(file_offset > file_size) file_size = file_offset;
|
||||
}
|
||||
|
||||
void writel(uintmax_t data, unsigned length = 1) {
|
||||
while(length--) {
|
||||
write(data);
|
||||
data >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
void writem(uintmax_t data, unsigned length = 1) {
|
||||
for(int i = length - 1; i >= 0; i--) {
|
||||
write(data >> (i << 3));
|
||||
}
|
||||
}
|
||||
|
||||
void write(const uint8_t *buffer, unsigned length) {
|
||||
while(length--) write(*buffer++);
|
||||
}
|
||||
|
||||
template<typename... Args> void print(Args... args) {
|
||||
string data(args...);
|
||||
const char *p = data;
|
||||
while(*p) write(*p++);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
buffer_flush();
|
||||
fflush(fp);
|
||||
}
|
||||
|
||||
void seek(int offset, index index_ = index::absolute) {
|
||||
if(!fp) return; //file not open
|
||||
buffer_flush();
|
||||
|
||||
uintmax_t req_offset = file_offset;
|
||||
switch(index_) {
|
||||
case index::absolute: req_offset = offset; break;
|
||||
case index::relative: req_offset += offset; break;
|
||||
}
|
||||
|
||||
if(req_offset < 0) req_offset = 0; //cannot seek before start of file
|
||||
if(req_offset > file_size) {
|
||||
if(file_mode == mode::read) { //cannot seek past end of file
|
||||
req_offset = file_size;
|
||||
} else { //pad file to requested location
|
||||
file_offset = file_size;
|
||||
while(file_size < req_offset) write(0x00);
|
||||
}
|
||||
}
|
||||
|
||||
file_offset = req_offset;
|
||||
}
|
||||
|
||||
int offset() {
|
||||
if(!fp) return -1; //file not open
|
||||
return file_offset;
|
||||
}
|
||||
|
||||
int size() {
|
||||
if(!fp) return -1; //file not open
|
||||
return file_size;
|
||||
}
|
||||
|
||||
bool truncate(unsigned size) {
|
||||
if(!fp) return false; //file not open
|
||||
#if !defined(_WIN32)
|
||||
return ftruncate(fileno(fp), size) == 0;
|
||||
#else
|
||||
return _chsize(fileno(fp), size) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool end() {
|
||||
if(!fp) return true; //file not open
|
||||
return file_offset >= file_size;
|
||||
}
|
||||
|
||||
static bool exists(const char *fn) {
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
#else
|
||||
FILE *fp = _wfopen(utf16_t(fn), L"rb");
|
||||
#endif
|
||||
if(fp) {
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned size(const char *fn) {
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
#else
|
||||
FILE *fp = _wfopen(utf16_t(fn), L"rb");
|
||||
#endif
|
||||
unsigned filesize = 0;
|
||||
if(fp) {
|
||||
fseek(fp, 0, SEEK_END);
|
||||
filesize = ftell(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
return filesize;
|
||||
}
|
||||
|
||||
bool open() {
|
||||
return fp;
|
||||
}
|
||||
|
||||
bool open(const char *fn, mode mode_) {
|
||||
if(fp) return false;
|
||||
|
||||
switch(file_mode = mode_) {
|
||||
#if !defined(_WIN32)
|
||||
case mode::read: fp = fopen(fn, "rb"); break;
|
||||
case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering
|
||||
case mode::readwrite: fp = fopen(fn, "rb+"); break;
|
||||
case mode::writeread: fp = fopen(fn, "wb+"); break;
|
||||
#else
|
||||
case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break;
|
||||
case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||||
case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
|
||||
case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||||
#endif
|
||||
}
|
||||
if(!fp) return false;
|
||||
buffer_offset = -1; //invalidate buffer
|
||||
file_offset = 0;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
file_size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
return true;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if(!fp) return;
|
||||
buffer_flush();
|
||||
fclose(fp);
|
||||
fp = 0;
|
||||
}
|
||||
|
||||
file() {
|
||||
memset(buffer, 0, sizeof buffer);
|
||||
buffer_offset = -1;
|
||||
buffer_dirty = false;
|
||||
fp = 0;
|
||||
file_offset = 0;
|
||||
file_size = 0;
|
||||
file_mode = mode::read;
|
||||
}
|
||||
|
||||
~file() {
|
||||
close();
|
||||
}
|
||||
|
||||
file& operator=(const file&) = delete;
|
||||
file(const file&) = delete;
|
||||
|
||||
private:
|
||||
enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 };
|
||||
char buffer[buffer_size];
|
||||
int buffer_offset;
|
||||
bool buffer_dirty;
|
||||
FILE *fp;
|
||||
unsigned file_offset;
|
||||
unsigned file_size;
|
||||
mode file_mode;
|
||||
|
||||
void buffer_sync() {
|
||||
if(!fp) return; //file not open
|
||||
if(buffer_offset != (file_offset & ~buffer_mask)) {
|
||||
buffer_flush();
|
||||
buffer_offset = file_offset & ~buffer_mask;
|
||||
fseek(fp, buffer_offset, SEEK_SET);
|
||||
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
|
||||
if(length) unsigned unused = fread(buffer, 1, length, fp);
|
||||
}
|
||||
}
|
||||
|
||||
void buffer_flush() {
|
||||
if(!fp) return; //file not open
|
||||
if(file_mode == mode::read) return; //buffer cannot be written to
|
||||
if(buffer_offset < 0) return; //buffer unused
|
||||
if(buffer_dirty == false) return; //buffer unmodified since read
|
||||
fseek(fp, buffer_offset, SEEK_SET);
|
||||
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
|
||||
if(length) unsigned unused = fwrite(buffer, 1, length, fp);
|
||||
buffer_offset = -1; //invalidate buffer
|
||||
buffer_dirty = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,200 @@
|
|||
#ifndef NALL_FILEMAP_HPP
|
||||
#define NALL_FILEMAP_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
class filemap {
|
||||
public:
|
||||
enum class mode : unsigned { read, write, readwrite, writeread };
|
||||
|
||||
bool opened() const { return p_opened(); }
|
||||
bool open(const char *filename, mode mode_) { return p_open(filename, mode_); }
|
||||
void close() { return p_close(); }
|
||||
unsigned size() const { return p_size; }
|
||||
uint8_t* data() { return p_handle; }
|
||||
const uint8_t* data() const { return p_handle; }
|
||||
filemap() : p_size(0), p_handle(0) { p_ctor(); }
|
||||
filemap(const char *filename, mode mode_) : p_size(0), p_handle(0) { p_ctor(); p_open(filename, mode_); }
|
||||
~filemap() { p_dtor(); }
|
||||
|
||||
private:
|
||||
unsigned p_size;
|
||||
uint8_t *p_handle;
|
||||
|
||||
#if defined(_WIN32)
|
||||
//=============
|
||||
//MapViewOfFile
|
||||
//=============
|
||||
|
||||
HANDLE p_filehandle, p_maphandle;
|
||||
|
||||
bool p_opened() const {
|
||||
return p_handle;
|
||||
}
|
||||
|
||||
bool p_open(const char *filename, mode mode_) {
|
||||
int desired_access, creation_disposition, flprotect, map_access;
|
||||
|
||||
switch(mode_) {
|
||||
default: return false;
|
||||
case mode::read:
|
||||
desired_access = GENERIC_READ;
|
||||
creation_disposition = OPEN_EXISTING;
|
||||
flprotect = PAGE_READONLY;
|
||||
map_access = FILE_MAP_READ;
|
||||
break;
|
||||
case mode::write:
|
||||
//write access requires read access
|
||||
desired_access = GENERIC_WRITE;
|
||||
creation_disposition = CREATE_ALWAYS;
|
||||
flprotect = PAGE_READWRITE;
|
||||
map_access = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
case mode::readwrite:
|
||||
desired_access = GENERIC_READ | GENERIC_WRITE;
|
||||
creation_disposition = OPEN_EXISTING;
|
||||
flprotect = PAGE_READWRITE;
|
||||
map_access = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
case mode::writeread:
|
||||
desired_access = GENERIC_READ | GENERIC_WRITE;
|
||||
creation_disposition = CREATE_NEW;
|
||||
flprotect = PAGE_READWRITE;
|
||||
map_access = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL,
|
||||
creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if(p_filehandle == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
p_size = GetFileSize(p_filehandle, NULL);
|
||||
|
||||
p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL);
|
||||
if(p_maphandle == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(p_filehandle);
|
||||
p_filehandle = INVALID_HANDLE_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size);
|
||||
return p_handle;
|
||||
}
|
||||
|
||||
void p_close() {
|
||||
if(p_handle) {
|
||||
UnmapViewOfFile(p_handle);
|
||||
p_handle = 0;
|
||||
}
|
||||
|
||||
if(p_maphandle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(p_maphandle);
|
||||
p_maphandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if(p_filehandle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(p_filehandle);
|
||||
p_filehandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
void p_ctor() {
|
||||
p_filehandle = INVALID_HANDLE_VALUE;
|
||||
p_maphandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
void p_dtor() {
|
||||
close();
|
||||
}
|
||||
|
||||
#else
|
||||
//====
|
||||
//mmap
|
||||
//====
|
||||
|
||||
int p_fd;
|
||||
|
||||
bool p_opened() const {
|
||||
return p_handle;
|
||||
}
|
||||
|
||||
bool p_open(const char *filename, mode mode_) {
|
||||
int open_flags, mmap_flags;
|
||||
|
||||
switch(mode_) {
|
||||
default: return false;
|
||||
case mode::read:
|
||||
open_flags = O_RDONLY;
|
||||
mmap_flags = PROT_READ;
|
||||
break;
|
||||
case mode::write:
|
||||
open_flags = O_RDWR | O_CREAT; //mmap() requires read access
|
||||
mmap_flags = PROT_WRITE;
|
||||
break;
|
||||
case mode::readwrite:
|
||||
open_flags = O_RDWR;
|
||||
mmap_flags = PROT_READ | PROT_WRITE;
|
||||
break;
|
||||
case mode::writeread:
|
||||
open_flags = O_RDWR | O_CREAT;
|
||||
mmap_flags = PROT_READ | PROT_WRITE;
|
||||
break;
|
||||
}
|
||||
|
||||
p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
if(p_fd < 0) return false;
|
||||
|
||||
struct stat p_stat;
|
||||
fstat(p_fd, &p_stat);
|
||||
p_size = p_stat.st_size;
|
||||
|
||||
p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0);
|
||||
if(p_handle == MAP_FAILED) {
|
||||
p_handle = 0;
|
||||
::close(p_fd);
|
||||
p_fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return p_handle;
|
||||
}
|
||||
|
||||
void p_close() {
|
||||
if(p_handle) {
|
||||
munmap(p_handle, p_size);
|
||||
p_handle = 0;
|
||||
}
|
||||
|
||||
if(p_fd >= 0) {
|
||||
::close(p_fd);
|
||||
p_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void p_ctor() {
|
||||
p_fd = -1;
|
||||
}
|
||||
|
||||
void p_dtor() {
|
||||
p_close();
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef NALL_FOREACH_HPP
|
||||
#define NALL_FOREACH_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <nall/concept.hpp>
|
||||
|
||||
#undef foreach
|
||||
#define foreach(iter, object) \
|
||||
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
|
||||
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef NALL_FUNCTION_HPP
|
||||
#define NALL_FUNCTION_HPP
|
||||
|
||||
namespace nall {
|
||||
template<typename T> class function;
|
||||
|
||||
template<typename R, typename... P> class function<R (P...)> {
|
||||
struct container {
|
||||
virtual R operator()(P... p) const = 0;
|
||||
virtual container* copy() const = 0;
|
||||
virtual ~container() {}
|
||||
} *callback;
|
||||
|
||||
struct global : container {
|
||||
R (*function)(P...);
|
||||
R operator()(P... p) const { return function(std::forward<P>(p)...); }
|
||||
container* copy() const { return new global(function); }
|
||||
global(R (*function)(P...)) : function(function) {}
|
||||
};
|
||||
|
||||
template<typename C> struct member : container {
|
||||
R (C::*function)(P...);
|
||||
C *object;
|
||||
R operator()(P... p) const { return (object->*function)(std::forward<P>(p)...); }
|
||||
container* copy() const { return new member(function, object); }
|
||||
member(R (C::*function)(P...), C *object) : function(function), object(object) {}
|
||||
};
|
||||
|
||||
template<typename L> struct lambda : container {
|
||||
mutable L object;
|
||||
R operator()(P... p) const { return object(std::forward<P>(p)...); }
|
||||
container* copy() const { return new lambda(object); }
|
||||
lambda(const L& object) : object(object) {}
|
||||
};
|
||||
|
||||
public:
|
||||
operator bool() const { return callback; }
|
||||
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
|
||||
void reset() { if(callback) { delete callback; callback = 0; } }
|
||||
|
||||
function& operator=(const function &source) {
|
||||
if(this != &source) {
|
||||
if(callback) { delete callback; callback = 0; }
|
||||
if(source.callback) callback = source.callback->copy();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
function(const function &source) : callback(0) { operator=(source); }
|
||||
function() : callback(0) {}
|
||||
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
|
||||
function(R (*function)(P...)) { callback = new global(function); }
|
||||
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
|
||||
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }
|
||||
template<typename L> function(const L& object) { callback = new lambda<L>(object); }
|
||||
~function() { if(callback) delete callback; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,105 @@
|
|||
#ifndef NALL_GAMEBOY_CARTRIDGE_HPP
|
||||
#define NALL_GAMEBOY_CARTRIDGE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
class GameBoyCartridge {
|
||||
public:
|
||||
string xml;
|
||||
inline GameBoyCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string mapper;
|
||||
bool ram;
|
||||
bool battery;
|
||||
bool rtc;
|
||||
bool rumble;
|
||||
|
||||
unsigned romsize;
|
||||
unsigned ramsize;
|
||||
} info;
|
||||
};
|
||||
|
||||
GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
|
||||
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
if(romsize < 0x4000) return;
|
||||
|
||||
info.mapper = "unknown";
|
||||
info.ram = false;
|
||||
info.battery = false;
|
||||
info.rtc = false;
|
||||
info.rumble = false;
|
||||
|
||||
info.romsize = 0;
|
||||
info.ramsize = 0;
|
||||
|
||||
switch(romdata[0x0147]) {
|
||||
case 0x00: info.mapper = "none"; break;
|
||||
case 0x01: info.mapper = "MBC1"; break;
|
||||
case 0x02: info.mapper = "MBC1"; info.ram = true; break;
|
||||
case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break;
|
||||
case 0x05: info.mapper = "MBC2"; info.ram = true; break;
|
||||
case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break;
|
||||
case 0x08: info.mapper = "none"; info.ram = true; break;
|
||||
case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break;
|
||||
case 0x0b: info.mapper = "MMM01"; break;
|
||||
case 0x0c: info.mapper = "MMM01"; info.ram = true; break;
|
||||
case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break;
|
||||
case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break;
|
||||
case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break;
|
||||
case 0x11: info.mapper = "MBC3"; break;
|
||||
case 0x12: info.mapper = "MBC3"; info.ram = true; break;
|
||||
case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break;
|
||||
case 0x19: info.mapper = "MBC5"; break;
|
||||
case 0x1a: info.mapper = "MBC5"; info.ram = true; break;
|
||||
case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break;
|
||||
case 0x1c: info.mapper = "MBC5"; info.rumble = true; break;
|
||||
case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break;
|
||||
case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break;
|
||||
case 0xfc: break; //Pocket Camera
|
||||
case 0xfd: break; //Bandai TAMA5
|
||||
case 0xfe: info.mapper = "HuC3"; break;
|
||||
case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break;
|
||||
}
|
||||
|
||||
switch(romdata[0x0148]) { default:
|
||||
case 0x00: info.romsize = 2 * 16 * 1024; break;
|
||||
case 0x01: info.romsize = 4 * 16 * 1024; break;
|
||||
case 0x02: info.romsize = 8 * 16 * 1024; break;
|
||||
case 0x03: info.romsize = 16 * 16 * 1024; break;
|
||||
case 0x04: info.romsize = 32 * 16 * 1024; break;
|
||||
case 0x05: info.romsize = 64 * 16 * 1024; break;
|
||||
case 0x06: info.romsize = 128 * 16 * 1024; break;
|
||||
case 0x07: info.romsize = 256 * 16 * 1024; break;
|
||||
case 0x52: info.romsize = 72 * 16 * 1024; break;
|
||||
case 0x53: info.romsize = 80 * 16 * 1024; break;
|
||||
case 0x54: info.romsize = 96 * 16 * 1024; break;
|
||||
}
|
||||
|
||||
switch(romdata[0x0149]) { default:
|
||||
case 0x00: info.ramsize = 0 * 1024; break;
|
||||
case 0x01: info.ramsize = 2 * 1024; break;
|
||||
case 0x02: info.ramsize = 8 * 1024; break;
|
||||
case 0x03: info.ramsize = 32 * 1024; break;
|
||||
}
|
||||
|
||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||
|
||||
xml << "<cartridge mapper='" << info.mapper << "'";
|
||||
if(info.rtc) xml << " rtc='true'";
|
||||
if(info.rumble) xml << " rumble='true'";
|
||||
xml << ">\n";
|
||||
|
||||
xml << " <rom size='" << hex(romsize) << "'/>\n"; //TODO: trust/check info.romsize?
|
||||
|
||||
if(info.ramsize > 0)
|
||||
xml << " <ram size='" << hex(info.ramsize) << "' battery='" << info.battery << "'/>\n";
|
||||
|
||||
xml << "</cartridge>\n";
|
||||
xml.transform("'", "\"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,386 @@
|
|||
#ifndef NALL_INPUT_HPP
|
||||
#define NALL_INPUT_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct Keyboard;
|
||||
Keyboard& keyboard(unsigned = 0);
|
||||
|
||||
static const char KeyboardScancodeName[][64] = {
|
||||
"Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
|
||||
"PrintScreen", "ScrollLock", "Pause", "Tilde",
|
||||
"Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0",
|
||||
"Dash", "Equal", "Backspace",
|
||||
"Insert", "Delete", "Home", "End", "PageUp", "PageDown",
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
||||
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||
"LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash",
|
||||
"Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0",
|
||||
"Point", "Enter", "Add", "Subtract", "Multiply", "Divide",
|
||||
"NumLock", "CapsLock",
|
||||
"Up", "Down", "Left", "Right",
|
||||
"Tab", "Return", "Spacebar", "Menu",
|
||||
"Shift", "Control", "Alt", "Super",
|
||||
};
|
||||
|
||||
struct Keyboard {
|
||||
const unsigned ID;
|
||||
enum { Base = 1 };
|
||||
enum { Count = 8, Size = 128 };
|
||||
|
||||
enum Scancode {
|
||||
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
||||
PrintScreen, ScrollLock, Pause, Tilde,
|
||||
Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0,
|
||||
Dash, Equal, Backspace,
|
||||
Insert, Delete, Home, End, PageUp, PageDown,
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash,
|
||||
Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0,
|
||||
Point, Enter, Add, Subtract, Multiply, Divide,
|
||||
NumLock, CapsLock,
|
||||
Up, Down, Left, Right,
|
||||
Tab, Return, Spacebar, Menu,
|
||||
Shift, Control, Alt, Super,
|
||||
Limit,
|
||||
};
|
||||
|
||||
static signed numberDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(keyboard(i).belongsTo(scancode)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static signed keyDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static signed modifierDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool isAnyKey(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(keyboard(i).isKey(scancode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isAnyModifier(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(keyboard(i).isModifier(scancode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint16_t decode(const char *name) {
|
||||
string s(name);
|
||||
if(!strbegin(name, "KB")) return 0;
|
||||
s.ltrim("KB");
|
||||
unsigned id = decimal(s);
|
||||
auto pos = strpos(s, "::");
|
||||
if(!pos) return 0;
|
||||
s = substr(s, pos() + 2);
|
||||
for(unsigned i = 0; i < Limit; i++) {
|
||||
if(s == KeyboardScancodeName[i]) return Base + Size * id + i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
string encode(uint16_t code) const {
|
||||
unsigned index = 0;
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
|
||||
index = code - (Base + Size * i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string() << "KB" << ID << "::" << KeyboardScancodeName[index];
|
||||
}
|
||||
|
||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
||||
uint16_t key(unsigned id) const { return Base + Size * ID + id; }
|
||||
bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); }
|
||||
bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); }
|
||||
bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); }
|
||||
|
||||
Keyboard(unsigned ID_) : ID(ID_) {}
|
||||
};
|
||||
|
||||
inline Keyboard& keyboard(unsigned id) {
|
||||
static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7);
|
||||
switch(id) { default:
|
||||
case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3;
|
||||
case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7;
|
||||
}
|
||||
}
|
||||
|
||||
static const char MouseScancodeName[][64] = {
|
||||
"Xaxis", "Yaxis", "Zaxis",
|
||||
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
|
||||
};
|
||||
|
||||
struct Mouse;
|
||||
Mouse& mouse(unsigned = 0);
|
||||
|
||||
struct Mouse {
|
||||
const unsigned ID;
|
||||
enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count };
|
||||
enum { Count = 8, Size = 16 };
|
||||
enum { Axes = 3, Buttons = 8 };
|
||||
|
||||
enum Scancode {
|
||||
Xaxis, Yaxis, Zaxis,
|
||||
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
|
||||
Limit,
|
||||
};
|
||||
|
||||
static signed numberDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(mouse(i).belongsTo(scancode)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static signed axisDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static signed buttonDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool isAnyAxis(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(mouse(i).isAxis(scancode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isAnyButton(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(mouse(i).isButton(scancode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint16_t decode(const char *name) {
|
||||
string s(name);
|
||||
if(!strbegin(name, "MS")) return 0;
|
||||
s.ltrim("MS");
|
||||
unsigned id = decimal(s);
|
||||
auto pos = strpos(s, "::");
|
||||
if(!pos) return 0;
|
||||
s = substr(s, pos() + 2);
|
||||
for(unsigned i = 0; i < Limit; i++) {
|
||||
if(s == MouseScancodeName[i]) return Base + Size * id + i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
string encode(uint16_t code) const {
|
||||
unsigned index = 0;
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
|
||||
index = code - (Base + Size * i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string() << "MS" << ID << "::" << MouseScancodeName[index];
|
||||
}
|
||||
|
||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
||||
uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; }
|
||||
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
|
||||
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); }
|
||||
bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); }
|
||||
bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); }
|
||||
|
||||
Mouse(unsigned ID_) : ID(ID_) {}
|
||||
};
|
||||
|
||||
inline Mouse& mouse(unsigned id) {
|
||||
static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7);
|
||||
switch(id) { default:
|
||||
case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3;
|
||||
case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7;
|
||||
}
|
||||
}
|
||||
|
||||
static const char JoypadScancodeName[][64] = {
|
||||
"Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7",
|
||||
"Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7",
|
||||
"Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15",
|
||||
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
|
||||
"Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15",
|
||||
"Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23",
|
||||
"Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31",
|
||||
};
|
||||
|
||||
struct Joypad;
|
||||
Joypad& joypad(unsigned = 0);
|
||||
|
||||
struct Joypad {
|
||||
const unsigned ID;
|
||||
enum { Base = Mouse::Base + Mouse::Size * Mouse::Count };
|
||||
enum { Count = 8, Size = 64 };
|
||||
enum { Hats = 8, Axes = 16, Buttons = 32 };
|
||||
|
||||
enum Scancode {
|
||||
Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7,
|
||||
Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7,
|
||||
Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15,
|
||||
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
|
||||
Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15,
|
||||
Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23,
|
||||
Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31,
|
||||
Limit,
|
||||
};
|
||||
|
||||
enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 };
|
||||
|
||||
static signed numberDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(joypad(i).belongsTo(scancode)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static signed hatDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static signed axisDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static signed buttonDecode(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool isAnyHat(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(joypad(i).isHat(scancode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isAnyAxis(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(joypad(i).isAxis(scancode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isAnyButton(uint16_t scancode) {
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(joypad(i).isButton(scancode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint16_t decode(const char *name) {
|
||||
string s(name);
|
||||
if(!strbegin(name, "JP")) return 0;
|
||||
s.ltrim("JP");
|
||||
unsigned id = decimal(s);
|
||||
auto pos = strpos(s, "::");
|
||||
if(!pos) return 0;
|
||||
s = substr(s, pos() + 2);
|
||||
for(unsigned i = 0; i < Limit; i++) {
|
||||
if(s == JoypadScancodeName[i]) return Base + Size * id + i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
string encode(uint16_t code) const {
|
||||
unsigned index = 0;
|
||||
for(unsigned i = 0; i < Count; i++) {
|
||||
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
|
||||
index = code - (Base + Size * i);
|
||||
}
|
||||
}
|
||||
return string() << "JP" << ID << "::" << JoypadScancodeName[index];
|
||||
}
|
||||
|
||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
||||
uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; }
|
||||
uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; }
|
||||
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
|
||||
bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); }
|
||||
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); }
|
||||
bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); }
|
||||
bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); }
|
||||
|
||||
Joypad(unsigned ID_) : ID(ID_) {}
|
||||
};
|
||||
|
||||
inline Joypad& joypad(unsigned id) {
|
||||
static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7);
|
||||
switch(id) { default:
|
||||
case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3;
|
||||
case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7;
|
||||
}
|
||||
}
|
||||
|
||||
struct Scancode {
|
||||
enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count };
|
||||
|
||||
static uint16_t decode(const char *name) {
|
||||
uint16_t code;
|
||||
code = Keyboard::decode(name);
|
||||
if(code) return code;
|
||||
code = Mouse::decode(name);
|
||||
if(code) return code;
|
||||
code = Joypad::decode(name);
|
||||
if(code) return code;
|
||||
return None;
|
||||
}
|
||||
|
||||
static string encode(uint16_t code) {
|
||||
for(unsigned i = 0; i < Keyboard::Count; i++) {
|
||||
if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code);
|
||||
}
|
||||
for(unsigned i = 0; i < Mouse::Count; i++) {
|
||||
if(mouse(i).belongsTo(code)) return mouse(i).encode(code);
|
||||
}
|
||||
for(unsigned i = 0; i < Joypad::Count; i++) {
|
||||
if(joypad(i).belongsTo(code)) return joypad(i).encode(code);
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,81 @@
|
|||
#ifndef NALL_LZSS_HPP
|
||||
#define NALL_LZSS_HPP
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class lzss {
|
||||
public:
|
||||
static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) {
|
||||
output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9];
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
unsigned flagoffset = o++;
|
||||
uint8_t flag = 0x00;
|
||||
|
||||
for(unsigned b = 0; b < 8 && i < inlength; b++) {
|
||||
unsigned longest = 0, pointer;
|
||||
for(unsigned index = 1; index < 4096; index++) {
|
||||
unsigned count = 0;
|
||||
while(true) {
|
||||
if(count >= 15 + 3) break; //verify pattern match is not longer than max length
|
||||
if(i + count >= inlength) break; //verify pattern match does not read past end of input
|
||||
if(i + count < index) break; //verify read is not before start of input
|
||||
if(input[i + count] != input[i + count - index]) break; //verify pattern still matches
|
||||
count++;
|
||||
}
|
||||
|
||||
if(count > longest) {
|
||||
longest = count;
|
||||
pointer = index;
|
||||
}
|
||||
}
|
||||
|
||||
if(longest < 3) output[o++] = input[i++];
|
||||
else {
|
||||
flag |= 1 << b;
|
||||
uint16_t x = ((longest - 3) << 12) + pointer;
|
||||
output[o++] = x;
|
||||
output[o++] = x >> 8;
|
||||
i += longest;
|
||||
}
|
||||
}
|
||||
|
||||
output[flagoffset] = flag;
|
||||
}
|
||||
|
||||
outlength = o;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) {
|
||||
output = new(zeromemory) uint8_t[length];
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(o < length) {
|
||||
uint8_t flag = input[i++];
|
||||
|
||||
for(unsigned b = 0; b < 8 && o < length; b++) {
|
||||
if(!(flag & (1 << b))) output[o++] = input[i++];
|
||||
else {
|
||||
uint16_t offset = input[i++];
|
||||
offset += input[i++] << 8;
|
||||
uint16_t lookuplength = (offset >> 12) + 3;
|
||||
offset &= 4095;
|
||||
for(unsigned index = 0; index < lookuplength && o + index < length; index++) {
|
||||
output[o + index] = output[o + index - offset];
|
||||
}
|
||||
o += lookuplength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef NALL_MODULO_HPP
|
||||
#define NALL_MODULO_HPP
|
||||
|
||||
#include <nall/serializer.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<typename T, int size> class modulo_array {
|
||||
public:
|
||||
inline T operator[](int index) const {
|
||||
return buffer[size + index];
|
||||
}
|
||||
|
||||
inline T read(int index) const {
|
||||
return buffer[size + index];
|
||||
}
|
||||
|
||||
inline void write(unsigned index, const T value) {
|
||||
buffer[index] =
|
||||
buffer[index + size] =
|
||||
buffer[index + size + size] = value;
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
s.array(buffer, size * 3);
|
||||
}
|
||||
|
||||
modulo_array() {
|
||||
buffer = new T[size * 3]();
|
||||
}
|
||||
|
||||
~modulo_array() {
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
T *buffer;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,122 @@
|
|||
#ifndef NALL_PLATFORM_HPP
|
||||
#define NALL_PLATFORM_HPP
|
||||
|
||||
#include <nall/utf8.hpp>
|
||||
|
||||
//=========================
|
||||
//standard platform headers
|
||||
//=========================
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <shlobj.h>
|
||||
#undef interface
|
||||
#define dllexport __declspec(dllexport)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#define dllexport
|
||||
#endif
|
||||
|
||||
//==================
|
||||
//warning supression
|
||||
//==================
|
||||
|
||||
//Visual C++
|
||||
#if defined(_MSC_VER)
|
||||
//disable libc "deprecation" warnings
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
//================
|
||||
//POSIX compliance
|
||||
//================
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define PATH_MAX _MAX_PATH
|
||||
#define va_copy(dest, src) ((dest) = (src))
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define getcwd _getcwd
|
||||
#define ftruncate _chsize
|
||||
#define putenv _putenv
|
||||
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
|
||||
#define rmdir _rmdir
|
||||
#define vsnprintf _vsnprintf
|
||||
#define usleep(n) Sleep(n / 1000)
|
||||
#endif
|
||||
|
||||
//================
|
||||
//inline expansion
|
||||
//================
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define noinline __attribute__((noinline))
|
||||
#define inline inline
|
||||
#define alwaysinline inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define noinline __declspec(noinline)
|
||||
#define inline inline
|
||||
#define alwaysinline inline __forceinline
|
||||
#else
|
||||
#define noinline
|
||||
#define inline inline
|
||||
#define alwaysinline inline
|
||||
#endif
|
||||
|
||||
//=========================
|
||||
//file system functionality
|
||||
//=========================
|
||||
|
||||
#if defined(_WIN32)
|
||||
inline char* realpath(const char *filename, char *resolvedname) {
|
||||
wchar_t fn[_MAX_PATH] = L"";
|
||||
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
|
||||
strcpy(resolvedname, nall::utf8_t(fn));
|
||||
return resolvedname;
|
||||
}
|
||||
|
||||
inline char* userpath(char *path) {
|
||||
wchar_t fp[_MAX_PATH] = L"";
|
||||
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
|
||||
strcpy(path, nall::utf8_t(fp));
|
||||
return path;
|
||||
}
|
||||
|
||||
inline char* getcwd(char *path) {
|
||||
wchar_t fp[_MAX_PATH] = L"";
|
||||
_wgetcwd(fp, _MAX_PATH);
|
||||
strcpy(path, nall::utf8_t(fp));
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
//realpath() already exists
|
||||
|
||||
inline char* userpath(char *path) {
|
||||
*path = 0;
|
||||
struct passwd *userinfo = getpwuid(getuid());
|
||||
if(userinfo) strcpy(path, userinfo->pw_dir);
|
||||
return path;
|
||||
}
|
||||
|
||||
inline char *getcwd(char *path) {
|
||||
return getcwd(path, PATH_MAX);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
#ifndef NALL_PRIORITYQUEUE_HPP
|
||||
#define NALL_PRIORITYQUEUE_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<typename type_t> void priority_queue_nocallback(type_t) {}
|
||||
|
||||
//priority queue implementation using binary min-heap array;
|
||||
//does not require normalize() function.
|
||||
//O(1) find (tick)
|
||||
//O(log n) insert (enqueue)
|
||||
//O(log n) remove (dequeue)
|
||||
template<typename type_t> class priority_queue {
|
||||
public:
|
||||
inline void tick(unsigned ticks) {
|
||||
basecounter += ticks;
|
||||
while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue());
|
||||
}
|
||||
|
||||
//counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks);
|
||||
//counter cannot exceed std::numeric_limits<unsigned>::max() >> 1.
|
||||
void enqueue(unsigned counter, type_t event) {
|
||||
unsigned child = heapsize++;
|
||||
counter += basecounter;
|
||||
|
||||
while(child) {
|
||||
unsigned parent = (child - 1) >> 1;
|
||||
if(gte(counter, heap[parent].counter)) break;
|
||||
|
||||
heap[child].counter = heap[parent].counter;
|
||||
heap[child].event = heap[parent].event;
|
||||
child = parent;
|
||||
}
|
||||
|
||||
heap[child].counter = counter;
|
||||
heap[child].event = event;
|
||||
}
|
||||
|
||||
type_t dequeue() {
|
||||
type_t event(heap[0].event);
|
||||
unsigned parent = 0;
|
||||
unsigned counter = heap[--heapsize].counter;
|
||||
|
||||
while(true) {
|
||||
unsigned child = (parent << 1) + 1;
|
||||
if(child >= heapsize) break;
|
||||
if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++;
|
||||
if(gte(heap[child].counter, counter)) break;
|
||||
|
||||
heap[parent].counter = heap[child].counter;
|
||||
heap[parent].event = heap[child].event;
|
||||
parent = child;
|
||||
}
|
||||
|
||||
heap[parent].counter = counter;
|
||||
heap[parent].event = heap[heapsize].event;
|
||||
return event;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
basecounter = 0;
|
||||
heapsize = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
s.integer(basecounter);
|
||||
s.integer(heapsize);
|
||||
for(unsigned n = 0; n < heapcapacity; n++) {
|
||||
s.integer(heap[n].counter);
|
||||
s.integer(heap[n].event);
|
||||
}
|
||||
}
|
||||
|
||||
priority_queue(unsigned size, function<void (type_t)> callback_ = &priority_queue_nocallback<type_t>)
|
||||
: callback(callback_) {
|
||||
heap = new heap_t[size];
|
||||
heapcapacity = size;
|
||||
reset();
|
||||
}
|
||||
|
||||
~priority_queue() {
|
||||
delete[] heap;
|
||||
}
|
||||
|
||||
priority_queue& operator=(const priority_queue&) = delete;
|
||||
priority_queue(const priority_queue&) = delete;
|
||||
|
||||
private:
|
||||
function<void (type_t)> callback;
|
||||
unsigned basecounter;
|
||||
unsigned heapsize;
|
||||
unsigned heapcapacity;
|
||||
struct heap_t {
|
||||
unsigned counter;
|
||||
type_t event;
|
||||
} *heap;
|
||||
|
||||
//return true if x is greater than or equal to y
|
||||
inline bool gte(unsigned x, unsigned y) {
|
||||
return x - y < (std::numeric_limits<unsigned>::max() >> 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef NALL_PROPERTY_HPP
|
||||
#define NALL_PROPERTY_HPP
|
||||
|
||||
//nall::property implements ownership semantics into container classes
|
||||
//example: property<owner>::readonly<type> implies that only owner has full
|
||||
//access to type; and all other code has readonly access.
|
||||
//
|
||||
//this code relies on extended friend semantics from C++0x to work, as it
|
||||
//declares a friend class via a template paramter. it also exploits a bug in
|
||||
//G++ 4.x to work even in C++98 mode.
|
||||
//
|
||||
//if compiling elsewhere, simply remove the friend class and private semantics
|
||||
|
||||
//property can be used either of two ways:
|
||||
//struct foo {
|
||||
// property<foo>::readonly<bool> x;
|
||||
// property<foo>::readwrite<int> y;
|
||||
//};
|
||||
//-or-
|
||||
//struct foo : property<foo> {
|
||||
// readonly<bool> x;
|
||||
// readwrite<int> y;
|
||||
//};
|
||||
|
||||
//return types are const T& (byref) instead fo T (byval) to avoid major speed
|
||||
//penalties for objects with expensive copy constructors
|
||||
|
||||
//operator-> provides access to underlying object type:
|
||||
//readonly<Object> foo;
|
||||
//foo->bar();
|
||||
//... will call Object::bar();
|
||||
|
||||
//operator='s reference is constant so as to avoid leaking a reference handle
|
||||
//that could bypass access restrictions
|
||||
|
||||
//both constant and non-constant operators are provided, though it may be
|
||||
//necessary to cast first, for instance:
|
||||
//struct foo : property<foo> { readonly<int> bar; } object;
|
||||
//int main() { int value = const_cast<const foo&>(object); }
|
||||
|
||||
//writeonly is useful for objects that have non-const reads, but const writes.
|
||||
//however, to avoid leaking handles, the interface is very restricted. the only
|
||||
//way to write is via operator=, which requires conversion via eg copy
|
||||
//constructor. example:
|
||||
//struct foo {
|
||||
// foo(bool value) { ... }
|
||||
//};
|
||||
//writeonly<foo> bar;
|
||||
//bar = true;
|
||||
|
||||
namespace nall {
|
||||
template<typename C> struct property {
|
||||
template<typename T> struct traits { typedef T type; };
|
||||
|
||||
template<typename T> struct readonly {
|
||||
const T* operator->() const { return &value; }
|
||||
const T& operator()() const { return value; }
|
||||
operator const T&() const { return value; }
|
||||
private:
|
||||
T* operator->() { return &value; }
|
||||
operator T&() { return value; }
|
||||
const T& operator=(const T& value_) { return value = value_; }
|
||||
T value;
|
||||
friend class traits<C>::type;
|
||||
};
|
||||
|
||||
template<typename T> struct writeonly {
|
||||
void operator=(const T& value_) { value = value_; }
|
||||
private:
|
||||
const T* operator->() const { return &value; }
|
||||
const T& operator()() const { return value; }
|
||||
operator const T&() const { return value; }
|
||||
T* operator->() { return &value; }
|
||||
operator T&() { return value; }
|
||||
T value;
|
||||
friend class traits<C>::type;
|
||||
};
|
||||
|
||||
template<typename T> struct readwrite {
|
||||
const T* operator->() const { return &value; }
|
||||
const T& operator()() const { return value; }
|
||||
operator const T&() const { return value; }
|
||||
T* operator->() { return &value; }
|
||||
operator T&() { return value; }
|
||||
const T& operator=(const T& value_) { return value = value_; }
|
||||
T value;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef NALL_RANDOM_HPP
|
||||
#define NALL_RANDOM_HPP
|
||||
|
||||
namespace nall {
|
||||
//pseudo-random number generator
|
||||
inline unsigned prng() {
|
||||
static unsigned n = 0;
|
||||
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
|
||||
struct random_cyclic {
|
||||
unsigned seed;
|
||||
inline unsigned operator()() {
|
||||
return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
random_cyclic() : seed(0) {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef NALL_SERIAL_HPP
|
||||
#define NALL_SERIAL_HPP
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class serial {
|
||||
public:
|
||||
//-1 on error, otherwise return bytes read
|
||||
int read(uint8_t *data, unsigned length) {
|
||||
if(port_open == false) return -1;
|
||||
return ::read(port, (void*)data, length);
|
||||
}
|
||||
|
||||
//-1 on error, otherwise return bytes written
|
||||
int write(const uint8_t *data, unsigned length) {
|
||||
if(port_open == false) return -1;
|
||||
return ::write(port, (void*)data, length);
|
||||
}
|
||||
|
||||
bool open(const char *portname, unsigned rate, bool flowcontrol) {
|
||||
close();
|
||||
|
||||
port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
|
||||
if(port == -1) return false;
|
||||
|
||||
if(ioctl(port, TIOCEXCL) == -1) { close(); return false; }
|
||||
if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; }
|
||||
if(tcgetattr(port, &original_attr) == -1) { close(); return false; }
|
||||
|
||||
termios attr = original_attr;
|
||||
cfmakeraw(&attr);
|
||||
cfsetspeed(&attr, rate);
|
||||
|
||||
attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN);
|
||||
attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
|
||||
attr.c_iflag |= (IGNBRK | IGNPAR);
|
||||
attr.c_oflag &=~ (OPOST);
|
||||
attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL);
|
||||
attr.c_cflag |= (CS8 | CREAD);
|
||||
if(flowcontrol == false) {
|
||||
attr.c_cflag &= ~CRTSCTS;
|
||||
} else {
|
||||
attr.c_cflag |= CRTSCTS;
|
||||
}
|
||||
attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0;
|
||||
|
||||
if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; }
|
||||
return port_open = true;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if(port != -1) {
|
||||
tcdrain(port);
|
||||
if(port_open == true) {
|
||||
tcsetattr(port, TCSANOW, &original_attr);
|
||||
port_open = false;
|
||||
}
|
||||
::close(port);
|
||||
port = -1;
|
||||
}
|
||||
}
|
||||
|
||||
serial() {
|
||||
port = -1;
|
||||
port_open = false;
|
||||
}
|
||||
|
||||
~serial() {
|
||||
close();
|
||||
}
|
||||
|
||||
private:
|
||||
int port;
|
||||
bool port_open;
|
||||
termios original_attr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,146 @@
|
|||
#ifndef NALL_SERIALIZER_HPP
|
||||
#define NALL_SERIALIZER_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
//serializer: a class designed to save and restore the state of classes.
|
||||
//
|
||||
//benefits:
|
||||
//- data() will be portable in size (it is not necessary to specify type sizes.)
|
||||
//- data() will be portable in endianness (always stored internally as little-endian.)
|
||||
//- one serialize function can both save and restore class states.
|
||||
//
|
||||
//caveats:
|
||||
//- only plain-old-data can be stored. complex classes must provide serialize(serializer&);
|
||||
//- floating-point usage is not portable across platforms
|
||||
|
||||
class serializer {
|
||||
public:
|
||||
enum mode_t { Load, Save, Size };
|
||||
|
||||
mode_t mode() const {
|
||||
return imode;
|
||||
}
|
||||
|
||||
const uint8_t* data() const {
|
||||
return idata;
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return isize;
|
||||
}
|
||||
|
||||
unsigned capacity() const {
|
||||
return icapacity;
|
||||
}
|
||||
|
||||
template<typename T> void floatingpoint(T &value) {
|
||||
enum { size = sizeof(T) };
|
||||
//this is rather dangerous, and not cross-platform safe;
|
||||
//but there is no standardized way to export FP-values
|
||||
uint8_t *p = (uint8_t*)&value;
|
||||
if(imode == Save) {
|
||||
for(unsigned n = 0; n < size; n++) idata[isize++] = p[n];
|
||||
} else if(imode == Load) {
|
||||
for(unsigned n = 0; n < size; n++) p[n] = idata[isize++];
|
||||
} else {
|
||||
isize += size;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void integer(T &value) {
|
||||
enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
|
||||
if(imode == Save) {
|
||||
for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3);
|
||||
} else if(imode == Load) {
|
||||
value = 0;
|
||||
for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3);
|
||||
} else if(imode == Size) {
|
||||
isize += size;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void array(T &array) {
|
||||
enum { size = sizeof(T) / sizeof(typename std::remove_extent<T>::type) };
|
||||
for(unsigned n = 0; n < size; n++) integer(array[n]);
|
||||
}
|
||||
|
||||
template<typename T> void array(T array, unsigned size) {
|
||||
for(unsigned n = 0; n < size; n++) integer(array[n]);
|
||||
}
|
||||
|
||||
//copy
|
||||
serializer& operator=(const serializer &s) {
|
||||
if(idata) delete[] idata;
|
||||
|
||||
imode = s.imode;
|
||||
idata = new uint8_t[s.icapacity];
|
||||
isize = s.isize;
|
||||
icapacity = s.icapacity;
|
||||
|
||||
memcpy(idata, s.idata, s.icapacity);
|
||||
return *this;
|
||||
}
|
||||
|
||||
serializer(const serializer &s) : idata(0) {
|
||||
operator=(s);
|
||||
}
|
||||
|
||||
//move
|
||||
serializer& operator=(serializer &&s) {
|
||||
if(idata) delete[] idata;
|
||||
|
||||
imode = s.imode;
|
||||
idata = s.idata;
|
||||
isize = s.isize;
|
||||
icapacity = s.icapacity;
|
||||
|
||||
s.idata = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
serializer(serializer &&s) {
|
||||
operator=(std::move(s));
|
||||
}
|
||||
|
||||
//construction
|
||||
serializer() {
|
||||
imode = Size;
|
||||
idata = 0;
|
||||
isize = 0;
|
||||
icapacity = 0;
|
||||
}
|
||||
|
||||
serializer(unsigned capacity) {
|
||||
imode = Save;
|
||||
idata = new uint8_t[capacity]();
|
||||
isize = 0;
|
||||
icapacity = capacity;
|
||||
}
|
||||
|
||||
serializer(const uint8_t *data, unsigned capacity) {
|
||||
imode = Load;
|
||||
idata = new uint8_t[capacity];
|
||||
isize = 0;
|
||||
icapacity = capacity;
|
||||
memcpy(idata, data, capacity);
|
||||
}
|
||||
|
||||
~serializer() {
|
||||
if(idata) delete[] idata;
|
||||
}
|
||||
|
||||
private:
|
||||
mode_t imode;
|
||||
uint8_t *idata;
|
||||
unsigned isize;
|
||||
unsigned icapacity;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,143 @@
|
|||
#ifndef NALL_SHA256_HPP
|
||||
#define NALL_SHA256_HPP
|
||||
|
||||
//author: vladitx
|
||||
|
||||
namespace nall {
|
||||
#define PTR(t, a) ((t*)(a))
|
||||
|
||||
#define SWAP32(x) ((uint32_t)( \
|
||||
(((uint32_t)(x) & 0x000000ff) << 24) | \
|
||||
(((uint32_t)(x) & 0x0000ff00) << 8) | \
|
||||
(((uint32_t)(x) & 0x00ff0000) >> 8) | \
|
||||
(((uint32_t)(x) & 0xff000000) >> 24) \
|
||||
))
|
||||
|
||||
#define ST32(a, d) *PTR(uint32_t, a) = (d)
|
||||
#define ST32BE(a, d) ST32(a, SWAP32(d))
|
||||
|
||||
#define LD32(a) *PTR(uint32_t, a)
|
||||
#define LD32BE(a) SWAP32(LD32(a))
|
||||
|
||||
#define LSL32(x, n) ((uint32_t)(x) << (n))
|
||||
#define LSR32(x, n) ((uint32_t)(x) >> (n))
|
||||
#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n)))
|
||||
|
||||
//first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
|
||||
static const uint32_t T_H[8] = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
};
|
||||
|
||||
//first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311
|
||||
static const uint32_t T_K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
};
|
||||
|
||||
struct sha256_ctx {
|
||||
uint8_t in[64];
|
||||
unsigned inlen;
|
||||
|
||||
uint32_t w[64];
|
||||
uint32_t h[8];
|
||||
uint64_t len;
|
||||
};
|
||||
|
||||
void sha256_init(sha256_ctx *p) {
|
||||
memset(p, 0, sizeof(sha256_ctx));
|
||||
memcpy(p->h, T_H, sizeof(T_H));
|
||||
}
|
||||
|
||||
static void sha256_block(sha256_ctx *p) {
|
||||
unsigned i;
|
||||
uint32_t s0, s1;
|
||||
uint32_t a, b, c, d, e, f, g, h;
|
||||
uint32_t t1, t2, maj, ch;
|
||||
|
||||
for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4);
|
||||
|
||||
for(i = 16; i < 64; i++) {
|
||||
s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3);
|
||||
s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10);
|
||||
p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1;
|
||||
}
|
||||
|
||||
a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3];
|
||||
e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7];
|
||||
|
||||
for(i = 0; i < 64; i++) {
|
||||
s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22);
|
||||
maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
t2 = s0 + maj;
|
||||
s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25);
|
||||
ch = (e & f) ^ (~e & g);
|
||||
t1 = h + s1 + ch + T_K[i] + p->w[i];
|
||||
|
||||
h = g; g = f; f = e; e = d + t1;
|
||||
d = c; c = b; b = a; a = t1 + t2;
|
||||
}
|
||||
|
||||
p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d;
|
||||
p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h;
|
||||
|
||||
//next block
|
||||
p->inlen = 0;
|
||||
}
|
||||
|
||||
void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
|
||||
unsigned l;
|
||||
p->len += len;
|
||||
|
||||
while(len) {
|
||||
l = 64 - p->inlen;
|
||||
l = (len < l) ? len : l;
|
||||
|
||||
memcpy(p->in + p->inlen, s, l);
|
||||
s += l;
|
||||
p->inlen += l;
|
||||
len -= l;
|
||||
|
||||
if(p->inlen == 64) sha256_block(p);
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_final(sha256_ctx *p) {
|
||||
uint64_t len;
|
||||
p->in[p->inlen++] = 0x80;
|
||||
|
||||
if(p->inlen > 56) {
|
||||
memset(p->in + p->inlen, 0, 64 - p->inlen);
|
||||
sha256_block(p);
|
||||
}
|
||||
|
||||
memset(p->in + p->inlen, 0, 56 - p->inlen);
|
||||
|
||||
len = p->len << 3;
|
||||
ST32BE(p->in + 56, len >> 32);
|
||||
ST32BE(p->in + 60, len);
|
||||
sha256_block(p);
|
||||
}
|
||||
|
||||
void sha256_hash(sha256_ctx *p, uint8_t *s) {
|
||||
uint32_t *t = (uint32_t*)s;
|
||||
for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
|
||||
}
|
||||
|
||||
#undef PTR
|
||||
#undef SWAP32
|
||||
#undef ST32
|
||||
#undef ST32BE
|
||||
#undef LD32
|
||||
#undef LD32BE
|
||||
#undef LSL32
|
||||
#undef LSR32
|
||||
#undef ROR32
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,875 @@
|
|||
#ifndef NALL_SNES_CARTRIDGE_HPP
|
||||
#define NALL_SNES_CARTRIDGE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
class SNESCartridge {
|
||||
public:
|
||||
string xmlMemoryMap;
|
||||
inline SNESCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
inline void read_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned find_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||
inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size);
|
||||
inline bool gameboy_has_rtc(const uint8_t *data, unsigned size);
|
||||
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
RomType = 0x16,
|
||||
RomSize = 0x17,
|
||||
RamSize = 0x18,
|
||||
CartRegion = 0x19,
|
||||
Company = 0x1a,
|
||||
Version = 0x1b,
|
||||
Complement = 0x1c, //inverse checksum
|
||||
Checksum = 0x1e,
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
ModeNormal,
|
||||
ModeBsxSlotted,
|
||||
ModeBsx,
|
||||
ModeSufamiTurbo,
|
||||
ModeSuperGameBoy,
|
||||
};
|
||||
|
||||
enum Type {
|
||||
TypeNormal,
|
||||
TypeBsxSlotted,
|
||||
TypeBsxBios,
|
||||
TypeBsx,
|
||||
TypeSufamiTurboBios,
|
||||
TypeSufamiTurbo,
|
||||
TypeSuperGameBoy1Bios,
|
||||
TypeSuperGameBoy2Bios,
|
||||
TypeGameBoy,
|
||||
TypeUnknown,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
};
|
||||
|
||||
enum MemoryMapper {
|
||||
LoROM,
|
||||
HiROM,
|
||||
ExLoROM,
|
||||
ExHiROM,
|
||||
SuperFXROM,
|
||||
SA1ROM,
|
||||
SPC7110ROM,
|
||||
BSCLoROM,
|
||||
BSCHiROM,
|
||||
BSXROM,
|
||||
STROM,
|
||||
};
|
||||
|
||||
enum DSP1MemoryMapper {
|
||||
DSP1Unmapped,
|
||||
DSP1LoROM1MB,
|
||||
DSP1LoROM2MB,
|
||||
DSP1HiROM,
|
||||
};
|
||||
|
||||
bool loaded; //is a base cartridge inserted?
|
||||
unsigned crc32; //crc32 of all cartridges (base+slot(s))
|
||||
unsigned rom_size;
|
||||
unsigned ram_size;
|
||||
|
||||
Mode mode;
|
||||
Type type;
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
|
||||
bool has_bsx_slot;
|
||||
bool has_superfx;
|
||||
bool has_sa1;
|
||||
bool has_srtc;
|
||||
bool has_sdd1;
|
||||
bool has_spc7110;
|
||||
bool has_spc7110rtc;
|
||||
bool has_cx4;
|
||||
bool has_dsp1;
|
||||
bool has_dsp2;
|
||||
bool has_dsp3;
|
||||
bool has_dsp4;
|
||||
bool has_obc1;
|
||||
bool has_st010;
|
||||
bool has_st011;
|
||||
bool has_st018;
|
||||
};
|
||||
|
||||
SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
read_header(data, size);
|
||||
|
||||
string xml = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||
|
||||
if(type == TypeBsx) {
|
||||
xml << "<cartridge/>";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == TypeSufamiTurbo) {
|
||||
xml << "<cartridge/>";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == TypeGameBoy) {
|
||||
xml << "<cartridge rtc='" << gameboy_has_rtc(data, size) << "'>\n";
|
||||
if(gameboy_ram_size(data, size) > 0) {
|
||||
xml << " <ram size='" << hex(gameboy_ram_size(data, size)) << "'/>\n";
|
||||
}
|
||||
xml << "</cartridge>\n";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
xml << "<cartridge";
|
||||
if(region == NTSC) {
|
||||
xml << " region='NTSC'";
|
||||
} else {
|
||||
xml << " region='PAL'";
|
||||
}
|
||||
xml << ">\n";
|
||||
|
||||
if(type == TypeSuperGameBoy1Bios) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <icd2 revision='1'>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </icd2>\n";
|
||||
} else if(type == TypeSuperGameBoy2Bios) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <icd2 revision='2'>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </icd2>\n";
|
||||
} else if(has_spc7110) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-0f:8000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-cf:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
|
||||
xml << " <spc7110>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <map address='d0-ff:0000-ffff' offset='100000' size='" << hex(size - 0x100000) << "'/>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='00:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='30:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:4800-483f'/>\n";
|
||||
xml << " <map address='80-bf:4800-483f'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
if(has_spc7110rtc) {
|
||||
xml << " <rtc>\n";
|
||||
xml << " <map address='00-3f:4840-4842'/>\n";
|
||||
xml << " <map address='80-bf:4840-4842'/>\n";
|
||||
xml << " </rtc>\n";
|
||||
}
|
||||
xml << " <dcu>\n";
|
||||
xml << " <map address='50:0000-ffff'/>\n";
|
||||
xml << " </dcu>\n";
|
||||
xml << " </spc7110>\n";
|
||||
} else if(mapper == LoROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
|
||||
} else {
|
||||
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-ff:0000-ffff'/>\n";
|
||||
}
|
||||
xml << " </ram>\n";
|
||||
}
|
||||
} else if(mapper == HiROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-ff:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
} else {
|
||||
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
|
||||
}
|
||||
xml << " </ram>\n";
|
||||
}
|
||||
} else if(mapper == ExLoROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
}
|
||||
} else if(mapper == ExHiROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-3f:8000-ffff' offset='400000'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff' offset='400000'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff' offset='000000'/>\n";
|
||||
xml << " <map mode='linear' address='c0-ff:0000-ffff' offset='000000'/>\n";
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
} else {
|
||||
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
|
||||
}
|
||||
xml << " </ram>\n";
|
||||
}
|
||||
} else if(mapper == SuperFXROM) {
|
||||
xml << " <superfx revision='2'>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='00-3f:6000-7fff' size='2000'/>\n";
|
||||
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:6000-7fff' size='2000'/>\n";
|
||||
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:3000-32ff'/>\n";
|
||||
xml << " <map address='80-bf:3000-32ff'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </superfx>\n";
|
||||
} else if(mapper == SA1ROM) {
|
||||
xml << " <sa1>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='direct' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='direct' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='direct' address='c0-ff:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram>\n";
|
||||
xml << " <map mode='direct' address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='direct' address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <iram size='800'>\n";
|
||||
xml << " <map mode='linear' address='00-3f:3000-37ff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:3000-37ff'/>\n";
|
||||
xml << " </iram>\n";
|
||||
xml << " <bwram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='40-4f:0000-ffff'/>\n";
|
||||
xml << " </bwram>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:2200-23ff'/>\n";
|
||||
xml << " <map address='80-bf:2200-23ff'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </sa1>\n";
|
||||
} else if(mapper == BSCLoROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-1f:8000-ffff' offset='000000'/>\n";
|
||||
xml << " <map mode='linear' address='20-3f:8000-ffff' offset='100000'/>\n";
|
||||
xml << " <map mode='linear' address='80-9f:8000-ffff' offset='200000'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:8000-ffff' offset='100000'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <bsx>\n";
|
||||
xml << " <slot>\n";
|
||||
xml << " <map mode='linear' address='c0-ef:0000-ffff'/>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " </bsx>\n";
|
||||
} else if(mapper == BSCHiROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-1f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='80-9f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <bsx>\n";
|
||||
xml << " <slot>\n";
|
||||
xml << " <map mode='shadow' address='20-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='a0-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " </bsx>\n";
|
||||
} else if(mapper == BSXROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <bsx>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:5000-5fff'/>\n";
|
||||
xml << " <map address='80-bf:5000-5fff'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </bsx>\n";
|
||||
} else if(mapper == STROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-1f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-9f:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <sufamiturbo>\n";
|
||||
xml << " <slot id='A'>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='20-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram>\n";
|
||||
xml << " <map mode='linear' address='60-63:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='e0-e3:8000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " <slot id='B'>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='40-5f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram>\n";
|
||||
xml << " <map mode='linear' address='70-73:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-f3:8000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " </sufamiturbo>\n";
|
||||
}
|
||||
|
||||
if(has_srtc) {
|
||||
xml << " <srtc>\n";
|
||||
xml << " <map address='00-3f:2800-2801'/>\n";
|
||||
xml << " <map address='80-bf:2800-2801'/>\n";
|
||||
xml << " </srtc>\n";
|
||||
}
|
||||
|
||||
if(has_sdd1) {
|
||||
xml << " <sdd1>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <map address='c0-ff:0000-ffff'/>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:4800-4807'/>\n";
|
||||
xml << " <map address='80-bf:4800-4807'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </sdd1>\n";
|
||||
}
|
||||
|
||||
if(has_cx4) {
|
||||
xml << " <cx4>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </cx4>\n";
|
||||
}
|
||||
|
||||
if(has_dsp1) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
|
||||
if(dsp1_mapper == DSP1LoROM1MB) {
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='20-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='a0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
} else if(dsp1_mapper == DSP1LoROM2MB) {
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60-6f:0000-3fff'/>\n";
|
||||
xml << " <map address='e0-ef:0000-3fff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='60-6f:4000-7fff'/>\n";
|
||||
xml << " <map address='e0-ef:4000-7fff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
} else if(dsp1_mapper == DSP1HiROM) {
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='00-1f:6000-6fff'/>\n";
|
||||
xml << " <map address='80-9f:6000-6fff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='00-1f:7000-7fff'/>\n";
|
||||
xml << " <map address='80-9f:7000-7fff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
}
|
||||
xml << " </necdsp>\n";
|
||||
}
|
||||
|
||||
if(has_dsp2) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='20-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='a0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " </necdsp>\n";
|
||||
}
|
||||
|
||||
if(has_dsp3) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='20-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='a0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " </necdsp>\n";
|
||||
}
|
||||
|
||||
if(has_dsp4) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='30-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='b0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='30-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='b0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " </necdsp>\n";
|
||||
}
|
||||
|
||||
if(has_obc1) {
|
||||
xml << " <obc1>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </obc1>\n";
|
||||
}
|
||||
|
||||
if(has_st010) {
|
||||
xml << " <necdsp revision='upd96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60:0000'/>\n";
|
||||
xml << " <map address='e0:0000'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='60:0001'/>\n";
|
||||
xml << " <map address='e0:0001'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " <dp>\n";
|
||||
xml << " <map address='68-6f:0000-0fff'/>\n";
|
||||
xml << " <map address='e8-ef:0000-0fff'/>\n";
|
||||
xml << " </dp>\n";
|
||||
xml << " </necdsp>\n";
|
||||
}
|
||||
|
||||
if(has_st011) {
|
||||
xml << " <necdsp revision='upd96050' frequency='15000000' program='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60:0000'/>\n";
|
||||
xml << " <map address='e0:0000'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='60:0001'/>\n";
|
||||
xml << " <map address='e0:0001'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " <dp>\n";
|
||||
xml << " <map address='68-6f:0000-0fff'/>\n";
|
||||
xml << " <map address='e8-ef:0000-0fff'/>\n";
|
||||
xml << " </dp>\n";
|
||||
xml << " </necdsp>\n";
|
||||
}
|
||||
|
||||
if(has_st018) {
|
||||
xml << " <setarisc program='ST-0018'>\n";
|
||||
xml << " <map address='00-3f:3800-38ff'/>\n";
|
||||
xml << " <map address='80-bf:3800-38ff'/>\n";
|
||||
xml << " </setarisc>\n";
|
||||
}
|
||||
|
||||
xml << "</cartridge>\n";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
}
|
||||
|
||||
void SNESCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
rom_size = size;
|
||||
ram_size = 0;
|
||||
|
||||
has_bsx_slot = false;
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
has_srtc = false;
|
||||
has_sdd1 = false;
|
||||
has_spc7110 = false;
|
||||
has_spc7110rtc = false;
|
||||
has_cx4 = false;
|
||||
has_dsp1 = false;
|
||||
has_dsp2 = false;
|
||||
has_dsp3 = false;
|
||||
has_dsp4 = false;
|
||||
has_obc1 = false;
|
||||
has_st010 = false;
|
||||
has_st011 = false;
|
||||
has_st018 = false;
|
||||
|
||||
//=====================
|
||||
//detect Game Boy carts
|
||||
//=====================
|
||||
|
||||
if(size >= 0x0140) {
|
||||
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
|
||||
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
|
||||
type = TypeGameBoy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(size < 32768) {
|
||||
type = TypeUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned index = find_header(data, size);
|
||||
const uint8_t mapperid = data[index + Mapper];
|
||||
const uint8_t rom_type = data[index + RomType];
|
||||
const uint8_t rom_size = data[index + RomSize];
|
||||
const uint8_t company = data[index + Company];
|
||||
const uint8_t regionid = data[index + CartRegion] & 0x7f;
|
||||
|
||||
ram_size = 1024 << (data[index + RamSize] & 7);
|
||||
if(ram_size == 1024) ram_size = 0; //no RAM present
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
|
||||
|
||||
//=======================
|
||||
//detect BS-X flash carts
|
||||
//=======================
|
||||
|
||||
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
||||
if(data[index + 0x14] == 0x00) {
|
||||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
type = TypeBsx;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================
|
||||
//detect Sufami Turbo carts
|
||||
//=========================
|
||||
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
type = TypeSufamiTurboBios;
|
||||
} else {
|
||||
type = TypeSufamiTurbo;
|
||||
}
|
||||
mapper = STROM;
|
||||
region = NTSC; //Sufami Turbo only released in Japan
|
||||
return; //RAM size handled outside this routine
|
||||
}
|
||||
|
||||
//==========================
|
||||
//detect Super Game Boy BIOS
|
||||
//==========================
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
|
||||
type = TypeSuperGameBoy2Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
|
||||
type = TypeSuperGameBoy1Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
//=====================
|
||||
//detect standard carts
|
||||
//=====================
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
if(data[index - 14] == 'Z') {
|
||||
if(data[index - 11] == 'J') {
|
||||
uint8_t n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
has_bsx_slot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsx_slot) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
type = TypeBsxBios;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
} else {
|
||||
type = TypeBsxSlotted;
|
||||
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
region = NTSC; //BS-X slotted cartridges only released in Japan
|
||||
}
|
||||
} else {
|
||||
//standard cart
|
||||
type = TypeNormal;
|
||||
|
||||
if(index == 0x7fc0 && size >= 0x401000) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapperid == 0x32) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
mapper = ExHiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
has_superfx = true;
|
||||
mapper = SuperFXROM;
|
||||
ram_size = 1024 << (data[index - 3] & 7);
|
||||
if(ram_size == 1024) ram_size = 0;
|
||||
}
|
||||
|
||||
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
||||
has_sa1 = true;
|
||||
mapper = SA1ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x35 && rom_type == 0x55) {
|
||||
has_srtc = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
has_sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
has_spc7110 = true;
|
||||
has_spc7110rtc = (rom_type == 0xf9);
|
||||
mapper = SPC7110ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0xf3) {
|
||||
has_cx4 = true;
|
||||
}
|
||||
|
||||
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(has_dsp1 == true) {
|
||||
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
|
||||
dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapperid & 0x2f) == 0x20) {
|
||||
dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapperid & 0x2f) == 0x21) {
|
||||
dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0x05) {
|
||||
has_dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
has_dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x03) {
|
||||
has_dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x25) {
|
||||
has_obc1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
has_st010 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
has_st011 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf5) {
|
||||
has_st018 = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
return 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
return 0x00ffc0;
|
||||
} else {
|
||||
return 0x40ffc0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
|
||||
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
|
||||
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
|
||||
|
||||
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
||||
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
|
||||
|
||||
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
||||
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
||||
if(resetvector < 0x8000) return 0;
|
||||
|
||||
//some images duplicate the header in multiple locations, and others have completely
|
||||
//invalid header information that cannot be relied upon.
|
||||
//below code will analyze the first opcode executed at the specified reset vector to
|
||||
//determine the probability that this is the correct header.
|
||||
|
||||
//most likely opcodes
|
||||
if(resetop == 0x78 //sei
|
||||
|| resetop == 0x18 //clc (clc; xce)
|
||||
|| resetop == 0x38 //sec (sec; xce)
|
||||
|| resetop == 0x9c //stz $nnnn (stz $4200)
|
||||
|| resetop == 0x4c //jmp $nnnn
|
||||
|| resetop == 0x5c //jml $nnnnnn
|
||||
) score += 8;
|
||||
|
||||
//plausible opcodes
|
||||
if(resetop == 0xc2 //rep #$nn
|
||||
|| resetop == 0xe2 //sep #$nn
|
||||
|| resetop == 0xad //lda $nnnn
|
||||
|| resetop == 0xae //ldx $nnnn
|
||||
|| resetop == 0xac //ldy $nnnn
|
||||
|| resetop == 0xaf //lda $nnnnnn
|
||||
|| resetop == 0xa9 //lda #$nn
|
||||
|| resetop == 0xa2 //ldx #$nn
|
||||
|| resetop == 0xa0 //ldy #$nn
|
||||
|| resetop == 0x20 //jsr $nnnn
|
||||
|| resetop == 0x22 //jsl $nnnnnn
|
||||
) score += 4;
|
||||
|
||||
//implausible opcodes
|
||||
if(resetop == 0x40 //rti
|
||||
|| resetop == 0x60 //rts
|
||||
|| resetop == 0x6b //rtl
|
||||
|| resetop == 0xcd //cmp $nnnn
|
||||
|| resetop == 0xec //cpx $nnnn
|
||||
|| resetop == 0xcc //cpy $nnnn
|
||||
) score -= 4;
|
||||
|
||||
//least likely opcodes
|
||||
if(resetop == 0x00 //brk #$nn
|
||||
|| resetop == 0x02 //cop #$nn
|
||||
|| resetop == 0xdb //stp
|
||||
|| resetop == 0x42 //wdm
|
||||
|| resetop == 0xff //sbc $nnnnnn,x
|
||||
) score -= 8;
|
||||
|
||||
//at times, both the header and reset vector's first opcode will match ...
|
||||
//fallback and rely on info validity in these cases to determine more likely header.
|
||||
|
||||
//a valid checksum is the biggest indicator of a valid header.
|
||||
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
|
||||
|
||||
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
|
||||
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
|
||||
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
||||
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
||||
|
||||
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
|
||||
if(data[addr + RomType] < 0x08) score++;
|
||||
if(data[addr + RomSize] < 0x10) score++;
|
||||
if(data[addr + RamSize] < 0x08) score++;
|
||||
if(data[addr + CartRegion] < 14) score++;
|
||||
|
||||
if(score < 0) score = 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
|
||||
if(size < 512) return 0;
|
||||
switch(data[0x0149]) {
|
||||
case 0x00: return 0 * 1024;
|
||||
case 0x01: return 8 * 1024;
|
||||
case 0x02: return 8 * 1024;
|
||||
case 0x03: return 32 * 1024;
|
||||
case 0x04: return 128 * 1024;
|
||||
case 0x05: return 128 * 1024;
|
||||
default: return 128 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
bool SNESCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
|
||||
if(size < 512) return false;
|
||||
if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,458 @@
|
|||
#ifndef NALL_SNES_CPU_HPP
|
||||
#define NALL_SNES_CPU_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SNESCPU {
|
||||
enum : unsigned {
|
||||
Implied, //
|
||||
Constant, //#$00
|
||||
AccumConstant, //#$00
|
||||
IndexConstant, //#$00
|
||||
Direct, //$00
|
||||
DirectX, //$00,x
|
||||
DirectY, //$00,y
|
||||
IDirect, //($00)
|
||||
IDirectX, //($00,x)
|
||||
IDirectY, //($00),y
|
||||
ILDirect, //[$00]
|
||||
ILDirectY, //[$00],y
|
||||
Address, //$0000
|
||||
AddressX, //$0000,x
|
||||
AddressY, //$0000,y
|
||||
IAddressX, //($0000,x)
|
||||
ILAddress, //[$0000]
|
||||
PAddress, //PBR:$0000
|
||||
PIAddress, //PBR:($0000)
|
||||
Long, //$000000
|
||||
LongX, //$000000,x
|
||||
Stack, //$00,s
|
||||
IStackY, //($00,s),y
|
||||
BlockMove, //$00,$00
|
||||
RelativeShort, //+/- $00
|
||||
RelativeLong, //+/- $0000
|
||||
};
|
||||
|
||||
struct OpcodeInfo {
|
||||
char name[4];
|
||||
unsigned mode;
|
||||
};
|
||||
|
||||
static const OpcodeInfo opcodeInfo[256];
|
||||
|
||||
static unsigned getOpcodeLength(bool accum, bool index, uint8_t opcode);
|
||||
static string disassemble(unsigned pc, bool accum, bool index, uint8_t opcode, uint8_t pl, uint8_t ph, uint8_t pb);
|
||||
};
|
||||
|
||||
const SNESCPU::OpcodeInfo SNESCPU::opcodeInfo[256] = {
|
||||
//0x00 - 0x0f
|
||||
{ "brk", Constant },
|
||||
{ "ora", IDirectX },
|
||||
{ "cop", Constant },
|
||||
{ "ora", Stack },
|
||||
|
||||
{ "tsb", Direct },
|
||||
{ "ora", Direct },
|
||||
{ "asl", Direct },
|
||||
{ "ora", ILDirect },
|
||||
|
||||
{ "php", Implied },
|
||||
{ "ora", AccumConstant },
|
||||
{ "asl", Implied },
|
||||
{ "phd", Implied },
|
||||
|
||||
{ "tsb", Address },
|
||||
{ "ora", Address },
|
||||
{ "asl", Address },
|
||||
{ "ora", Long },
|
||||
|
||||
//0x10 - 0x1f
|
||||
{ "bpl", RelativeShort },
|
||||
{ "ora", IDirectY },
|
||||
{ "ora", IDirect },
|
||||
{ "ora", IStackY },
|
||||
|
||||
{ "trb", Direct },
|
||||
{ "ora", DirectX },
|
||||
{ "asl", DirectX },
|
||||
{ "ora", ILDirectY },
|
||||
|
||||
{ "clc", Implied },
|
||||
{ "ora", AddressY },
|
||||
{ "inc", Implied },
|
||||
{ "tcs", Implied },
|
||||
|
||||
{ "trb", Address },
|
||||
{ "ora", AddressX },
|
||||
{ "asl", AddressX },
|
||||
{ "ora", LongX },
|
||||
|
||||
//0x20 - 0x2f
|
||||
{ "jsr", Address },
|
||||
{ "and", IDirectX },
|
||||
{ "jsl", Long },
|
||||
{ "and", Stack },
|
||||
|
||||
{ "bit", Direct },
|
||||
{ "and", Direct },
|
||||
{ "rol", Direct },
|
||||
{ "and", ILDirect },
|
||||
|
||||
{ "plp", Implied },
|
||||
{ "and", AccumConstant },
|
||||
{ "rol", Implied },
|
||||
{ "pld", Implied },
|
||||
|
||||
{ "bit", Address },
|
||||
{ "and", Address },
|
||||
{ "rol", Address },
|
||||
{ "and", Long },
|
||||
|
||||
//0x30 - 0x3f
|
||||
{ "bmi", RelativeShort },
|
||||
{ "and", IDirectY },
|
||||
{ "and", IDirect },
|
||||
{ "and", IStackY },
|
||||
|
||||
{ "bit", DirectX },
|
||||
{ "and", DirectX },
|
||||
{ "rol", DirectX },
|
||||
{ "and", ILDirectY },
|
||||
|
||||
{ "sec", Implied },
|
||||
{ "and", AddressY },
|
||||
{ "dec", Implied },
|
||||
{ "tsc", Implied },
|
||||
|
||||
{ "bit", AddressX },
|
||||
{ "and", AddressX },
|
||||
{ "rol", AddressX },
|
||||
{ "and", LongX },
|
||||
|
||||
//0x40 - 0x4f
|
||||
{ "rti", Implied },
|
||||
{ "eor", IDirectX },
|
||||
{ "wdm", Constant },
|
||||
{ "eor", Stack },
|
||||
|
||||
{ "mvp", BlockMove },
|
||||
{ "eor", Direct },
|
||||
{ "lsr", Direct },
|
||||
{ "eor", ILDirect },
|
||||
|
||||
{ "pha", Implied },
|
||||
{ "eor", AccumConstant },
|
||||
{ "lsr", Implied },
|
||||
{ "phk", Implied },
|
||||
|
||||
{ "jmp", PAddress },
|
||||
{ "eor", Address },
|
||||
{ "lsr", Address },
|
||||
{ "eor", Long },
|
||||
|
||||
//0x50 - 0x5f
|
||||
{ "bvc", RelativeShort },
|
||||
{ "eor", IDirectY },
|
||||
{ "eor", IDirect },
|
||||
{ "eor", IStackY },
|
||||
|
||||
{ "mvn", BlockMove },
|
||||
{ "eor", DirectX },
|
||||
{ "lsr", DirectX },
|
||||
{ "eor", ILDirectY },
|
||||
|
||||
{ "cli", Implied },
|
||||
{ "eor", AddressY },
|
||||
{ "phy", Implied },
|
||||
{ "tcd", Implied },
|
||||
|
||||
{ "jml", Long },
|
||||
{ "eor", AddressX },
|
||||
{ "lsr", AddressX },
|
||||
{ "eor", LongX },
|
||||
|
||||
//0x60 - 0x6f
|
||||
{ "rts", Implied },
|
||||
{ "adc", IDirectX },
|
||||
{ "per", Address },
|
||||
{ "adc", Stack },
|
||||
|
||||
{ "stz", Direct },
|
||||
{ "adc", Direct },
|
||||
{ "ror", Direct },
|
||||
{ "adc", ILDirect },
|
||||
|
||||
{ "pla", Implied },
|
||||
{ "adc", AccumConstant },
|
||||
{ "ror", Implied },
|
||||
{ "rtl", Implied },
|
||||
|
||||
{ "jmp", PIAddress },
|
||||
{ "adc", Address },
|
||||
{ "ror", Address },
|
||||
{ "adc", Long },
|
||||
|
||||
//0x70 - 0x7f
|
||||
{ "bvs", RelativeShort },
|
||||
{ "adc", IDirectY },
|
||||
{ "adc", IDirect },
|
||||
{ "adc", IStackY },
|
||||
|
||||
{ "stz", DirectX },
|
||||
{ "adc", DirectX },
|
||||
{ "ror", DirectX },
|
||||
{ "adc", ILDirectY },
|
||||
|
||||
{ "sei", Implied },
|
||||
{ "adc", AddressY },
|
||||
{ "ply", Implied },
|
||||
{ "tdc", Implied },
|
||||
|
||||
{ "jmp", IAddressX },
|
||||
{ "adc", AddressX },
|
||||
{ "ror", AddressX },
|
||||
{ "adc", LongX },
|
||||
|
||||
//0x80 - 0x8f
|
||||
{ "bra", RelativeShort },
|
||||
{ "sta", IDirectX },
|
||||
{ "brl", RelativeLong },
|
||||
{ "sta", Stack },
|
||||
|
||||
{ "sty", Direct },
|
||||
{ "sta", Direct },
|
||||
{ "stx", Direct },
|
||||
{ "sta", ILDirect },
|
||||
|
||||
{ "dey", Implied },
|
||||
{ "bit", AccumConstant },
|
||||
{ "txa", Implied },
|
||||
{ "phb", Implied },
|
||||
|
||||
{ "sty", Address },
|
||||
{ "sta", Address },
|
||||
{ "stx", Address },
|
||||
{ "sta", Long },
|
||||
|
||||
//0x90 - 0x9f
|
||||
{ "bcc", RelativeShort },
|
||||
{ "sta", IDirectY },
|
||||
{ "sta", IDirect },
|
||||
{ "sta", IStackY },
|
||||
|
||||
{ "sty", DirectX },
|
||||
{ "sta", DirectX },
|
||||
{ "stx", DirectY },
|
||||
{ "sta", ILDirectY },
|
||||
|
||||
{ "tya", Implied },
|
||||
{ "sta", AddressY },
|
||||
{ "txs", Implied },
|
||||
{ "txy", Implied },
|
||||
|
||||
{ "stz", Address },
|
||||
{ "sta", AddressX },
|
||||
{ "stz", AddressX },
|
||||
{ "sta", LongX },
|
||||
|
||||
//0xa0 - 0xaf
|
||||
{ "ldy", IndexConstant },
|
||||
{ "lda", IDirectX },
|
||||
{ "ldx", IndexConstant },
|
||||
{ "lda", Stack },
|
||||
|
||||
{ "ldy", Direct },
|
||||
{ "lda", Direct },
|
||||
{ "ldx", Direct },
|
||||
{ "lda", ILDirect },
|
||||
|
||||
{ "tay", Implied },
|
||||
{ "lda", AccumConstant },
|
||||
{ "tax", Implied },
|
||||
{ "plb", Implied },
|
||||
|
||||
{ "ldy", Address },
|
||||
{ "lda", Address },
|
||||
{ "ldx", Address },
|
||||
{ "lda", Long },
|
||||
|
||||
//0xb0 - 0xbf
|
||||
{ "bcs", RelativeShort },
|
||||
{ "lda", IDirectY },
|
||||
{ "lda", IDirect },
|
||||
{ "lda", IStackY },
|
||||
|
||||
{ "ldy", DirectX },
|
||||
{ "lda", DirectX },
|
||||
{ "ldx", DirectY },
|
||||
{ "lda", ILDirectY },
|
||||
|
||||
{ "clv", Implied },
|
||||
{ "lda", AddressY },
|
||||
{ "tsx", Implied },
|
||||
{ "tyx", Implied },
|
||||
|
||||
{ "ldy", AddressX },
|
||||
{ "lda", AddressX },
|
||||
{ "ldx", AddressY },
|
||||
{ "lda", LongX },
|
||||
|
||||
//0xc0 - 0xcf
|
||||
{ "cpy", IndexConstant },
|
||||
{ "cmp", IDirectX },
|
||||
{ "rep", Constant },
|
||||
{ "cmp", Stack },
|
||||
|
||||
{ "cpy", Direct },
|
||||
{ "cmp", Direct },
|
||||
{ "dec", Direct },
|
||||
{ "cmp", ILDirect },
|
||||
|
||||
{ "iny", Implied },
|
||||
{ "cmp", AccumConstant },
|
||||
{ "dex", Implied },
|
||||
{ "wai", Implied },
|
||||
|
||||
{ "cpy", Address },
|
||||
{ "cmp", Address },
|
||||
{ "dec", Address },
|
||||
{ "cmp", Long },
|
||||
|
||||
//0xd0 - 0xdf
|
||||
{ "bne", RelativeShort },
|
||||
{ "cmp", IDirectY },
|
||||
{ "cmp", IDirect },
|
||||
{ "cmp", IStackY },
|
||||
|
||||
{ "pei", IDirect },
|
||||
{ "cmp", DirectX },
|
||||
{ "dec", DirectX },
|
||||
{ "cmp", ILDirectY },
|
||||
|
||||
{ "cld", Implied },
|
||||
{ "cmp", AddressY },
|
||||
{ "phx", Implied },
|
||||
{ "stp", Implied },
|
||||
|
||||
{ "jmp", ILAddress },
|
||||
{ "cmp", AddressX },
|
||||
{ "dec", AddressX },
|
||||
{ "cmp", LongX },
|
||||
|
||||
//0xe0 - 0xef
|
||||
{ "cpx", IndexConstant },
|
||||
{ "sbc", IDirectX },
|
||||
{ "sep", Constant },
|
||||
{ "sbc", Stack },
|
||||
|
||||
{ "cpx", Direct },
|
||||
{ "sbc", Direct },
|
||||
{ "inc", Direct },
|
||||
{ "sbc", ILDirect },
|
||||
|
||||
{ "inx", Implied },
|
||||
{ "sbc", AccumConstant },
|
||||
{ "nop", Implied },
|
||||
{ "xba", Implied },
|
||||
|
||||
{ "cpx", Address },
|
||||
{ "sbc", Address },
|
||||
{ "inc", Address },
|
||||
{ "sbc", Long },
|
||||
|
||||
//0xf0 - 0xff
|
||||
{ "beq", RelativeShort },
|
||||
{ "sbc", IDirectY },
|
||||
{ "sbc", IDirect },
|
||||
{ "sbc", IStackY },
|
||||
|
||||
{ "pea", Address },
|
||||
{ "sbc", DirectX },
|
||||
{ "inc", DirectX },
|
||||
{ "sbc", ILDirectY },
|
||||
|
||||
{ "sed", Implied },
|
||||
{ "sbc", AddressY },
|
||||
{ "plx", Implied },
|
||||
{ "xce", Implied },
|
||||
|
||||
{ "jsr", IAddressX },
|
||||
{ "sbc", AddressX },
|
||||
{ "inc", AddressX },
|
||||
{ "sbc", LongX },
|
||||
};
|
||||
|
||||
inline unsigned SNESCPU::getOpcodeLength(bool accum, bool index, uint8_t opcode) {
|
||||
switch(opcodeInfo[opcode].mode) { default:
|
||||
case Implied: return 1;
|
||||
case Constant: return 2;
|
||||
case AccumConstant: return 3 - accum;
|
||||
case IndexConstant: return 3 - index;
|
||||
case Direct: return 2;
|
||||
case DirectX: return 2;
|
||||
case DirectY: return 2;
|
||||
case IDirect: return 2;
|
||||
case IDirectX: return 2;
|
||||
case IDirectY: return 2;
|
||||
case ILDirect: return 2;
|
||||
case ILDirectY: return 2;
|
||||
case Address: return 3;
|
||||
case AddressX: return 3;
|
||||
case AddressY: return 3;
|
||||
case IAddressX: return 3;
|
||||
case ILAddress: return 3;
|
||||
case PAddress: return 3;
|
||||
case PIAddress: return 3;
|
||||
case Long: return 4;
|
||||
case LongX: return 4;
|
||||
case Stack: return 2;
|
||||
case IStackY: return 2;
|
||||
case BlockMove: return 3;
|
||||
case RelativeShort: return 2;
|
||||
case RelativeLong: return 3;
|
||||
}
|
||||
}
|
||||
|
||||
inline string SNESCPU::disassemble(unsigned pc, bool accum, bool index, uint8_t opcode, uint8_t pl, uint8_t ph, uint8_t pb) {
|
||||
string name = opcodeInfo[opcode].name;
|
||||
unsigned mode = opcodeInfo[opcode].mode;
|
||||
|
||||
if(mode == Implied) return name;
|
||||
if(mode == Constant) return { name, " #$", hex<2>(pl) };
|
||||
if(mode == AccumConstant) return { name, " #$", accum ? "" : hex<2>(ph), hex<2>(pl) };
|
||||
if(mode == IndexConstant) return { name, " #$", index ? "" : hex<2>(ph), hex<2>(pl) };
|
||||
if(mode == Direct) return { name, " $", hex<2>(pl) };
|
||||
if(mode == DirectX) return { name, " $", hex<2>(pl), ",x" };
|
||||
if(mode == DirectY) return { name, " $", hex<2>(pl), ",y" };
|
||||
if(mode == IDirect) return { name, " ($", hex<2>(pl), ")" };
|
||||
if(mode == IDirectX) return { name, " ($", hex<2>(pl), ",x)" };
|
||||
if(mode == IDirectY) return { name, " ($", hex<2>(pl), "),y" };
|
||||
if(mode == ILDirect) return { name, " [$", hex<2>(pl), "]" };
|
||||
if(mode == ILDirectY) return { name, " [$", hex<2>(pl), "],y" };
|
||||
if(mode == Address) return { name, " $", hex<2>(ph), hex<2>(pl) };
|
||||
if(mode == AddressX) return { name, " $", hex<2>(ph), hex<2>(pl), ",x" };
|
||||
if(mode == AddressY) return { name, " $", hex<2>(ph), hex<2>(pl), ",y" };
|
||||
if(mode == IAddressX) return { name, " ($", hex<2>(ph), hex<2>(pl), ",x)" };
|
||||
if(mode == ILAddress) return { name, " [$", hex<2>(ph), hex<2>(pl), "]" };
|
||||
if(mode == PAddress) return { name, " $", hex<2>(ph), hex<2>(pl) };
|
||||
if(mode == PIAddress) return { name, " ($", hex<2>(ph), hex<2>(pl), ")" };
|
||||
if(mode == Long) return { name, " $", hex<2>(pb), hex<2>(ph), hex<2>(pl) };
|
||||
if(mode == LongX) return { name, " $", hex<2>(pb), hex<2>(ph), hex<2>(pl), ",x" };
|
||||
if(mode == Stack) return { name, " $", hex<2>(pl), ",s" };
|
||||
if(mode == IStackY) return { name, " ($", hex<2>(pl), ",s),y" };
|
||||
if(mode == BlockMove) return { name, " $", hex<2>(ph), ",$", hex<2>(pl) };
|
||||
if(mode == RelativeShort) {
|
||||
unsigned addr = (pc + 2) + (int8_t)(pl << 0);
|
||||
return { name, " $", hex<4>(addr) };
|
||||
}
|
||||
if(mode == RelativeLong) {
|
||||
unsigned addr = (pc + 3) + (int16_t)((ph << 8) + (pl << 0));
|
||||
return { name, " $", hex<4>(addr) };
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,639 @@
|
|||
#ifndef NALL_SNES_SMP_HPP
|
||||
#define NALL_SNES_SMP_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SNESSMP {
|
||||
enum : unsigned {
|
||||
Implied, //
|
||||
TVector, //0
|
||||
Direct, //$00
|
||||
DirectRelative, //$00,+/-$00
|
||||
ADirect, //a,$00
|
||||
AAbsolute, //a,$0000
|
||||
AIX, //a,(x)
|
||||
AIDirectX, //a,($00+x)
|
||||
AConstant, //a,#$00
|
||||
DirectDirect, //$00,$00
|
||||
CAbsoluteBit, //c,$0000:0
|
||||
Absolute, //$0000
|
||||
P, //p
|
||||
AbsoluteA, //$0000,a
|
||||
Relative, //+/-$00
|
||||
ADirectX, //a,$00+x
|
||||
AAbsoluteX, //a,$0000+x
|
||||
AAbsoluteY, //a,$0000+y
|
||||
AIDirectY, //a,($00)+y
|
||||
DirectConstant, //$00,#$00
|
||||
IXIY, //(x),(y)
|
||||
DirectX, //$00+x
|
||||
A, //a
|
||||
X, //x
|
||||
XAbsolute, //x,$0000
|
||||
IAbsoluteX, //($0000+x)
|
||||
CNAbsoluteBit, //c,!$0000:0
|
||||
XDirect, //x,$00
|
||||
PVector, //$ff00
|
||||
YaDirect, //ya,$00
|
||||
XA, //x,a
|
||||
YAbsolute, //y,$0000
|
||||
Y, //y
|
||||
AX, //a,x
|
||||
YDirect, //y,$00
|
||||
YConstant, //y,#$00
|
||||
XSp, //x,sp
|
||||
YaX, //ya,x
|
||||
IXPA, //(x)+,a
|
||||
SpX, //sp,x
|
||||
AIXP, //a,(x)+
|
||||
DirectA, //$00,a
|
||||
IXA, //(x),a
|
||||
IDirectXA, //($00+x),a
|
||||
XConstant, //x,#$00
|
||||
AbsoluteX, //$0000,x
|
||||
AbsoluteBitC, //$0000:0,c
|
||||
DirectY, //$00,y
|
||||
AbsoluteY, //$0000,y
|
||||
Ya, //ya
|
||||
DirectXA, //$00+x,a
|
||||
AbsoluteXA, //$0000+x,a
|
||||
AbsoluteYA, //$0000+y,a
|
||||
IDirectYA, //($00)+y,a
|
||||
DirectYX, //$00+y,x
|
||||
DirectYa, //$00,ya
|
||||
DirectXY, //$00+x,y
|
||||
AY, //a,y
|
||||
DirectXRelative, //$00+x,+/-$00
|
||||
XDirectY, //x,$00+y
|
||||
YDirectX, //y,$00+x
|
||||
YA, //y,a
|
||||
YRelative, //y,+/-$00
|
||||
};
|
||||
|
||||
struct OpcodeInfo {
|
||||
char name[6];
|
||||
unsigned mode;
|
||||
};
|
||||
|
||||
static const OpcodeInfo opcodeInfo[256];
|
||||
|
||||
static unsigned getOpcodeLength(uint8_t opcode);
|
||||
static string disassemble(uint16_t pc, uint8_t opcode, uint8_t pl, uint8_t ph);
|
||||
static string disassemble(uint16_t pc, bool p, uint8_t opcode, uint8_t pl, uint8_t ph);
|
||||
};
|
||||
|
||||
const SNESSMP::OpcodeInfo SNESSMP::opcodeInfo[256] = {
|
||||
//0x00 - 0x0f
|
||||
{ "nop ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set0 ", Direct },
|
||||
{ "bbs0 ", DirectRelative },
|
||||
|
||||
{ "or ", ADirect },
|
||||
{ "or ", AAbsolute },
|
||||
{ "or ", AIX },
|
||||
{ "or ", AIDirectX },
|
||||
|
||||
{ "or ", AConstant },
|
||||
{ "or ", DirectDirect },
|
||||
{ "or1 ", CAbsoluteBit },
|
||||
{ "asl ", Direct },
|
||||
|
||||
{ "asl ", Absolute },
|
||||
{ "push ", P },
|
||||
{ "tset ", AbsoluteA },
|
||||
{ "brk ", Implied },
|
||||
|
||||
//0x10 - 0x1f
|
||||
{ "bpl ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr0 ", Direct },
|
||||
{ "bbc0 ", DirectRelative },
|
||||
|
||||
{ "or ", ADirectX },
|
||||
{ "or ", AAbsoluteX },
|
||||
{ "or ", AAbsoluteY },
|
||||
{ "or ", AIDirectY },
|
||||
|
||||
{ "or ", DirectConstant },
|
||||
{ "or ", IXIY },
|
||||
{ "decw ", Direct },
|
||||
{ "asl ", DirectX },
|
||||
|
||||
{ "asl ", A },
|
||||
{ "dec ", X },
|
||||
{ "cmp ", XAbsolute },
|
||||
{ "jmp ", IAbsoluteX },
|
||||
|
||||
//0x20 - 0x2f
|
||||
{ "clrp ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set1 ", Direct },
|
||||
{ "bbs1 ", DirectRelative },
|
||||
|
||||
{ "and ", ADirect },
|
||||
{ "and ", AAbsolute },
|
||||
{ "and ", AIX },
|
||||
{ "and ", AIDirectX },
|
||||
|
||||
{ "and ", AConstant },
|
||||
{ "and ", DirectDirect },
|
||||
{ "or1 ", CNAbsoluteBit },
|
||||
{ "rol ", Direct },
|
||||
|
||||
{ "rol ", Absolute },
|
||||
{ "push ", A },
|
||||
{ "cbne ", DirectRelative },
|
||||
{ "bra ", Relative },
|
||||
|
||||
//0x30 - 0x3f
|
||||
{ "bmi ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr1 ", Direct },
|
||||
{ "bbc1 ", DirectRelative },
|
||||
|
||||
{ "and ", ADirectX },
|
||||
{ "and ", AAbsoluteX },
|
||||
{ "and ", AAbsoluteY },
|
||||
{ "and ", AIDirectY },
|
||||
|
||||
{ "and ", DirectConstant },
|
||||
{ "and ", IXIY },
|
||||
{ "incw ", Direct },
|
||||
{ "rol ", DirectX },
|
||||
|
||||
{ "rol ", A },
|
||||
{ "inc ", X },
|
||||
{ "cmp ", XDirect },
|
||||
{ "call ", Absolute },
|
||||
|
||||
//0x40 - 0x4f
|
||||
{ "setp ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set2 ", Direct },
|
||||
{ "bbs2 ", DirectRelative },
|
||||
|
||||
{ "eor ", ADirect },
|
||||
{ "eor ", AAbsolute },
|
||||
{ "eor ", AIX },
|
||||
{ "eor ", AIDirectX },
|
||||
|
||||
{ "eor ", AConstant },
|
||||
{ "eor ", DirectDirect },
|
||||
{ "and1 ", CAbsoluteBit },
|
||||
{ "lsr ", Direct },
|
||||
|
||||
{ "lsr ", Absolute },
|
||||
{ "push ", X },
|
||||
{ "tclr ", AbsoluteA },
|
||||
{ "pcall", PVector },
|
||||
|
||||
//0x50 - 0x5f
|
||||
{ "bvc ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr2 ", Direct },
|
||||
{ "bbc2 ", DirectRelative },
|
||||
|
||||
{ "eor ", ADirectX },
|
||||
{ "eor ", AAbsoluteX },
|
||||
{ "eor ", AAbsoluteY },
|
||||
{ "eor ", AIDirectY },
|
||||
|
||||
{ "eor ", DirectConstant },
|
||||
{ "eor ", IXIY },
|
||||
{ "cmpw ", YaDirect },
|
||||
{ "lsr ", DirectX },
|
||||
|
||||
{ "lsr ", A },
|
||||
{ "mov ", XA },
|
||||
{ "cmp ", YAbsolute },
|
||||
{ "jmp ", Absolute },
|
||||
|
||||
//0x60 - 0x6f
|
||||
{ "clrc ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set3 ", Direct },
|
||||
{ "bbs3 ", DirectRelative },
|
||||
|
||||
{ "cmp ", ADirect },
|
||||
{ "cmp ", AAbsolute },
|
||||
{ "cmp ", AIX },
|
||||
{ "cmp ", AIDirectX },
|
||||
|
||||
{ "cmp ", AConstant },
|
||||
{ "cmp ", DirectDirect },
|
||||
{ "and1 ", CNAbsoluteBit },
|
||||
{ "ror ", Direct },
|
||||
|
||||
{ "ror ", Absolute },
|
||||
{ "push ", Y },
|
||||
{ "dbnz ", DirectRelative },
|
||||
{ "ret ", Implied },
|
||||
|
||||
//0x70 - 0x7f
|
||||
{ "bvs ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr3 ", Direct },
|
||||
{ "bbc3 ", DirectRelative },
|
||||
|
||||
{ "cmp ", ADirectX },
|
||||
{ "cmp ", AAbsoluteX },
|
||||
{ "cmp ", AAbsoluteY },
|
||||
{ "cmp ", AIDirectY },
|
||||
|
||||
{ "cmp ", DirectConstant },
|
||||
{ "cmp ", IXIY },
|
||||
{ "addw ", YaDirect },
|
||||
{ "ror ", DirectX },
|
||||
|
||||
{ "ror ", A },
|
||||
{ "mov ", AX },
|
||||
{ "cmp ", YDirect },
|
||||
{ "reti ", Implied },
|
||||
|
||||
//0x80 - 0x8f
|
||||
{ "setc ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set4 ", Direct },
|
||||
{ "bbs4 ", DirectRelative },
|
||||
|
||||
{ "adc ", ADirect },
|
||||
{ "adc ", AAbsolute },
|
||||
{ "adc ", AIX },
|
||||
{ "adc ", AIDirectX },
|
||||
|
||||
{ "adc ", AConstant },
|
||||
{ "adc ", DirectDirect },
|
||||
{ "eor1 ", CAbsoluteBit },
|
||||
{ "dec ", Direct },
|
||||
|
||||
{ "dec ", Absolute },
|
||||
{ "mov ", YConstant },
|
||||
{ "pop ", P },
|
||||
{ "mov ", DirectConstant },
|
||||
|
||||
//0x90 - 0x9f
|
||||
{ "bcc ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr4 ", Direct },
|
||||
{ "bbc4 ", DirectRelative },
|
||||
|
||||
{ "adc ", ADirectX },
|
||||
{ "adc ", AAbsoluteX },
|
||||
{ "adc ", AAbsoluteY },
|
||||
{ "adc ", AIDirectY },
|
||||
|
||||
{ "adc ", DirectRelative },
|
||||
{ "adc ", IXIY },
|
||||
{ "subw ", YaDirect },
|
||||
{ "dec ", DirectX },
|
||||
|
||||
{ "dec ", A },
|
||||
{ "mov ", XSp },
|
||||
{ "div ", YaX },
|
||||
{ "xcn ", A },
|
||||
|
||||
//0xa0 - 0xaf
|
||||
{ "ei ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set5 ", Direct },
|
||||
{ "bbs5 ", DirectRelative },
|
||||
|
||||
{ "sbc ", ADirect },
|
||||
{ "sbc ", AAbsolute },
|
||||
{ "sbc ", AIX },
|
||||
{ "sbc ", AIDirectX },
|
||||
|
||||
{ "sbc ", AConstant },
|
||||
{ "sbc ", DirectDirect },
|
||||
{ "mov1 ", CAbsoluteBit },
|
||||
{ "inc ", Direct },
|
||||
|
||||
{ "inc ", Absolute },
|
||||
{ "cmp ", YConstant },
|
||||
{ "pop ", A },
|
||||
{ "mov ", IXPA },
|
||||
|
||||
//0xb0 - 0xbf
|
||||
{ "bcs ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr5 ", Direct },
|
||||
{ "bbc5 ", DirectRelative },
|
||||
|
||||
{ "sbc ", ADirectX },
|
||||
{ "sbc ", AAbsoluteX },
|
||||
{ "sbc ", AAbsoluteY },
|
||||
{ "sbc ", AIDirectY },
|
||||
|
||||
{ "sbc ", DirectConstant },
|
||||
{ "sbc ", IXIY },
|
||||
{ "movw ", YaDirect },
|
||||
{ "inc ", DirectX },
|
||||
|
||||
{ "inc ", A },
|
||||
{ "mov ", SpX },
|
||||
{ "das ", A },
|
||||
{ "mov ", AIXP },
|
||||
|
||||
//0xc0 - 0xcf
|
||||
{ "di ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set6 ", Direct },
|
||||
{ "bbs6 ", DirectRelative },
|
||||
|
||||
{ "mov ", DirectA },
|
||||
{ "mov ", AbsoluteA },
|
||||
{ "mov ", IXA },
|
||||
{ "mov ", IDirectXA },
|
||||
|
||||
{ "cmp ", XConstant },
|
||||
{ "mov ", AbsoluteX },
|
||||
{ "mov1 ", AbsoluteBitC },
|
||||
{ "mov ", DirectY },
|
||||
|
||||
{ "mov ", AbsoluteY },
|
||||
{ "mov ", XConstant },
|
||||
{ "pop ", X },
|
||||
{ "mul ", Ya },
|
||||
|
||||
//0xd0 - 0xdf
|
||||
{ "bne ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr6 ", Relative },
|
||||
{ "bbc6 ", DirectRelative },
|
||||
|
||||
{ "mov ", DirectXA },
|
||||
{ "mov ", AbsoluteXA },
|
||||
{ "mov ", AbsoluteYA },
|
||||
{ "mov ", IDirectYA },
|
||||
|
||||
{ "mov ", DirectX },
|
||||
{ "mov ", DirectYX },
|
||||
{ "movw ", DirectYa },
|
||||
{ "mov ", DirectXY },
|
||||
|
||||
{ "dec ", Y },
|
||||
{ "mov ", AY },
|
||||
{ "cbne ", DirectXRelative },
|
||||
{ "daa ", A },
|
||||
|
||||
//0xe0 - 0xef
|
||||
{ "clrv ", Implied },
|
||||
{ "tcall", TVector },
|
||||
{ "set7 ", Direct },
|
||||
{ "bbs7 ", DirectRelative },
|
||||
|
||||
{ "mov ", ADirect },
|
||||
{ "mov ", AAbsolute },
|
||||
{ "mov ", AIX },
|
||||
{ "mov ", AIDirectX },
|
||||
|
||||
{ "mov ", AConstant },
|
||||
{ "mov ", XAbsolute },
|
||||
{ "not1 ", CAbsoluteBit },
|
||||
{ "mov ", YDirect },
|
||||
|
||||
{ "mov ", YAbsolute },
|
||||
{ "notc ", Implied },
|
||||
{ "pop ", Y },
|
||||
{ "sleep", Implied },
|
||||
|
||||
//0xf0 - 0xff
|
||||
{ "beq ", Relative },
|
||||
{ "tcall", TVector },
|
||||
{ "clr7 ", Direct },
|
||||
{ "bbc7 ", DirectRelative },
|
||||
|
||||
{ "mov ", ADirectX },
|
||||
{ "mov ", AAbsoluteX },
|
||||
{ "mov ", AAbsoluteY },
|
||||
{ "mov ", AIDirectY },
|
||||
|
||||
{ "mov ", XDirect },
|
||||
{ "mov ", XDirectY },
|
||||
{ "mov ", DirectDirect },
|
||||
{ "mov ", YDirectX },
|
||||
|
||||
{ "inc ", Y },
|
||||
{ "mov ", YA },
|
||||
{ "dbz ", YRelative },
|
||||
{ "stop ", Implied },
|
||||
};
|
||||
|
||||
inline unsigned SNESSMP::getOpcodeLength(uint8_t opcode) {
|
||||
switch(opcodeInfo[opcode].mode) { default:
|
||||
case Implied: return 1; //
|
||||
case TVector: return 1; //0
|
||||
case Direct: return 2; //$00
|
||||
case DirectRelative: return 3; //$00,+/-$00
|
||||
case ADirect: return 2; //a,$00
|
||||
case AAbsolute: return 3; //a,$0000
|
||||
case AIX: return 1; //a,(x)
|
||||
case AIDirectX: return 2; //a,($00+x)
|
||||
case AConstant: return 2; //a,#$00
|
||||
case DirectDirect: return 3; //$00,$00
|
||||
case CAbsoluteBit: return 3; //c,$0000:0
|
||||
case Absolute: return 3; //$0000
|
||||
case P: return 1; //p
|
||||
case AbsoluteA: return 3; //$0000,a
|
||||
case Relative: return 2; //+/-$00
|
||||
case ADirectX: return 2; //a,$00+x
|
||||
case AAbsoluteX: return 3; //a,$0000+x
|
||||
case AAbsoluteY: return 3; //a,$0000+y
|
||||
case AIDirectY: return 2; //a,($00)+y
|
||||
case DirectConstant: return 3; //$00,#$00
|
||||
case IXIY: return 1; //(x),(y)
|
||||
case DirectX: return 2; //$00+x
|
||||
case A: return 1; //a
|
||||
case X: return 1; //x
|
||||
case XAbsolute: return 3; //x,$0000
|
||||
case IAbsoluteX: return 3; //($0000+x)
|
||||
case CNAbsoluteBit: return 3; //c,!$0000:0
|
||||
case XDirect: return 2; //x,$00
|
||||
case PVector: return 2; //$ff00
|
||||
case YaDirect: return 2; //ya,$00
|
||||
case XA: return 1; //x,a
|
||||
case YAbsolute: return 3; //y,$0000
|
||||
case Y: return 1; //y
|
||||
case AX: return 1; //a,x
|
||||
case YDirect: return 2; //y,$00
|
||||
case YConstant: return 2; //y,#$00
|
||||
case XSp: return 1; //x,sp
|
||||
case YaX: return 1; //ya,x
|
||||
case IXPA: return 1; //(x)+,a
|
||||
case SpX: return 1; //sp,x
|
||||
case AIXP: return 1; //a,(x)+
|
||||
case DirectA: return 2; //$00,a
|
||||
case IXA: return 1; //(x),a
|
||||
case IDirectXA: return 2; //($00+x),a
|
||||
case XConstant: return 2; //x,#$00
|
||||
case AbsoluteX: return 3; //$0000,x
|
||||
case AbsoluteBitC: return 3; //$0000:0,c
|
||||
case DirectY: return 2; //$00,y
|
||||
case AbsoluteY: return 3; //$0000,y
|
||||
case Ya: return 1; //ya
|
||||
case DirectXA: return 2; //$00+x,a
|
||||
case AbsoluteXA: return 3; //$0000+x,a
|
||||
case AbsoluteYA: return 3; //$0000+y,a
|
||||
case IDirectYA: return 2; //($00)+y,a
|
||||
case DirectYX: return 2; //$00+y,x
|
||||
case DirectYa: return 2; //$00,ya
|
||||
case DirectXY: return 2; //$00+x,y
|
||||
case AY: return 1; //a,y
|
||||
case DirectXRelative: return 3; //$00+x,+/-$00
|
||||
case XDirectY: return 2; //x,$00+y
|
||||
case YDirectX: return 2; //y,$00+x
|
||||
case YA: return 1; //y,a
|
||||
case YRelative: return 2; //y,+/-$00
|
||||
}
|
||||
}
|
||||
|
||||
inline string SNESSMP::disassemble(uint16_t pc, uint8_t opcode, uint8_t pl, uint8_t ph) {
|
||||
string name = opcodeInfo[opcode].name;
|
||||
unsigned mode = opcodeInfo[opcode].mode;
|
||||
unsigned pa = (ph << 8) + pl;
|
||||
|
||||
if(mode == Implied) return name;
|
||||
if(mode == TVector) return { name, " ", opcode >> 4 };
|
||||
if(mode == Direct) return { name, " $", hex<2>(pl) };
|
||||
if(mode == DirectRelative) return { name, " $", hex<2>(pl), ",$", hex<4>(pc + 3 + (int8_t)ph) };
|
||||
if(mode == ADirect) return { name, " a,$", hex<2>(pl) };
|
||||
if(mode == AAbsolute) return { name, " a,$", hex<4>(pa) };
|
||||
if(mode == AIX) return { name, "a,(x)" };
|
||||
if(mode == AIDirectX) return { name, " a,($", hex<2>(pl), "+x)" };
|
||||
if(mode == AConstant) return { name, " a,#$", hex<2>(pl) };
|
||||
if(mode == DirectDirect) return { name, " $", hex<2>(ph), ",$", hex<2>(pl) };
|
||||
if(mode == CAbsoluteBit) return { name, " c,$", hex<4>(pa & 0x1fff), ":", pa >> 13 };
|
||||
if(mode == Absolute) return { name, " $", hex<4>(pa) };
|
||||
if(mode == P) return { name, " p" };
|
||||
if(mode == AbsoluteA) return { name, " $", hex<4>(pa), ",a" };
|
||||
if(mode == Relative) return { name, " $", hex<4>(pc + 2 + (int8_t)pl) };
|
||||
if(mode == ADirectX) return { name, " a,$", hex<2>(pl), "+x" };
|
||||
if(mode == AAbsoluteX) return { name, " a,$", hex<4>(pa), "+x" };
|
||||
if(mode == AAbsoluteY) return { name, " a,$", hex<4>(pa), "+y" };
|
||||
if(mode == AIDirectY) return { name, " a,($", hex<2>(pl), ")+y" };
|
||||
if(mode == DirectConstant) return { name, " $", hex<2>(ph), ",#$", hex<2>(pl) };
|
||||
if(mode == IXIY) return { name, " (x),(y)" };
|
||||
if(mode == DirectX) return { name, " $", hex<2>(pl), "+x" };
|
||||
if(mode == A) return { name, " a" };
|
||||
if(mode == X) return { name, " x" };
|
||||
if(mode == XAbsolute) return { name, " x,$", hex<4>(pa) };
|
||||
if(mode == IAbsoluteX) return { name, " ($", hex<4>(pa), "+x)" };
|
||||
if(mode == CNAbsoluteBit) return { name, " c,!$", hex<4>(pa & 0x1fff), ":", pa >> 13 };
|
||||
if(mode == XDirect) return { name, " x,$", hex<2>(pl) };
|
||||
if(mode == PVector) return { name, " $ff", hex<2>(pl) };
|
||||
if(mode == YaDirect) return { name, " ya,$", hex<2>(pl) };
|
||||
if(mode == XA) return { name, " x,a" };
|
||||
if(mode == YAbsolute) return { name, " y,$", hex<4>(pa) };
|
||||
if(mode == Y) return { name, " y" };
|
||||
if(mode == AX) return { name, " a,x" };
|
||||
if(mode == YDirect) return { name, " y,$", hex<2>(pl) };
|
||||
if(mode == YConstant) return { name, " y,#$", hex<2>(pl) };
|
||||
if(mode == XSp) return { name, " x,sp" };
|
||||
if(mode == YaX) return { name, " ya,x" };
|
||||
if(mode == IXPA) return { name, " (x)+,a" };
|
||||
if(mode == SpX) return { name, " sp,x" };
|
||||
if(mode == AIXP) return { name, " a,(x)+" };
|
||||
if(mode == DirectA) return { name, " $", hex<2>(pl), ",a" };
|
||||
if(mode == IXA) return { name, " (x),a" };
|
||||
if(mode == IDirectXA) return { name, " ($", hex<2>(pl), "+x),a" };
|
||||
if(mode == XConstant) return { name, " x,#$", hex<2>(pl) };
|
||||
if(mode == AbsoluteX) return { name, " $", hex<4>(pa), ",x" };
|
||||
if(mode == AbsoluteBitC) return { name, " $", hex<4>(pa & 0x1fff), ":", pa >> 13, ",c" };
|
||||
if(mode == DirectY) return { name, " $", hex<2>(pl), ",y" };
|
||||
if(mode == AbsoluteY) return { name, " $", hex<4>(pa), ",y" };
|
||||
if(mode == Ya) return { name, " ya" };
|
||||
if(mode == DirectXA) return { name, " $", hex<2>(pl), "+x,a" };
|
||||
if(mode == AbsoluteXA) return { name, " $", hex<4>(pa), "+x,a" };
|
||||
if(mode == AbsoluteYA) return { name, " $", hex<4>(pa), "+y,a" };
|
||||
if(mode == IDirectYA) return { name, " ($", hex<2>(pl), ")+y,a" };
|
||||
if(mode == DirectYX) return { name, " $", hex<2>(pl), "+y,x" };
|
||||
if(mode == DirectYa) return { name, " $", hex<2>(pl), ",ya" };
|
||||
if(mode == DirectXY) return { name, " $", hex<2>(pl), "+x,y" };
|
||||
if(mode == AY) return { name, " a,y" };
|
||||
if(mode == DirectXRelative) return { name, " $", hex<2>(pl), ",$", hex<4>(pc + 3 + (int8_t)ph) };
|
||||
if(mode == XDirectY) return { name, " x,$", hex<2>(pl), "+y" };
|
||||
if(mode == YDirectX) return { name, " y,$", hex<2>(pl), "+x" };
|
||||
if(mode == YA) return { name, " y,a" };
|
||||
if(mode == YRelative) return { name, " y,$", hex<4>(pc + 2 + (int8_t)pl) };
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
inline string SNESSMP::disassemble(uint16_t pc, bool p, uint8_t opcode, uint8_t pl, uint8_t ph) {
|
||||
string name = opcodeInfo[opcode].name;
|
||||
unsigned mode = opcodeInfo[opcode].mode;
|
||||
unsigned pdl = (p << 8) + pl;
|
||||
unsigned pdh = (p << 8) + ph;
|
||||
unsigned pa = (ph << 8) + pl;
|
||||
|
||||
if(mode == Implied) return name;
|
||||
if(mode == TVector) return { name, " ", opcode >> 4 };
|
||||
if(mode == Direct) return { name, " $", hex<3>(pdl) };
|
||||
if(mode == DirectRelative) return { name, " $", hex<3>(pdl), ",$", hex<4>(pc + 3 + (int8_t)ph) };
|
||||
if(mode == ADirect) return { name, " a,$", hex<3>(pdl) };
|
||||
if(mode == AAbsolute) return { name, " a,$", hex<4>(pa) };
|
||||
if(mode == AIX) return { name, "a,(x)" };
|
||||
if(mode == AIDirectX) return { name, " a,($", hex<3>(pdl), "+x)" };
|
||||
if(mode == AConstant) return { name, " a,#$", hex<2>(pl) };
|
||||
if(mode == DirectDirect) return { name, " $", hex<3>(pdh), ",$", hex<3>(pdl) };
|
||||
if(mode == CAbsoluteBit) return { name, " c,$", hex<4>(pa & 0x1fff), ":", pa >> 13 };
|
||||
if(mode == Absolute) return { name, " $", hex<4>(pa) };
|
||||
if(mode == P) return { name, " p" };
|
||||
if(mode == AbsoluteA) return { name, " $", hex<4>(pa), ",a" };
|
||||
if(mode == Relative) return { name, " $", hex<4>(pc + 2 + (int8_t)pl) };
|
||||
if(mode == ADirectX) return { name, " a,$", hex<3>(pdl), "+x" };
|
||||
if(mode == AAbsoluteX) return { name, " a,$", hex<4>(pa), "+x" };
|
||||
if(mode == AAbsoluteY) return { name, " a,$", hex<4>(pa), "+y" };
|
||||
if(mode == AIDirectY) return { name, " a,($", hex<3>(pdl), ")+y" };
|
||||
if(mode == DirectConstant) return { name, " $", hex<3>(pdh), ",#$", hex<2>(pl) };
|
||||
if(mode == IXIY) return { name, " (x),(y)" };
|
||||
if(mode == DirectX) return { name, " $", hex<3>(pdl), "+x" };
|
||||
if(mode == A) return { name, " a" };
|
||||
if(mode == X) return { name, " x" };
|
||||
if(mode == XAbsolute) return { name, " x,$", hex<4>(pa) };
|
||||
if(mode == IAbsoluteX) return { name, " ($", hex<4>(pa), "+x)" };
|
||||
if(mode == CNAbsoluteBit) return { name, " c,!$", hex<4>(pa & 0x1fff), ":", pa >> 13 };
|
||||
if(mode == XDirect) return { name, " x,$", hex<3>(pdl) };
|
||||
if(mode == PVector) return { name, " $ff", hex<2>(pl) };
|
||||
if(mode == YaDirect) return { name, " ya,$", hex<3>(pdl) };
|
||||
if(mode == XA) return { name, " x,a" };
|
||||
if(mode == YAbsolute) return { name, " y,$", hex<4>(pa) };
|
||||
if(mode == Y) return { name, " y" };
|
||||
if(mode == AX) return { name, " a,x" };
|
||||
if(mode == YDirect) return { name, " y,$", hex<3>(pdl) };
|
||||
if(mode == YConstant) return { name, " y,#$", hex<2>(pl) };
|
||||
if(mode == XSp) return { name, " x,sp" };
|
||||
if(mode == YaX) return { name, " ya,x" };
|
||||
if(mode == IXPA) return { name, " (x)+,a" };
|
||||
if(mode == SpX) return { name, " sp,x" };
|
||||
if(mode == AIXP) return { name, " a,(x)+" };
|
||||
if(mode == DirectA) return { name, " $", hex<3>(pdl), ",a" };
|
||||
if(mode == IXA) return { name, " (x),a" };
|
||||
if(mode == IDirectXA) return { name, " ($", hex<3>(pdl), "+x),a" };
|
||||
if(mode == XConstant) return { name, " x,#$", hex<2>(pl) };
|
||||
if(mode == AbsoluteX) return { name, " $", hex<4>(pa), ",x" };
|
||||
if(mode == AbsoluteBitC) return { name, " $", hex<4>(pa & 0x1fff), ":", pa >> 13, ",c" };
|
||||
if(mode == DirectY) return { name, " $", hex<3>(pdl), ",y" };
|
||||
if(mode == AbsoluteY) return { name, " $", hex<4>(pa), ",y" };
|
||||
if(mode == Ya) return { name, " ya" };
|
||||
if(mode == DirectXA) return { name, " $", hex<3>(pdl), "+x,a" };
|
||||
if(mode == AbsoluteXA) return { name, " $", hex<4>(pa), "+x,a" };
|
||||
if(mode == AbsoluteYA) return { name, " $", hex<4>(pa), "+y,a" };
|
||||
if(mode == IDirectYA) return { name, " ($", hex<3>(pdl), ")+y,a" };
|
||||
if(mode == DirectYX) return { name, " $", hex<3>(pdl), "+y,x" };
|
||||
if(mode == DirectYa) return { name, " $", hex<3>(pdl), ",ya" };
|
||||
if(mode == DirectXY) return { name, " $", hex<3>(pdl), "+x,y" };
|
||||
if(mode == AY) return { name, " a,y" };
|
||||
if(mode == DirectXRelative) return { name, " $", hex<3>(pdl), ",$", hex<4>(pc + 3 + (int8_t)ph) };
|
||||
if(mode == XDirectY) return { name, " x,$", hex<3>(pdl), "+y" };
|
||||
if(mode == YDirectX) return { name, " y,$", hex<3>(pdl), "+x" };
|
||||
if(mode == YA) return { name, " y,a" };
|
||||
if(mode == YRelative) return { name, " y,$", hex<4>(pc + 2 + (int8_t)pl) };
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef NALL_SORT_HPP
|
||||
#define NALL_SORT_HPP
|
||||
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
//class: merge sort
|
||||
//average: O(n log n)
|
||||
//worst: O(n log n)
|
||||
//memory: O(n)
|
||||
//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.)
|
||||
|
||||
namespace nall {
|
||||
template<typename T>
|
||||
void sort(T list[], unsigned length) {
|
||||
if(length <= 1) return; //nothing to sort
|
||||
|
||||
//use insertion sort to quickly sort smaller blocks
|
||||
if(length < 64) {
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
unsigned min = i;
|
||||
for(unsigned j = i + 1; j < length; j++) {
|
||||
if(list[j] < list[min]) min = j;
|
||||
}
|
||||
if(min != i) swap(list[i], list[min]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//split list in half and recursively sort both
|
||||
unsigned middle = length / 2;
|
||||
sort(list, middle);
|
||||
sort(list + middle, length - middle);
|
||||
|
||||
//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++];
|
||||
} else {
|
||||
buffer[offset++] = list[right++];
|
||||
}
|
||||
}
|
||||
while(left < middle) buffer[offset++] = list[left++];
|
||||
while(right < length) buffer[offset++] = list[right++];
|
||||
|
||||
for(unsigned i = 0; i < length; i++) list[i] = buffer[i];
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#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
|
|
@ -0,0 +1,44 @@
|
|||
#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;
|
||||
typedef signed int int32_t;
|
||||
typedef signed long long int64_t;
|
||||
typedef int64_t intmax_t;
|
||||
#if defined(_WIN64)
|
||||
typedef int64_t intptr_t;
|
||||
#else
|
||||
typedef int32_t intptr_t;
|
||||
#endif
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
#if defined(_WIN64)
|
||||
typedef uint64_t uintptr_t;
|
||||
#else
|
||||
typedef uint32_t uintptr_t;
|
||||
#endif
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" );
|
||||
static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size");
|
||||
static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size");
|
||||
static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size");
|
||||
|
||||
static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" );
|
||||
static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size");
|
||||
static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size");
|
||||
static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size");
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef NALL_STRING_HPP
|
||||
#define NALL_STRING_HPP
|
||||
|
||||
#include <initializer_list>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
#include <nall/string/base.hpp>
|
||||
#include <nall/string/bsv.hpp>
|
||||
#include <nall/string/core.hpp>
|
||||
#include <nall/string/cast.hpp>
|
||||
#include <nall/string/compare.hpp>
|
||||
#include <nall/string/convert.hpp>
|
||||
#include <nall/string/filename.hpp>
|
||||
#include <nall/string/math.hpp>
|
||||
#include <nall/string/platform.hpp>
|
||||
#include <nall/string/strl.hpp>
|
||||
#include <nall/string/strpos.hpp>
|
||||
#include <nall/string/trim.hpp>
|
||||
#include <nall/string/replace.hpp>
|
||||
#include <nall/string/split.hpp>
|
||||
#include <nall/string/utility.hpp>
|
||||
#include <nall/string/variadic.hpp>
|
||||
#include <nall/string/wrapper.hpp>
|
||||
#include <nall/string/xml.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<> struct has_length<string> { enum { value = true }; };
|
||||
template<> struct has_size<lstring> { enum { value = true }; };
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,159 @@
|
|||
#ifndef NALL_STRING_BASE_HPP
|
||||
#define NALL_STRING_BASE_HPP
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <nall/concept.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
class string;
|
||||
template<typename T> inline string to_string(T);
|
||||
|
||||
class string {
|
||||
public:
|
||||
inline void reserve(unsigned);
|
||||
|
||||
inline string& assign(const char*);
|
||||
inline string& append(const char*);
|
||||
inline string& append(bool);
|
||||
inline string& append(signed int value);
|
||||
inline string& append(unsigned int value);
|
||||
inline string& append(double value);
|
||||
|
||||
inline bool readfile(const char*);
|
||||
|
||||
inline string& replace (const char*, const char*);
|
||||
inline string& qreplace(const char*, const char*);
|
||||
|
||||
inline unsigned length() const;
|
||||
|
||||
inline bool equals(const char*) const;
|
||||
inline bool iequals(const char*) const;
|
||||
|
||||
inline bool wildcard(const char*) const;
|
||||
inline bool iwildcard(const char*) const;
|
||||
|
||||
inline bool beginswith(const char*) const;
|
||||
inline bool ibeginswith(const char*) const;
|
||||
inline bool endswith(const char*) const;
|
||||
inline bool iendswith(const char*) const;
|
||||
|
||||
inline string& lower();
|
||||
inline string& upper();
|
||||
inline string& transform(const char *before, const char *after);
|
||||
|
||||
template<unsigned limit = 0> inline string& ltrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& rtrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& trim (const char *key = " ");
|
||||
|
||||
inline optional<unsigned> position(const char *key) const;
|
||||
inline optional<unsigned> qposition(const char *key) const;
|
||||
|
||||
template<typename T> inline string& operator= (T value);
|
||||
template<typename T> inline string& operator<<(T value);
|
||||
|
||||
inline operator const char*() const;
|
||||
inline char* operator()();
|
||||
inline char& operator[](int);
|
||||
|
||||
inline bool operator==(const char*) const;
|
||||
inline bool operator!=(const char*) const;
|
||||
inline bool operator< (const char*) const;
|
||||
inline bool operator<=(const char*) const;
|
||||
inline bool operator> (const char*) const;
|
||||
inline bool operator>=(const char*) const;
|
||||
|
||||
inline string& operator=(const string&);
|
||||
inline string& operator=(string&&);
|
||||
|
||||
template<typename... Args> inline string(Args&&... args);
|
||||
inline string(const string&);
|
||||
inline string(string&&);
|
||||
inline ~string();
|
||||
|
||||
protected:
|
||||
char *data;
|
||||
unsigned size;
|
||||
|
||||
#if defined(QSTRING_H)
|
||||
public:
|
||||
inline operator QString() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
class lstring : public linear_vector<string> {
|
||||
public:
|
||||
template<typename T> inline lstring& operator<<(T value);
|
||||
|
||||
inline optional<unsigned> find(const char*) const;
|
||||
template<unsigned limit = 0> inline void split (const char*, const char*);
|
||||
template<unsigned limit = 0> inline void qsplit(const char*, const char*);
|
||||
|
||||
lstring();
|
||||
lstring(std::initializer_list<string>);
|
||||
};
|
||||
|
||||
//compare.hpp
|
||||
inline char chrlower(char c);
|
||||
inline char chrupper(char c);
|
||||
inline int stricmp(const char *str1, const char *str2);
|
||||
inline bool wildcard(const char *str, const char *pattern);
|
||||
inline bool iwildcard(const char *str, const char *pattern);
|
||||
inline bool strbegin (const char *str, const char *key);
|
||||
inline bool stribegin(const char *str, const char *key);
|
||||
inline bool strend (const char *str, const char *key);
|
||||
inline bool striend(const char *str, const char *key);
|
||||
|
||||
//convert.hpp
|
||||
inline char* strlower(char *str);
|
||||
inline char* strupper(char *str);
|
||||
inline char* strtr(char *dest, const char *before, const char *after);
|
||||
inline uintmax_t hex (const char *str);
|
||||
inline intmax_t integer(const char *str);
|
||||
inline uintmax_t decimal(const char *str);
|
||||
inline uintmax_t binary (const char *str);
|
||||
inline double fp (const char *str);
|
||||
|
||||
//math.hpp
|
||||
inline bool strint (const char *str, int &result);
|
||||
inline bool strmath(const char *str, int &result);
|
||||
|
||||
//platform.hpp
|
||||
inline string realpath(const char *name);
|
||||
inline string userpath();
|
||||
inline string currentpath();
|
||||
|
||||
//strl.hpp
|
||||
inline unsigned strlcpy(char *dest, const char *src, unsigned length);
|
||||
inline unsigned strlcat(char *dest, const char *src, unsigned length);
|
||||
|
||||
//strpos.hpp
|
||||
inline optional<unsigned> strpos(const char *str, const char *key);
|
||||
inline optional<unsigned> qstrpos(const char *str, const char *key);
|
||||
|
||||
//trim.hpp
|
||||
template<unsigned limit = 0> inline char* ltrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* rtrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* trim (char *str, const char *key = " ");
|
||||
|
||||
//utility.hpp
|
||||
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
|
||||
inline unsigned strlcat(string &dest, const char *src, unsigned length);
|
||||
inline string substr(const char *src, unsigned start = 0, unsigned length = 0);
|
||||
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string integer(intmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string decimal(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
|
||||
inline unsigned fp(char *str, double value);
|
||||
inline string fp(double value);
|
||||
|
||||
//variadic.hpp
|
||||
template<typename... Args> inline void print(Args&&... args);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef NALL_STRING_BSV_HPP
|
||||
#define NALL_STRING_BSV_HPP
|
||||
|
||||
//BSV parser
|
||||
//version 0.01
|
||||
|
||||
namespace nall {
|
||||
|
||||
inline string bsv_decode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '}' ) return "";
|
||||
if(*input == '\r') return "";
|
||||
if(*input == '\n') return "";
|
||||
|
||||
//normal characters
|
||||
if(*input != '{') { output[offset++] = *input++; continue; }
|
||||
|
||||
//entities
|
||||
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
|
||||
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
|
||||
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
|
||||
|
||||
//illegal entities
|
||||
return "";
|
||||
}
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
inline string bsv_encode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '\r') return "";
|
||||
|
||||
if(*input == '\n') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'f';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*input == '{') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*input == '}') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'r';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
output[offset++] = *input++;
|
||||
}
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef NALL_STRING_CAST_HPP
|
||||
#define NALL_STRING_CAST_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//this is needed, as C++0x does not support explicit template specialization inside classes
|
||||
template<> inline string to_string<bool> (bool v) { return v ? "true" : "false"; }
|
||||
template<> inline string to_string<signed int> (signed int v) { return integer(v); }
|
||||
template<> inline string to_string<unsigned int> (unsigned int v) { return decimal(v); }
|
||||
template<> inline string to_string<double> (double v) { return fp(v); }
|
||||
template<> inline string to_string<char*> (char *v) { return v; }
|
||||
template<> inline string to_string<const char*> (const char *v) { return v; }
|
||||
template<> inline string to_string<string> (string v) { return v; }
|
||||
template<> inline string to_string<const string&>(const string &v) { return v; }
|
||||
|
||||
template<typename T> string& string::operator= (T value) { return assign(to_string<T>(value)); }
|
||||
template<typename T> string& string::operator<<(T value) { return append(to_string<T>(value)); }
|
||||
|
||||
template<typename T> lstring& lstring::operator<<(T value) {
|
||||
operator[](size()).assign(to_string<T>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if defined(QSTRING_H)
|
||||
template<> inline string to_string<QString>(QString v) { return v.toUtf8().constData(); }
|
||||
template<> inline string to_string<const QString&>(const QString &v) { return v.toUtf8().constData(); }
|
||||
string::operator QString() const { return QString::fromUtf8(*this); }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,110 @@
|
|||
#ifndef NALL_STRING_COMPARE_HPP
|
||||
#define NALL_STRING_COMPARE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
char chrlower(char c) {
|
||||
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
char chrupper(char c) {
|
||||
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
int stricmp(const char *str1, const char *str2) {
|
||||
while(*str1) {
|
||||
if(chrlower(*str1) != chrlower(*str2)) break;
|
||||
str1++, str2++;
|
||||
}
|
||||
return (int)chrlower(*str1) - (int)chrlower(*str2);
|
||||
}
|
||||
|
||||
bool wildcard(const char *s, const char *p) {
|
||||
const char *cp = 0, *mp = 0;
|
||||
while(*s && *p != '*') {
|
||||
if(*p != '?' && *s != *p) return false;
|
||||
p++, s++;
|
||||
}
|
||||
while(*s) {
|
||||
if(*p == '*') {
|
||||
if(!*++p) return true;
|
||||
mp = p, cp = s + 1;
|
||||
} else if(*p == '?' || *p == *s) {
|
||||
p++, s++;
|
||||
} else {
|
||||
p = mp, s = cp++;
|
||||
}
|
||||
}
|
||||
while(*p == '*') p++;
|
||||
return !*p;
|
||||
}
|
||||
|
||||
bool iwildcard(const char *s, const char *p) {
|
||||
const char *cp = 0, *mp = 0;
|
||||
while(*s && *p != '*') {
|
||||
if(*p != '?' && chrlower(*s) != chrlower(*p)) return false;
|
||||
p++, s++;
|
||||
}
|
||||
while(*s) {
|
||||
if(*p == '*') {
|
||||
if(!*++p) return true;
|
||||
mp = p, cp = s + 1;
|
||||
} else if(*p == '?' || chrlower(*p) == chrlower(*s)) {
|
||||
p++, s++;
|
||||
} else {
|
||||
p = mp, s = cp++;
|
||||
}
|
||||
}
|
||||
while(*p == '*') p++;
|
||||
return !*p;
|
||||
}
|
||||
|
||||
bool strbegin(const char *str, const char *key) {
|
||||
int i, ssl = strlen(str), ksl = strlen(key);
|
||||
|
||||
if(ksl > ssl) return false;
|
||||
return (!memcmp(str, key, ksl));
|
||||
}
|
||||
|
||||
bool stribegin(const char *str, const char *key) {
|
||||
int ssl = strlen(str), ksl = strlen(key);
|
||||
|
||||
if(ksl > ssl) return false;
|
||||
for(int i = 0; i < ksl; i++) {
|
||||
if(str[i] >= 'A' && str[i] <= 'Z') {
|
||||
if(str[i] != key[i] && str[i]+0x20 != key[i])return false;
|
||||
} else if(str[i] >= 'a' && str[i] <= 'z') {
|
||||
if(str[i] != key[i] && str[i]-0x20 != key[i])return false;
|
||||
} else {
|
||||
if(str[i] != key[i])return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strend(const char *str, const char *key) {
|
||||
int ssl = strlen(str), ksl = strlen(key);
|
||||
|
||||
if(ksl > ssl) return false;
|
||||
return (!memcmp(str + ssl - ksl, key, ksl));
|
||||
}
|
||||
|
||||
bool striend(const char *str, const char *key) {
|
||||
int ssl = strlen(str), ksl = strlen(key);
|
||||
|
||||
if(ksl > ssl) return false;
|
||||
for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) {
|
||||
if(str[i] >= 'A' && str[i] <= 'Z') {
|
||||
if(str[i] != key[z] && str[i]+0x20 != key[z])return false;
|
||||
} else if(str[i] >= 'a' && str[i] <= 'z') {
|
||||
if(str[i] != key[z] && str[i]-0x20 != key[z])return false;
|
||||
} else {
|
||||
if(str[i] != key[z])return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,153 @@
|
|||
#ifndef NALL_STRING_CONVERT_HPP
|
||||
#define NALL_STRING_CONVERT_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
char* strlower(char *str) {
|
||||
if(!str) return 0;
|
||||
int i = 0;
|
||||
while(str[i]) {
|
||||
str[i] = chrlower(str[i]);
|
||||
i++;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
char* strupper(char *str) {
|
||||
if(!str) return 0;
|
||||
int i = 0;
|
||||
while(str[i]) {
|
||||
str[i] = chrupper(str[i]);
|
||||
i++;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
char* strtr(char *dest, const char *before, const char *after) {
|
||||
if(!dest || !before || !after) return dest;
|
||||
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
|
||||
|
||||
if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace
|
||||
for(unsigned i = 0; i < sl; i++) {
|
||||
for(unsigned l = 0; l < bsl; l++) {
|
||||
if(dest[i] == before[l]) {
|
||||
dest[i] = after[l];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
uintmax_t hex(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
||||
//skip hex identifiers 0x and $, if present
|
||||
if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2;
|
||||
else if(*str == '$') str++;
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else if(x >= 'A' && x <= 'F') x -= 'A' - 10;
|
||||
else if(x >= 'a' && x <= 'f') x -= 'a' - 10;
|
||||
else break; //stop at first invalid character
|
||||
result = result * 16 + x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
intmax_t integer(const char *str) {
|
||||
if(!str) return 0;
|
||||
intmax_t result = 0;
|
||||
bool negate = false;
|
||||
|
||||
//check for negation
|
||||
if(*str == '-') {
|
||||
negate = true;
|
||||
str++;
|
||||
}
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result = result * 10 + x;
|
||||
}
|
||||
|
||||
return !negate ? result : -result;
|
||||
}
|
||||
|
||||
uintmax_t decimal(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result = result * 10 + x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uintmax_t binary(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
||||
//skip bin identifiers 0b and %, if present
|
||||
if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2;
|
||||
else if(*str == '%') str++;
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x == '0' || x == '1') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result = result * 2 + x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double fp(const char *str) {
|
||||
if(!str) return 0.0;
|
||||
bool negate = false;
|
||||
|
||||
//check for negation
|
||||
if(*str == '-') {
|
||||
negate = true;
|
||||
str++;
|
||||
}
|
||||
|
||||
intmax_t result_integral = 0;
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else if(x == '.' || x == ',') break; //break loop and read fractional part
|
||||
else return (double)result_integral; //invalid value, assume no fractional part
|
||||
result_integral = result_integral * 10 + x;
|
||||
}
|
||||
|
||||
intmax_t result_fractional = 0;
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result_fractional = result_fractional * 10 + x;
|
||||
}
|
||||
|
||||
//calculate fractional portion
|
||||
double result = (double)result_fractional;
|
||||
while((uintmax_t)result > 0) result /= 10.0;
|
||||
result += (double)result_integral;
|
||||
|
||||
return !negate ? result : -result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,139 @@
|
|||
#ifndef NALL_STRING_CORE_HPP
|
||||
#define NALL_STRING_CORE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
void string::reserve(unsigned size_) {
|
||||
if(size_ > size) {
|
||||
size = size_;
|
||||
data = (char*)realloc(data, size + 1);
|
||||
data[size] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
string& string::assign(const char *s) {
|
||||
unsigned length = strlen(s);
|
||||
reserve(length);
|
||||
strcpy(data, s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append(const char *s) {
|
||||
unsigned length = strlen(data) + strlen(s);
|
||||
reserve(length);
|
||||
strcat(data, s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append(bool value) { append(value ? "true" : "false"); return *this; }
|
||||
string& string::append(signed int value) { append(integer(value)); return *this; }
|
||||
string& string::append(unsigned int value) { append(decimal(value)); return *this; }
|
||||
string& string::append(double value) { append(fp(value)); return *this; }
|
||||
|
||||
string::operator const char*() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
char* string::operator()() {
|
||||
return data;
|
||||
}
|
||||
|
||||
char& string::operator[](int index) {
|
||||
reserve(index);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
bool string::operator==(const char *str) const { return strcmp(data, str) == 0; }
|
||||
bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; }
|
||||
bool string::operator< (const char *str) const { return strcmp(data, str) < 0; }
|
||||
bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; }
|
||||
bool string::operator> (const char *str) const { return strcmp(data, str) > 0; }
|
||||
bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; }
|
||||
|
||||
string& string::operator=(const string &value) {
|
||||
assign(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator=(string &&source) {
|
||||
if(data) free(data);
|
||||
size = source.size;
|
||||
data = source.data;
|
||||
source.data = 0;
|
||||
source.size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static void istring(string &output) {
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static void istring(string &output, const T &value, Args&&... args) {
|
||||
output.append(value);
|
||||
istring(output, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args> string::string(Args&&... args) {
|
||||
size = 64;
|
||||
data = (char*)malloc(size + 1);
|
||||
*data = 0;
|
||||
istring(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
string::string(const string &value) {
|
||||
size = strlen(value);
|
||||
data = strdup(value);
|
||||
}
|
||||
|
||||
string::string(string &&source) {
|
||||
size = source.size;
|
||||
data = source.data;
|
||||
source.data = 0;
|
||||
}
|
||||
|
||||
string::~string() {
|
||||
if(data) free(data);
|
||||
}
|
||||
|
||||
bool string::readfile(const char *filename) {
|
||||
assign("");
|
||||
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
#else
|
||||
FILE *fp = _wfopen(utf16_t(filename), L"rb");
|
||||
#endif
|
||||
if(!fp) return false;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
unsigned size = ftell(fp);
|
||||
rewind(fp);
|
||||
char *fdata = new char[size + 1];
|
||||
unsigned unused = fread(fdata, 1, size, fp);
|
||||
fclose(fp);
|
||||
fdata[size] = 0;
|
||||
assign(fdata);
|
||||
delete[] fdata;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<unsigned> lstring::find(const char *key) const {
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
if(operator[](i) == key) return { true, i };
|
||||
}
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
inline lstring::lstring() {
|
||||
}
|
||||
|
||||
inline lstring::lstring(std::initializer_list<string> list) {
|
||||
for(const string *s = list.begin(); s != list.end(); ++s) {
|
||||
operator<<(*s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef NALL_FILENAME_HPP
|
||||
#define NALL_FILENAME_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
// "foo/bar.c" -> "foo/"
|
||||
// "foo/" -> "foo/"
|
||||
// "bar.c" -> "./"
|
||||
inline string dir(char const *name) {
|
||||
string result = name;
|
||||
for(signed i = strlen(result); i >= 0; i--) {
|
||||
if(result[i] == '/' || result[i] == '\\') {
|
||||
result[i + 1] = 0;
|
||||
break;
|
||||
}
|
||||
if(i == 0) result = "./";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// "foo/bar.c" -> "bar.c"
|
||||
inline string notdir(char const *name) {
|
||||
for(signed i = strlen(name); i >= 0; i--) {
|
||||
if(name[i] == '/' || name[i] == '\\') {
|
||||
name += i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
string result = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
// "foo/bar.c" -> "foo/bar"
|
||||
inline string basename(char const *name) {
|
||||
string result = name;
|
||||
for(signed i = strlen(result); i >= 0; i--) {
|
||||
if(result[i] == '/' || result[i] == '\\') {
|
||||
//file has no extension
|
||||
break;
|
||||
}
|
||||
if(result[i] == '.') {
|
||||
result[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// "foo/bar.c" -> "c"
|
||||
inline string extension(char const *name) {
|
||||
for(signed i = strlen(name); i >= 0; i--) {
|
||||
if(name[i] == '.') {
|
||||
name += i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
string result = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,164 @@
|
|||
#ifndef NALL_STRING_MATH_HPP
|
||||
#define NALL_STRING_MATH_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
static int eval_integer(const char *&s) {
|
||||
if(!*s) throw "unrecognized_integer";
|
||||
int value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
//hexadecimal
|
||||
if(x == '0' && (y == 'X' || y == 'x')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
|
||||
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
|
||||
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//binary
|
||||
if(x == '0' && (y == 'B' || y == 'b')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//octal (or decimal '0')
|
||||
if(x == '0') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//decimal
|
||||
if(x >= '0' && x <= '9') {
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//char
|
||||
if(x == '\'' && y != '\'') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
value = value * 256 + *s++;
|
||||
if(*s == '\'') { s += 1; return value; }
|
||||
if(!*s) throw "mismatched_char";
|
||||
}
|
||||
}
|
||||
|
||||
throw "unrecognized_integer";
|
||||
}
|
||||
|
||||
static int eval(const char *&s, int depth = 0) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) throw "unrecognized_token";
|
||||
int value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
if(*s == '(') {
|
||||
value = eval(++s, 1);
|
||||
if(*s++ != ')') throw "mismatched_group";
|
||||
}
|
||||
|
||||
else if(x == '!') value = !eval(++s, 13);
|
||||
else if(x == '~') value = ~eval(++s, 13);
|
||||
else if(x == '+') value = +eval(++s, 13);
|
||||
else if(x == '-') value = -eval(++s, 13);
|
||||
|
||||
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
|
||||
|
||||
else throw "unrecognized_token";
|
||||
|
||||
while(true) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) break;
|
||||
x = *s, y = *(s + 1);
|
||||
|
||||
if(depth >= 13) break;
|
||||
if(x == '*') { value *= eval(++s, 13); continue; }
|
||||
if(x == '/') { value /= eval(++s, 13); continue; }
|
||||
if(x == '%') { value %= eval(++s, 13); continue; }
|
||||
|
||||
if(depth >= 12) break;
|
||||
if(x == '+') { value += eval(++s, 12); continue; }
|
||||
if(x == '-') { value -= eval(++s, 12); continue; }
|
||||
|
||||
if(depth >= 11) break;
|
||||
if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; }
|
||||
if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; }
|
||||
|
||||
if(depth >= 10) break;
|
||||
if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; }
|
||||
if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; }
|
||||
if(x == '<') { value = value < eval(++s, 10); continue; }
|
||||
if(x == '>') { value = value > eval(++s, 10); continue; }
|
||||
|
||||
if(depth >= 9) break;
|
||||
if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; }
|
||||
if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; }
|
||||
|
||||
if(depth >= 8) break;
|
||||
if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; }
|
||||
|
||||
if(depth >= 7) break;
|
||||
if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; }
|
||||
|
||||
if(depth >= 6) break;
|
||||
if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; }
|
||||
|
||||
if(depth >= 5) break;
|
||||
if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; }
|
||||
|
||||
if(depth >= 4) break;
|
||||
if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; }
|
||||
|
||||
if(depth >= 3) break;
|
||||
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
|
||||
|
||||
if(x == '?') {
|
||||
int lhs = eval(++s, 2);
|
||||
if(*s != ':') throw "mismatched_ternary";
|
||||
int rhs = eval(++s, 2);
|
||||
value = value ? lhs : rhs;
|
||||
continue;
|
||||
}
|
||||
if(depth >= 2) break;
|
||||
|
||||
if(depth > 0 && x == ')') break;
|
||||
|
||||
throw "unrecognized_token";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool strint(const char *s, int &result) {
|
||||
try {
|
||||
result = eval_integer(s);
|
||||
return true;
|
||||
} catch(const char*) {
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool strmath(const char *s, int &result) {
|
||||
try {
|
||||
result = eval(s);
|
||||
return true;
|
||||
} catch(const char*) {
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef NALL_STRING_PLATFORM_HPP
|
||||
#define NALL_STRING_PLATFORM_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
string realpath(const char *name) {
|
||||
char path[PATH_MAX];
|
||||
if(::realpath(name, path)) {
|
||||
string result(path);
|
||||
result.transform("\\", "/");
|
||||
if(result.endswith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
string userpath() {
|
||||
char path[PATH_MAX];
|
||||
if(::userpath(path)) {
|
||||
string result(path);
|
||||
result.transform("\\", "/");
|
||||
if(result.endswith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
string currentpath() {
|
||||
char path[PATH_MAX];
|
||||
if(::getcwd(path)) {
|
||||
string result(path);
|
||||
result.transform("\\", "/");
|
||||
if(result.endswith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
#ifndef NALL_STRING_REPLACE_HPP
|
||||
#define NALL_STRING_REPLACE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
string& string::replace(const char *key, const char *token) {
|
||||
int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
|
||||
unsigned int replace_count = 0, size = ssl;
|
||||
char *buffer;
|
||||
|
||||
if(ksl <= ssl) {
|
||||
if(tsl > ksl) { //the new string may be longer than the old string...
|
||||
for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need...
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
replace_count++;
|
||||
i += ksl;
|
||||
} else i++;
|
||||
}
|
||||
size = ssl + ((tsl - ksl) * replace_count);
|
||||
reserve(size);
|
||||
}
|
||||
|
||||
buffer = new char[size + 1];
|
||||
for(i = z = 0; i < ssl;) {
|
||||
if(i <= ssl - ksl) {
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
memcpy(buffer + z, token, tsl);
|
||||
z += tsl;
|
||||
i += ksl;
|
||||
} else buffer[z++] = data[i++];
|
||||
} else buffer[z++] = data[i++];
|
||||
}
|
||||
buffer[z] = 0;
|
||||
|
||||
assign(buffer);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::qreplace(const char *key, const char *token) {
|
||||
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
|
||||
unsigned int replace_count = 0, size = ssl;
|
||||
uint8_t x;
|
||||
char *buffer;
|
||||
|
||||
if(ksl <= ssl) {
|
||||
if(tsl > ksl) {
|
||||
for(i = 0; i <= ssl - ksl;) {
|
||||
x = data[i];
|
||||
if(x == '\"' || x == '\'') {
|
||||
l = i;
|
||||
i++;
|
||||
while(data[i++] != x) {
|
||||
if(i == ssl) {
|
||||
i = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
replace_count++;
|
||||
i += ksl;
|
||||
} else i++;
|
||||
}
|
||||
size = ssl + ((tsl - ksl) * replace_count);
|
||||
reserve(size);
|
||||
}
|
||||
|
||||
buffer = new char[size + 1];
|
||||
for(i = z = 0; i < ssl;) {
|
||||
x = data[i];
|
||||
if(x == '\"' || x == '\'') {
|
||||
l = i++;
|
||||
while(data[i] != x && i < ssl)i++;
|
||||
if(i >= ssl)i = l;
|
||||
else {
|
||||
memcpy(buffer + z, data + l, i - l);
|
||||
z += i - l;
|
||||
}
|
||||
}
|
||||
if(i <= ssl - ksl) {
|
||||
if(!memcmp(data + i, key, ksl)) {
|
||||
memcpy(buffer + z, token, tsl);
|
||||
z += tsl;
|
||||
i += ksl;
|
||||
replace_count++;
|
||||
} else buffer[z++] = data[i++];
|
||||
} else buffer[z++] = data[i++];
|
||||
}
|
||||
buffer[z] = 0;
|
||||
|
||||
assign(buffer);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef NALL_STRING_SPLIT_HPP
|
||||
#define NALL_STRING_SPLIT_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<unsigned Limit> void lstring::split(const char *key, const char *src) {
|
||||
unsigned limit = Limit;
|
||||
reset();
|
||||
|
||||
int ssl = strlen(src), ksl = strlen(key);
|
||||
int lp = 0, split_count = 0;
|
||||
|
||||
for(int i = 0; i <= ssl - ksl;) {
|
||||
if(!memcmp(src + i, key, ksl)) {
|
||||
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
|
||||
i += ksl;
|
||||
lp = i;
|
||||
if(!--limit) break;
|
||||
} else i++;
|
||||
}
|
||||
|
||||
operator[](split_count++) = src + lp;
|
||||
}
|
||||
|
||||
template<unsigned Limit> void lstring::qsplit(const char *key, const char *src) {
|
||||
unsigned limit = Limit;
|
||||
reset();
|
||||
|
||||
int ssl = strlen(src), ksl = strlen(key);
|
||||
int lp = 0, split_count = 0;
|
||||
|
||||
for(int i = 0; i <= ssl - ksl;) {
|
||||
uint8_t x = src[i];
|
||||
|
||||
if(x == '\"' || x == '\'') {
|
||||
int z = i++; //skip opening quote
|
||||
while(i < ssl && src[i] != x) i++;
|
||||
if(i >= ssl) i = z; //failed match, rewind i
|
||||
else {
|
||||
i++; //skip closing quote
|
||||
continue; //restart in case next char is also a quote
|
||||
}
|
||||
}
|
||||
|
||||
if(!memcmp(src + i, key, ksl)) {
|
||||
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
|
||||
i += ksl;
|
||||
lp = i;
|
||||
if(!--limit) break;
|
||||
} else i++;
|
||||
}
|
||||
|
||||
operator[](split_count++) = src + lp;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef NALL_STRING_STRL_HPP
|
||||
#define NALL_STRING_STRL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller
|
||||
|
||||
//return = strlen(src)
|
||||
unsigned strlcpy(char *dest, const char *src, unsigned length) {
|
||||
char *d = dest;
|
||||
const char *s = src;
|
||||
unsigned n = length;
|
||||
|
||||
if(n) {
|
||||
while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached
|
||||
}
|
||||
|
||||
if(!n) {
|
||||
if(length) *d = 0;
|
||||
while(*s++); //traverse rest of s, so that s - src == strlen(src)
|
||||
}
|
||||
|
||||
return (s - src - 1); //return length of copied string, sans null terminator
|
||||
}
|
||||
|
||||
//return = strlen(src) + min(length, strlen(dest))
|
||||
unsigned strlcat(char *dest, const char *src, unsigned length) {
|
||||
char *d = dest;
|
||||
const char *s = src;
|
||||
unsigned n = length;
|
||||
|
||||
while(n-- && *d) d++; //find end of dest
|
||||
unsigned dlength = d - dest;
|
||||
n = length - dlength; //subtract length of dest from maximum string length
|
||||
|
||||
if(!n) return dlength + strlen(s);
|
||||
|
||||
while(*s) {
|
||||
if(n != 1) {
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*d = 0;
|
||||
|
||||
return dlength + (s - src); //return length of resulting string, sans null terminator
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef NALL_STRING_STRPOS_HPP
|
||||
#define NALL_STRING_STRPOS_HPP
|
||||
|
||||
//usage example:
|
||||
//if(auto pos = strpos(str, key)) print(pos(), "\n");
|
||||
//prints position of key within str, only if it is found
|
||||
|
||||
namespace nall {
|
||||
|
||||
optional<unsigned> strpos(const char *str, const char *key) {
|
||||
unsigned ssl = strlen(str), ksl = strlen(key);
|
||||
if(ksl > ssl) return { false, 0 };
|
||||
|
||||
for(unsigned i = 0; i <= ssl - ksl; i++) {
|
||||
if(!memcmp(str + i, key, ksl)) return { true, i };
|
||||
}
|
||||
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
optional<unsigned> qstrpos(const char *str, const char *key) {
|
||||
unsigned ssl = strlen(str), ksl = strlen(key);
|
||||
if(ksl > ssl) return { false, 0 };
|
||||
|
||||
for(unsigned i = 0; i <= ssl - ksl;) {
|
||||
uint8_t x = str[i];
|
||||
if(x == '\"' || x == '\'') {
|
||||
uint8_t z = i++;
|
||||
while(str[i] != x && i < ssl) i++;
|
||||
if(i >= ssl) i = z;
|
||||
}
|
||||
if(!memcmp(str + i, key, ksl)) return { true, i };
|
||||
i++;
|
||||
}
|
||||
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef NALL_STRING_TRIM_HPP
|
||||
#define NALL_STRING_TRIM_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//limit defaults to zero, which will underflow on first compare; equivalent to no limit
|
||||
template<unsigned Limit> char* ltrim(char *str, const char *key) {
|
||||
unsigned limit = Limit;
|
||||
if(!key || !*key) return str;
|
||||
while(strbegin(str, key)) {
|
||||
char *dest = str, *src = str + strlen(key);
|
||||
while(true) {
|
||||
*dest = *src++;
|
||||
if(!*dest) break;
|
||||
dest++;
|
||||
}
|
||||
if(--limit == 0) break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
template<unsigned Limit> char* rtrim(char *str, const char *key) {
|
||||
unsigned limit = Limit;
|
||||
if(!key || !*key) return str;
|
||||
while(strend(str, key)) {
|
||||
str[strlen(str) - strlen(key)] = 0;
|
||||
if(--limit == 0) break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
template<unsigned limit> char* trim(char *str, const char *key) {
|
||||
return ltrim<limit>(rtrim<limit>(str, key), key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,157 @@
|
|||
#ifndef NALL_STRING_UTILITY_HPP
|
||||
#define NALL_STRING_UTILITY_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
unsigned strlcpy(string &dest, const char *src, unsigned length) {
|
||||
dest.reserve(length);
|
||||
return strlcpy(dest(), src, length);
|
||||
}
|
||||
|
||||
unsigned strlcat(string &dest, const char *src, unsigned length) {
|
||||
dest.reserve(length);
|
||||
return strlcat(dest(), src, length);
|
||||
}
|
||||
|
||||
string substr(const char *src, unsigned start, unsigned length) {
|
||||
string dest;
|
||||
if(length == 0) {
|
||||
//copy entire string
|
||||
dest = src + start;
|
||||
} else {
|
||||
//copy partial string
|
||||
strlcpy(dest, src + start, length + 1);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* arithmetic <> string */
|
||||
|
||||
template<unsigned length, char padding> string hex(uintmax_t value) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
|
||||
//render string backwards, as we do not know its length yet
|
||||
do {
|
||||
unsigned n = value & 15;
|
||||
output[offset++] = n < 10 ? '0' + n : 'a' + n - 10;
|
||||
value >>= 4;
|
||||
} while(value);
|
||||
|
||||
while(offset < length) output[offset++] = padding;
|
||||
output[offset--] = 0;
|
||||
|
||||
//reverse the string in-place
|
||||
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
|
||||
char temp = output[i];
|
||||
output[i] = output[offset - i];
|
||||
output[offset - i] = temp;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
template<unsigned length, char padding> string integer(intmax_t value) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
|
||||
bool negative = value < 0;
|
||||
if(negative) value = abs(value);
|
||||
|
||||
do {
|
||||
unsigned n = value % 10;
|
||||
output[offset++] = '0' + n;
|
||||
value /= 10;
|
||||
} while(value);
|
||||
|
||||
while(offset < length) output[offset++] = padding;
|
||||
if(negative) output[offset++] = '-';
|
||||
output[offset--] = 0;
|
||||
|
||||
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
|
||||
char temp = output[i];
|
||||
output[i] = output[offset - i];
|
||||
output[offset - i] = temp;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
template<unsigned length, char padding> string decimal(uintmax_t value) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
|
||||
do {
|
||||
unsigned n = value % 10;
|
||||
output[offset++] = '0' + n;
|
||||
value /= 10;
|
||||
} while(value);
|
||||
|
||||
while(offset < length) output[offset++] = padding;
|
||||
output[offset--] = 0;
|
||||
|
||||
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
|
||||
char temp = output[i];
|
||||
output[i] = output[offset - i];
|
||||
output[offset - i] = temp;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
template<unsigned length, char padding> string binary(uintmax_t value) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
|
||||
do {
|
||||
unsigned n = value & 1;
|
||||
output[offset++] = '0' + n;
|
||||
value >>= 1;
|
||||
} while(value);
|
||||
|
||||
while(offset < length) output[offset++] = padding;
|
||||
output[offset--] = 0;
|
||||
|
||||
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
|
||||
char temp = output[i];
|
||||
output[i] = output[offset - i];
|
||||
output[offset - i] = temp;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//using sprintf is certainly not the most ideal method to convert
|
||||
//a double to a string ... but attempting to parse a double by
|
||||
//hand, digit-by-digit, results in subtle rounding errors.
|
||||
unsigned fp(char *str, double value) {
|
||||
char buffer[256];
|
||||
sprintf(buffer, "%f", value);
|
||||
|
||||
//remove excess 0's in fraction (2.500000 -> 2.5)
|
||||
for(char *p = buffer; *p; p++) {
|
||||
if(*p == '.') {
|
||||
char *p = buffer + strlen(buffer) - 1;
|
||||
while(*p == '0') {
|
||||
if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1.
|
||||
p--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned length = strlen(buffer);
|
||||
if(str) strcpy(str, buffer);
|
||||
return length + 1;
|
||||
}
|
||||
|
||||
string fp(double value) {
|
||||
string temp;
|
||||
temp.reserve(fp(0, value));
|
||||
fp(temp(), value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef NALL_STRING_VARIADIC_HPP
|
||||
#define NALL_STRING_VARIADIC_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename... Args> inline void print(Args&&... args) {
|
||||
printf("%s", (const char*)string(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef NALL_STRING_WRAPPER_HPP
|
||||
#define NALL_STRING_WRAPPER_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
unsigned string::length() const { return strlen(data); }
|
||||
|
||||
bool string::equals(const char *str) const { return !strcmp(data, str); }
|
||||
bool string::iequals(const char *str) const { return !stricmp(data, str); }
|
||||
|
||||
bool string::wildcard(const char *str) const { return nall::wildcard(data, str); }
|
||||
bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); }
|
||||
|
||||
bool string::beginswith(const char *str) const { return strbegin(data, str); }
|
||||
bool string::ibeginswith(const char *str) const { return stribegin(data, str); }
|
||||
|
||||
bool string::endswith(const char *str) const { return strend(data, str); }
|
||||
bool string::iendswith(const char *str) const { return striend(data, str); }
|
||||
|
||||
string& string::lower() { nall::strlower(data); return *this; }
|
||||
string& string::upper() { nall::strupper(data); return *this; }
|
||||
string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; }
|
||||
|
||||
template<unsigned limit> string& string::ltrim(const char *key) { nall::ltrim<limit>(data, key); return *this; }
|
||||
template<unsigned limit> string& string::rtrim(const char *key) { nall::rtrim<limit>(data, key); return *this; }
|
||||
template<unsigned limit> string& string::trim (const char *key) { nall::trim <limit>(data, key); return *this; }
|
||||
|
||||
optional<unsigned> string::position(const char *key) const { return strpos(data, key); }
|
||||
optional<unsigned> string::qposition(const char *key) const { return qstrpos(data, key); }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,266 @@
|
|||
#ifndef NALL_STRING_XML_HPP
|
||||
#define NALL_STRING_XML_HPP
|
||||
|
||||
//XML subset parser
|
||||
//version 0.05
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct xml_attribute {
|
||||
string name;
|
||||
string content;
|
||||
virtual string parse() const;
|
||||
};
|
||||
|
||||
struct xml_element : xml_attribute {
|
||||
string parse() const;
|
||||
linear_vector<xml_attribute> attribute;
|
||||
linear_vector<xml_element> element;
|
||||
|
||||
protected:
|
||||
void parse_doctype(const char *&data);
|
||||
bool parse_head(string data);
|
||||
bool parse_body(const char *&data);
|
||||
friend xml_element xml_parse(const char *data);
|
||||
};
|
||||
|
||||
inline string xml_attribute::parse() const {
|
||||
string data;
|
||||
unsigned offset = 0;
|
||||
|
||||
const char *source = content;
|
||||
while(*source) {
|
||||
if(*source == '&') {
|
||||
if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; }
|
||||
if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; }
|
||||
if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; }
|
||||
if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; }
|
||||
if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; }
|
||||
}
|
||||
|
||||
//reject illegal characters
|
||||
if(*source == '&') return "";
|
||||
if(*source == '<') return "";
|
||||
if(*source == '>') return "";
|
||||
|
||||
data[offset++] = *source++;
|
||||
}
|
||||
|
||||
data[offset] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline string xml_element::parse() const {
|
||||
string data;
|
||||
unsigned offset = 0;
|
||||
|
||||
const char *source = content;
|
||||
while(*source) {
|
||||
if(*source == '&') {
|
||||
if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; }
|
||||
if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; }
|
||||
if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; }
|
||||
if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; }
|
||||
if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; }
|
||||
}
|
||||
|
||||
if(strbegin(source, "<!--")) {
|
||||
if(auto pos = strpos(source, "-->")) {
|
||||
source += pos() + 3;
|
||||
continue;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
if(strbegin(source, "<![CDATA[")) {
|
||||
if(auto pos = strpos(source, "]]>")) {
|
||||
if(pos() - 9 > 0) {
|
||||
string cdata = substr(source, 9, pos() - 9);
|
||||
data << cdata;
|
||||
offset += strlen(cdata);
|
||||
}
|
||||
source += 9 + offset + 3;
|
||||
continue;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
//reject illegal characters
|
||||
if(*source == '&') return "";
|
||||
if(*source == '<') return "";
|
||||
if(*source == '>') return "";
|
||||
|
||||
data[offset++] = *source++;
|
||||
}
|
||||
|
||||
data[offset] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline void xml_element::parse_doctype(const char *&data) {
|
||||
name = "!DOCTYPE";
|
||||
const char *content_begin = data;
|
||||
|
||||
signed counter = 0;
|
||||
while(*data) {
|
||||
char value = *data++;
|
||||
if(value == '<') counter++;
|
||||
if(value == '>') counter--;
|
||||
if(counter < 0) {
|
||||
content = substr(content_begin, 0, data - content_begin - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw "...";
|
||||
}
|
||||
|
||||
inline bool xml_element::parse_head(string data) {
|
||||
data.qreplace("\t", " ");
|
||||
data.qreplace("\r", " ");
|
||||
data.qreplace("\n", " ");
|
||||
while(qstrpos(data, " ")) data.qreplace(" ", " ");
|
||||
data.qreplace(" =", "=");
|
||||
data.qreplace("= ", "=");
|
||||
data.rtrim();
|
||||
|
||||
lstring part;
|
||||
part.qsplit(" ", data);
|
||||
|
||||
name = part[0];
|
||||
if(name == "") throw "...";
|
||||
|
||||
for(unsigned i = 1; i < part.size(); i++) {
|
||||
lstring side;
|
||||
side.qsplit("=", part[i]);
|
||||
if(side.size() != 2) throw "...";
|
||||
|
||||
xml_attribute attr;
|
||||
attr.name = side[0];
|
||||
attr.content = side[1];
|
||||
if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\"");
|
||||
else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'");
|
||||
else throw "...";
|
||||
attribute.append(attr);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool xml_element::parse_body(const char *&data) {
|
||||
while(true) {
|
||||
if(!*data) return false;
|
||||
if(*data++ != '<') continue;
|
||||
if(*data == '/') return false;
|
||||
|
||||
if(strbegin(data, "!DOCTYPE") == true) {
|
||||
parse_doctype(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(strbegin(data, "!--")) {
|
||||
if(auto offset = strpos(data, "-->")) {
|
||||
data += offset() + 3;
|
||||
continue;
|
||||
} else {
|
||||
throw "...";
|
||||
}
|
||||
}
|
||||
|
||||
if(strbegin(data, "![CDATA[")) {
|
||||
if(auto offset = strpos(data, "]]>")) {
|
||||
data += offset() + 3;
|
||||
continue;
|
||||
} else {
|
||||
throw "...";
|
||||
}
|
||||
}
|
||||
|
||||
auto offset = strpos(data, ">");
|
||||
if(!offset) throw "...";
|
||||
|
||||
string tag = substr(data, 0, offset());
|
||||
data += offset() + 1;
|
||||
const char *content_begin = data;
|
||||
|
||||
bool self_terminating = false;
|
||||
|
||||
if(strend(tag, "?") == true) {
|
||||
self_terminating = true;
|
||||
tag.rtrim<1>("?");
|
||||
} else if(strend(tag, "/") == true) {
|
||||
self_terminating = true;
|
||||
tag.rtrim<1>("/");
|
||||
}
|
||||
|
||||
parse_head(tag);
|
||||
if(self_terminating) return true;
|
||||
|
||||
while(*data) {
|
||||
unsigned index = element.size();
|
||||
xml_element node;
|
||||
if(node.parse_body(data) == false) {
|
||||
if(*data == '/') {
|
||||
signed length = data - content_begin - 1;
|
||||
if(length > 0) content = substr(content_begin, 0, length);
|
||||
|
||||
data++;
|
||||
auto offset = strpos(data, ">");
|
||||
if(!offset) throw "...";
|
||||
|
||||
tag = substr(data, 0, offset());
|
||||
data += offset() + 1;
|
||||
|
||||
tag.replace("\t", " ");
|
||||
tag.replace("\r", " ");
|
||||
tag.replace("\n", " ");
|
||||
while(strpos(tag, " ")) tag.replace(" ", " ");
|
||||
tag.rtrim();
|
||||
|
||||
if(name != tag) throw "...";
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
element.append(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ensure there is only one root element
|
||||
inline bool xml_validate(xml_element &document) {
|
||||
unsigned root_counter = 0;
|
||||
|
||||
for(unsigned i = 0; i < document.element.size(); i++) {
|
||||
string &name = document.element[i].name;
|
||||
if(strbegin(name, "?")) continue;
|
||||
if(strbegin(name, "!")) continue;
|
||||
if(++root_counter > 1) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline xml_element xml_parse(const char *data) {
|
||||
xml_element self;
|
||||
|
||||
try {
|
||||
while(*data) {
|
||||
xml_element node;
|
||||
if(node.parse_body(data) == false) {
|
||||
break;
|
||||
} else {
|
||||
self.element.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
if(xml_validate(self) == false) throw "...";
|
||||
return self;
|
||||
} catch(const char*) {
|
||||
xml_element empty;
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,223 @@
|
|||
#ifndef NALL_UPS_HPP
|
||||
#define NALL_UPS_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct ups {
|
||||
enum class result : unsigned {
|
||||
unknown,
|
||||
success,
|
||||
patch_unwritable,
|
||||
patch_invalid,
|
||||
source_invalid,
|
||||
target_invalid,
|
||||
target_too_small,
|
||||
patch_checksum_invalid,
|
||||
source_checksum_invalid,
|
||||
target_checksum_invalid,
|
||||
};
|
||||
|
||||
function<void (unsigned offset, unsigned length)> progress;
|
||||
|
||||
result create(
|
||||
const uint8_t *sourcedata, unsigned sourcelength,
|
||||
const uint8_t *targetdata, unsigned targetlength,
|
||||
const char *patchfilename
|
||||
) {
|
||||
source_data = (uint8_t*)sourcedata, target_data = (uint8_t*)targetdata;
|
||||
source_length = sourcelength, target_length = targetlength;
|
||||
source_offset = target_offset = 0;
|
||||
source_checksum = target_checksum = patch_checksum = ~0;
|
||||
|
||||
if(patch_file.open(patchfilename, file::mode::write) == false) return result::patch_unwritable;
|
||||
|
||||
patch_write('U');
|
||||
patch_write('P');
|
||||
patch_write('S');
|
||||
patch_write('1');
|
||||
encode(source_length);
|
||||
encode(target_length);
|
||||
|
||||
unsigned output_length = source_length > target_length ? source_length : target_length;
|
||||
unsigned relative = 0;
|
||||
for(unsigned offset = 0; offset < output_length;) {
|
||||
uint8_t x = source_read();
|
||||
uint8_t y = target_read();
|
||||
|
||||
if(x == y) {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
encode(offset++ - relative);
|
||||
patch_write(x ^ y);
|
||||
|
||||
while(true) {
|
||||
if(offset >= output_length) {
|
||||
patch_write(0x00);
|
||||
break;
|
||||
}
|
||||
|
||||
x = source_read();
|
||||
y = target_read();
|
||||
offset++;
|
||||
patch_write(x ^ y);
|
||||
if(x == y) break;
|
||||
}
|
||||
|
||||
relative = offset;
|
||||
}
|
||||
|
||||
source_checksum = ~source_checksum;
|
||||
target_checksum = ~target_checksum;
|
||||
for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8));
|
||||
for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8));
|
||||
uint32_t patch_result_checksum = ~patch_checksum;
|
||||
for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8));
|
||||
|
||||
patch_file.close();
|
||||
return result::success;
|
||||
}
|
||||
|
||||
result apply(
|
||||
const uint8_t *patchdata, unsigned patchlength,
|
||||
const uint8_t *sourcedata, unsigned sourcelength,
|
||||
uint8_t *targetdata, unsigned &targetlength
|
||||
) {
|
||||
patch_data = (uint8_t*)patchdata, source_data = (uint8_t*)sourcedata, target_data = targetdata;
|
||||
patch_length = patchlength, source_length = sourcelength, target_length = targetlength;
|
||||
patch_offset = source_offset = target_offset = 0;
|
||||
patch_checksum = source_checksum = target_checksum = ~0;
|
||||
|
||||
if(patch_length < 18) return result::patch_invalid;
|
||||
if(patch_read() != 'U') return result::patch_invalid;
|
||||
if(patch_read() != 'P') return result::patch_invalid;
|
||||
if(patch_read() != 'S') return result::patch_invalid;
|
||||
if(patch_read() != '1') return result::patch_invalid;
|
||||
|
||||
unsigned source_read_length = decode();
|
||||
unsigned target_read_length = decode();
|
||||
|
||||
if(source_length != source_read_length && source_length != target_read_length) return result::source_invalid;
|
||||
targetlength = (source_length == source_read_length ? target_read_length : source_read_length);
|
||||
if(target_length < targetlength) return result::target_too_small;
|
||||
target_length = targetlength;
|
||||
|
||||
while(patch_offset < patch_length - 12) {
|
||||
unsigned length = decode();
|
||||
while(length--) target_write(source_read());
|
||||
while(true) {
|
||||
uint8_t patch_xor = patch_read();
|
||||
target_write(patch_xor ^ source_read());
|
||||
if(patch_xor == 0) break;
|
||||
}
|
||||
}
|
||||
while(source_offset < source_length) target_write(source_read());
|
||||
while(target_offset < target_length) target_write(source_read());
|
||||
|
||||
uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0;
|
||||
for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8);
|
||||
for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8);
|
||||
uint32_t patch_result_checksum = ~patch_checksum;
|
||||
source_checksum = ~source_checksum;
|
||||
target_checksum = ~target_checksum;
|
||||
for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8);
|
||||
|
||||
if(patch_result_checksum != patch_read_checksum) return result::patch_invalid;
|
||||
if(source_checksum == source_read_checksum && source_length == source_read_length) {
|
||||
if(target_checksum == target_read_checksum && target_length == target_read_length) return result::success;
|
||||
return result::target_invalid;
|
||||
} else if(source_checksum == target_read_checksum && source_length == target_read_length) {
|
||||
if(target_checksum == source_read_checksum && target_length == source_read_length) return result::success;
|
||||
return result::target_invalid;
|
||||
} else {
|
||||
return result::source_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *patch_data, *source_data, *target_data;
|
||||
unsigned patch_length, source_length, target_length;
|
||||
unsigned patch_offset, source_offset, target_offset;
|
||||
unsigned patch_checksum, source_checksum, target_checksum;
|
||||
file patch_file;
|
||||
|
||||
uint8_t patch_read() {
|
||||
if(patch_offset < patch_length) {
|
||||
uint8_t n = patch_data[patch_offset++];
|
||||
patch_checksum = crc32_adjust(patch_checksum, n);
|
||||
return n;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
uint8_t source_read() {
|
||||
if(source_offset < source_length) {
|
||||
uint8_t n = source_data[source_offset++];
|
||||
source_checksum = crc32_adjust(source_checksum, n);
|
||||
return n;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
uint8_t target_read() {
|
||||
uint8_t result = 0x00;
|
||||
if(target_offset < target_length) {
|
||||
result = target_data[target_offset];
|
||||
target_checksum = crc32_adjust(target_checksum, result);
|
||||
}
|
||||
if(((target_offset++ & 255) == 0) && progress) {
|
||||
progress(target_offset, source_length > target_length ? source_length : target_length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void patch_write(uint8_t n) {
|
||||
patch_file.write(n);
|
||||
patch_checksum = crc32_adjust(patch_checksum, n);
|
||||
}
|
||||
|
||||
void target_write(uint8_t n) {
|
||||
if(target_offset < target_length) {
|
||||
target_data[target_offset] = n;
|
||||
target_checksum = crc32_adjust(target_checksum, n);
|
||||
}
|
||||
if(((target_offset++ & 255) == 0) && progress) {
|
||||
progress(target_offset, source_length > target_length ? source_length : target_length);
|
||||
}
|
||||
}
|
||||
|
||||
void encode(uint64_t offset) {
|
||||
while(true) {
|
||||
uint64_t x = offset & 0x7f;
|
||||
offset >>= 7;
|
||||
if(offset == 0) {
|
||||
patch_write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
patch_write(x);
|
||||
offset--;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t decode() {
|
||||
uint64_t offset = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = patch_read();
|
||||
offset += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
offset += shift;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef NALL_UTF8_HPP
|
||||
#define NALL_UTF8_HPP
|
||||
|
||||
//UTF-8 <> UTF-16 conversion
|
||||
//used only for Win32; Linux, etc use UTF-8 internally
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#undef UNICODE
|
||||
#undef _WIN32_WINNT
|
||||
#undef NOMINMAX
|
||||
#define UNICODE
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#undef interface
|
||||
|
||||
namespace nall {
|
||||
//UTF-8 to UTF-16
|
||||
class utf16_t {
|
||||
public:
|
||||
operator wchar_t*() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
operator const wchar_t*() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
utf16_t(const char *s = "") {
|
||||
if(!s) s = "";
|
||||
unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0);
|
||||
buffer = new wchar_t[length + 1]();
|
||||
MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length);
|
||||
}
|
||||
|
||||
~utf16_t() {
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
wchar_t *buffer;
|
||||
};
|
||||
|
||||
//UTF-16 to UTF-8
|
||||
class utf8_t {
|
||||
public:
|
||||
operator char*() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
operator const char*() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
utf8_t(const wchar_t *s = L"") {
|
||||
if(!s) s = L"";
|
||||
unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0);
|
||||
buffer = new char[length + 1]();
|
||||
WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0);
|
||||
}
|
||||
|
||||
~utf8_t() {
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
utf8_t(const utf8_t&) = delete;
|
||||
utf8_t& operator=(const utf8_t&) = delete;
|
||||
|
||||
private:
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
inline void utf8_args(int &argc, char **&argv) {
|
||||
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
argv = new char*[argc];
|
||||
for(unsigned i = 0; i < argc; i++) {
|
||||
argv[i] = new char[_MAX_PATH];
|
||||
strcpy(argv[i], nall::utf8_t(wargv[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //if defined(_WIN32)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef NALL_UTILITY_HPP
|
||||
#define NALL_UTILITY_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#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> inline void swap(T &x, T &y) {
|
||||
T temp(std::move(x));
|
||||
x = std::move(y);
|
||||
y = std::move(temp);
|
||||
}
|
||||
|
||||
template<typename T> struct base_from_member {
|
||||
T value;
|
||||
base_from_member(T value_) : value(value_) {}
|
||||
};
|
||||
|
||||
template<typename T> class optional {
|
||||
bool valid;
|
||||
T value;
|
||||
public:
|
||||
inline operator bool() const { return valid; }
|
||||
inline const T& operator()() const { if(!valid) throw; return value; }
|
||||
inline optional(bool valid, const T &value) : valid(valid), value(value) {}
|
||||
};
|
||||
|
||||
template<typename T> inline T* allocate(unsigned size, const T &value) {
|
||||
T *array = new T[size];
|
||||
for(unsigned i = 0; i < size; i++) array[i] = value;
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,118 @@
|
|||
#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 {
|
||||
private:
|
||||
unsigned data;
|
||||
|
||||
public:
|
||||
inline operator unsigned() const { return data; }
|
||||
inline unsigned operator ++(int) { unsigned r = data; data = uclip<bits>(data + 1); return r; }
|
||||
inline unsigned operator --(int) { unsigned r = data; data = uclip<bits>(data - 1); return r; }
|
||||
inline unsigned operator ++() { return data = uclip<bits>(data + 1); }
|
||||
inline unsigned operator --() { return data = uclip<bits>(data - 1); }
|
||||
inline unsigned operator =(const unsigned i) { return data = uclip<bits>(i); }
|
||||
inline unsigned operator |=(const unsigned i) { return data = uclip<bits>(data | i); }
|
||||
inline unsigned operator ^=(const unsigned i) { return data = uclip<bits>(data ^ i); }
|
||||
inline unsigned operator &=(const unsigned i) { return data = uclip<bits>(data & i); }
|
||||
inline unsigned operator<<=(const unsigned i) { return data = uclip<bits>(data << i); }
|
||||
inline unsigned operator>>=(const unsigned i) { return data = uclip<bits>(data >> i); }
|
||||
inline unsigned operator +=(const unsigned i) { return data = uclip<bits>(data + i); }
|
||||
inline unsigned operator -=(const unsigned i) { return data = uclip<bits>(data - i); }
|
||||
inline unsigned operator *=(const unsigned i) { return data = uclip<bits>(data * i); }
|
||||
inline unsigned operator /=(const unsigned i) { return data = uclip<bits>(data / i); }
|
||||
inline unsigned operator %=(const unsigned i) { return data = uclip<bits>(data % i); }
|
||||
|
||||
inline uint_t() : data(0) {}
|
||||
inline uint_t(const unsigned i) : data(uclip<bits>(i)) {}
|
||||
};
|
||||
|
||||
template<unsigned bits> class int_t {
|
||||
private:
|
||||
signed data;
|
||||
|
||||
public:
|
||||
inline operator signed() const { return data; }
|
||||
inline signed operator ++(int) { signed r = data; data = sclip<bits>(data + 1); return r; }
|
||||
inline signed operator --(int) { signed r = data; data = sclip<bits>(data - 1); return r; }
|
||||
inline signed operator ++() { return data = sclip<bits>(data + 1); }
|
||||
inline signed operator --() { return data = sclip<bits>(data - 1); }
|
||||
inline signed operator =(const signed i) { return data = sclip<bits>(i); }
|
||||
inline signed operator |=(const signed i) { return data = sclip<bits>(data | i); }
|
||||
inline signed operator ^=(const signed i) { return data = sclip<bits>(data ^ i); }
|
||||
inline signed operator &=(const signed i) { return data = sclip<bits>(data & i); }
|
||||
inline signed operator<<=(const signed i) { return data = sclip<bits>(data << i); }
|
||||
inline signed operator>>=(const signed i) { return data = sclip<bits>(data >> i); }
|
||||
inline signed operator +=(const signed i) { return data = sclip<bits>(data + i); }
|
||||
inline signed operator -=(const signed i) { return data = sclip<bits>(data - i); }
|
||||
inline signed operator *=(const signed i) { return data = sclip<bits>(data * i); }
|
||||
inline signed operator /=(const signed i) { return data = sclip<bits>(data / i); }
|
||||
inline signed operator %=(const signed i) { return data = sclip<bits>(data % i); }
|
||||
|
||||
inline int_t() : data(0) {}
|
||||
inline int_t(const signed i) : data(sclip<bits>(i)) {}
|
||||
};
|
||||
|
||||
class varuint_t {
|
||||
private:
|
||||
unsigned data;
|
||||
unsigned mask;
|
||||
|
||||
public:
|
||||
inline operator unsigned() const { return data; }
|
||||
inline unsigned operator ++(int) { unsigned r = data; data = (data + 1) & mask; return r; }
|
||||
inline unsigned operator --(int) { unsigned r = data; data = (data - 1) & mask; return r; }
|
||||
inline unsigned operator ++() { return data = (data + 1) & mask; }
|
||||
inline unsigned operator --() { return data = (data - 1) & mask; }
|
||||
inline unsigned operator =(const unsigned i) { return data = (i) & mask; }
|
||||
inline unsigned operator |=(const unsigned i) { return data = (data | i) & mask; }
|
||||
inline unsigned operator ^=(const unsigned i) { return data = (data ^ i) & mask; }
|
||||
inline unsigned operator &=(const unsigned i) { return data = (data & i) & mask; }
|
||||
inline unsigned operator<<=(const unsigned i) { return data = (data << i) & mask; }
|
||||
inline unsigned operator>>=(const unsigned i) { return data = (data >> i) & mask; }
|
||||
inline unsigned operator +=(const unsigned i) { return data = (data + i) & mask; }
|
||||
inline unsigned operator -=(const unsigned i) { return data = (data - i) & mask; }
|
||||
inline unsigned operator *=(const unsigned i) { return data = (data * i) & mask; }
|
||||
inline unsigned operator /=(const unsigned i) { return data = (data / i) & mask; }
|
||||
inline unsigned operator %=(const unsigned i) { return data = (data % i) & mask; }
|
||||
|
||||
inline void bits(unsigned bits) { mask = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1); data &= mask; }
|
||||
inline varuint_t() : data(0), mask(~0U) {}
|
||||
inline varuint_t(const unsigned i) : data(i), mask(~0U) {}
|
||||
};
|
||||
|
||||
class varuintmax_t {
|
||||
private:
|
||||
uintmax_t data;
|
||||
uintmax_t mask;
|
||||
|
||||
public:
|
||||
inline operator uintmax_t() const { return data; }
|
||||
inline uintmax_t operator ++(int) { uintmax_t r = data; data = (data + 1) & mask; return r; }
|
||||
inline uintmax_t operator --(int) { uintmax_t r = data; data = (data - 1) & mask; return r; }
|
||||
inline uintmax_t operator ++() { return data = (data + 1) & mask; }
|
||||
inline uintmax_t operator --() { return data = (data - 1) & mask; }
|
||||
inline uintmax_t operator =(const uintmax_t i) { return data = (i) & mask; }
|
||||
inline uintmax_t operator |=(const uintmax_t i) { return data = (data | i) & mask; }
|
||||
inline uintmax_t operator ^=(const uintmax_t i) { return data = (data ^ i) & mask; }
|
||||
inline uintmax_t operator &=(const uintmax_t i) { return data = (data & i) & mask; }
|
||||
inline uintmax_t operator<<=(const uintmax_t i) { return data = (data << i) & mask; }
|
||||
inline uintmax_t operator>>=(const uintmax_t i) { return data = (data >> i) & mask; }
|
||||
inline uintmax_t operator +=(const uintmax_t i) { return data = (data + i) & mask; }
|
||||
inline uintmax_t operator -=(const uintmax_t i) { return data = (data - i) & mask; }
|
||||
inline uintmax_t operator *=(const uintmax_t i) { return data = (data * i) & mask; }
|
||||
inline uintmax_t operator /=(const uintmax_t i) { return data = (data / i) & mask; }
|
||||
inline uintmax_t operator %=(const uintmax_t i) { return data = (data % i) & mask; }
|
||||
|
||||
inline void bits(unsigned bits) { mask = (1ULL << (bits - 1)) + ((1ULL << (bits - 1)) - 1); data &= mask; }
|
||||
inline varuintmax_t() : data(0), mask(~0ULL) {}
|
||||
inline varuintmax_t(const uintmax_t i) : data(i), mask(~0ULL) {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,281 @@
|
|||
#ifndef NALL_VECTOR_HPP
|
||||
#define NALL_VECTOR_HPP
|
||||
|
||||
#include <initializer_list>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/concept.hpp>
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
//linear_vector
|
||||
//memory: O(capacity * 2)
|
||||
//
|
||||
//linear_vector uses placement new + manual destructor calls to create a
|
||||
//contiguous block of memory for all objects. accessing individual elements
|
||||
//is fast, though resizing the array incurs significant overhead.
|
||||
//reserve() overhead is reduced from quadratic time to amortized constant time
|
||||
//by resizing twice as much as requested.
|
||||
//
|
||||
//if objects hold memory address references to themselves (introspection), a
|
||||
//valid copy constructor will be needed to keep pointers valid.
|
||||
|
||||
template<typename T> class linear_vector {
|
||||
protected:
|
||||
T *pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) {
|
||||
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
|
||||
free(pool);
|
||||
}
|
||||
pool = 0;
|
||||
poolsize = 0;
|
||||
objectsize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
|
||||
|
||||
T *poolcopy = (T*)calloc(newsize, sizeof(T));
|
||||
for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]);
|
||||
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
|
||||
free(pool);
|
||||
pool = poolcopy;
|
||||
poolsize = newsize;
|
||||
objectsize = min(objectsize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(newsize);
|
||||
|
||||
if(newsize < objectsize) {
|
||||
//vector is shrinking; destroy excess objects
|
||||
for(unsigned i = newsize; i < objectsize; i++) pool[i].~T();
|
||||
} else if(newsize > objectsize) {
|
||||
//vector is expanding; allocate new objects
|
||||
for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T;
|
||||
}
|
||||
|
||||
objectsize = newsize;
|
||||
}
|
||||
|
||||
void append(const T data) {
|
||||
if(objectsize + 1 > poolsize) reserve(objectsize + 1);
|
||||
new(pool + objectsize++) T(data);
|
||||
}
|
||||
|
||||
template<typename U> void insert(unsigned index, const U list) {
|
||||
linear_vector<T> merged;
|
||||
for(unsigned i = 0; i < index; i++) merged.append(pool[i]);
|
||||
foreach(item, list) merged.append(item);
|
||||
for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]);
|
||||
operator=(merged);
|
||||
}
|
||||
|
||||
void insert(unsigned index, const T item) {
|
||||
insert(index, linear_vector<T>{ item });
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
inline T& operator[](unsigned index) {
|
||||
if(index >= objectsize) resize(index + 1);
|
||||
return pool[index];
|
||||
}
|
||||
|
||||
inline const T& operator[](unsigned index) const {
|
||||
if(index >= objectsize) throw "vector[] out of bounds";
|
||||
return pool[index];
|
||||
}
|
||||
|
||||
//copy
|
||||
inline linear_vector<T>& operator=(const linear_vector<T> &source) {
|
||||
reset();
|
||||
reserve(source.capacity());
|
||||
resize(source.size());
|
||||
for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
linear_vector(const linear_vector<T> &source) : pool(0), poolsize(0), objectsize(0) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
//move
|
||||
inline linear_vector<T>& operator=(linear_vector<T> &&source) {
|
||||
reset();
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
objectsize = source.objectsize;
|
||||
source.pool = 0;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
linear_vector(linear_vector<T> &&source) : pool(0), poolsize(0), objectsize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
//construction
|
||||
linear_vector() : pool(0), poolsize(0), objectsize(0) {
|
||||
}
|
||||
|
||||
linear_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
|
||||
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
||||
}
|
||||
|
||||
~linear_vector() {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
//pointer_vector
|
||||
//memory: O(1)
|
||||
//
|
||||
//pointer_vector keeps an array of pointers to each vector object. this adds
|
||||
//significant overhead to individual accesses, but allows for optimal memory
|
||||
//utilization.
|
||||
//
|
||||
//by guaranteeing that the base memory address of each objects never changes,
|
||||
//this avoids the need for an object to have a valid copy constructor.
|
||||
|
||||
template<typename T> class pointer_vector {
|
||||
protected:
|
||||
T **pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) {
|
||||
for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; }
|
||||
free(pool);
|
||||
}
|
||||
pool = 0;
|
||||
poolsize = 0;
|
||||
objectsize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
|
||||
|
||||
for(unsigned i = newsize; i < objectsize; i++) {
|
||||
if(pool[i]) { delete pool[i]; pool[i] = 0; }
|
||||
}
|
||||
|
||||
pool = (T**)realloc(pool, newsize * sizeof(T*));
|
||||
for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0;
|
||||
poolsize = newsize;
|
||||
objectsize = min(objectsize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(newsize);
|
||||
|
||||
for(unsigned i = newsize; i < objectsize; i++) {
|
||||
if(pool[i]) { delete pool[i]; pool[i] = 0; }
|
||||
}
|
||||
|
||||
objectsize = newsize;
|
||||
}
|
||||
|
||||
void append(const T data) {
|
||||
if(objectsize + 1 > poolsize) reserve(objectsize + 1);
|
||||
pool[objectsize++] = new T(data);
|
||||
}
|
||||
|
||||
template<typename U> void insert(unsigned index, const U list) {
|
||||
pointer_vector<T> merged;
|
||||
for(unsigned i = 0; i < index; i++) merged.append(*pool[i]);
|
||||
foreach(item, list) merged.append(item);
|
||||
for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]);
|
||||
operator=(merged);
|
||||
}
|
||||
|
||||
void insert(unsigned index, const T item) {
|
||||
insert(index, pointer_vector<T>{ item });
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
inline T& operator[](unsigned index) {
|
||||
if(index >= objectsize) resize(index + 1);
|
||||
if(!pool[index]) pool[index] = new T;
|
||||
return *pool[index];
|
||||
}
|
||||
|
||||
inline const T& operator[](unsigned index) const {
|
||||
if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
|
||||
return *pool[index];
|
||||
}
|
||||
|
||||
//copy
|
||||
inline pointer_vector<T>& operator=(const pointer_vector<T> &source) {
|
||||
reset();
|
||||
reserve(source.capacity());
|
||||
resize(source.size());
|
||||
for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
pointer_vector(const pointer_vector<T> &source) : pool(0), poolsize(0), objectsize(0) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
//move
|
||||
inline pointer_vector<T>& operator=(pointer_vector<T> &&source) {
|
||||
reset();
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
objectsize = source.objectsize;
|
||||
source.pool = 0;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
pointer_vector(pointer_vector<T> &&source) : pool(0), poolsize(0), objectsize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
//construction
|
||||
pointer_vector() : pool(0), poolsize(0), objectsize(0) {
|
||||
}
|
||||
|
||||
pointer_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
|
||||
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
||||
}
|
||||
|
||||
~pointer_vector() {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct has_size<linear_vector<T>> { enum { value = true }; };
|
||||
template<typename T> struct has_size<pointer_vector<T>> { enum { value = true }; };
|
||||
}
|
||||
|
||||
#endif
|
|
@ -26,6 +26,9 @@ struct Window::Data {
|
|||
COLORREF brushColor;
|
||||
HMENU menu;
|
||||
HWND status;
|
||||
bool isFullscreen;
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, con
|
|||
widget->window = CreateWindowEx(
|
||||
0, L"phoenix_window", utf16_t(text),
|
||||
WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX,
|
||||
x, y, width, height,
|
||||
window->x = x, window->y = y, window->width = width, window->height = height,
|
||||
0, 0, GetModuleHandle(0), 0
|
||||
);
|
||||
window->menu = CreateMenu();
|
||||
|
@ -39,6 +39,13 @@ Geometry Window::geometry() {
|
|||
}
|
||||
|
||||
void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
|
||||
if(window->isFullscreen == false) {
|
||||
window->x = x;
|
||||
window->y = y;
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
}
|
||||
|
||||
bool isVisible = visible();
|
||||
if(isVisible) setVisible(false);
|
||||
SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
|
@ -70,16 +77,33 @@ void Window::setStatusVisible(bool visible) {
|
|||
resize(window->width, window->height);
|
||||
}
|
||||
|
||||
bool Window::fullscreen() {
|
||||
return window->isFullscreen;
|
||||
}
|
||||
|
||||
void Window::setFullscreen(bool fullscreen) {
|
||||
window->isFullscreen = fullscreen;
|
||||
if(fullscreen == false) {
|
||||
SetWindowLong(widget->window, GWL_STYLE, WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX);
|
||||
setGeometry(window->x, window->y, window->width, window->height);
|
||||
} else {
|
||||
SetWindowLong(widget->window, GWL_STYLE, WS_VISIBLE | WS_POPUP);
|
||||
setGeometry(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
|
||||
}
|
||||
}
|
||||
|
||||
Window::Window() {
|
||||
window = new Window::Data;
|
||||
window->defaultFont = 0;
|
||||
window->brush = 0;
|
||||
window->isFullscreen = false;
|
||||
window->x = 0;
|
||||
window->y = 0;
|
||||
window->width = 0;
|
||||
window->height = 0;
|
||||
}
|
||||
|
||||
void Window::resize(unsigned width, unsigned height) {
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
|
||||
SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED);
|
||||
RECT rc;
|
||||
GetClientRect(widget->window, &rc);
|
||||
|
|
|
@ -112,6 +112,8 @@ struct Window : Widget {
|
|||
void setStatusText(const nall::string &text);
|
||||
void setMenuVisible(bool visible = true);
|
||||
void setStatusVisible(bool visible = true);
|
||||
bool fullscreen();
|
||||
void setFullscreen(bool fullscreen = true);
|
||||
Window();
|
||||
//private:
|
||||
struct Data;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
public:
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
private:
|
||||
|
||||
struct Regs {
|
||||
//internal
|
||||
uint8 ppu1_mdr;
|
||||
|
@ -86,6 +92,4 @@ uint8 cgram_read(unsigned addr);
|
|||
void cgram_write(unsigned addr, uint8 data);
|
||||
|
||||
void mmio_update_video_mode();
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
void mmio_reset();
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
bool Cheat::active() const { return cheat_enabled; }
|
||||
bool Cheat::exists(unsigned addr) const { return bitmask[addr >> 3] & 1 << (addr & 7); }
|
|
@ -15,7 +15,7 @@ void Cheat::enable(bool state) {
|
|||
}
|
||||
|
||||
void Cheat::synchronize() {
|
||||
memset(bitmask, 0x00, sizeof bitmask);
|
||||
memcpy(bus.lookup, lookup, 16 * 1024 * 1024);
|
||||
code_enabled = false;
|
||||
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
|
@ -26,16 +26,16 @@ void Cheat::synchronize() {
|
|||
code_enabled = true;
|
||||
|
||||
unsigned addr = mirror(code.addr[n]);
|
||||
bitmask[addr >> 3] |= 1 << (addr & 7);
|
||||
bus.lookup[addr] = 0xff;
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
||||
unsigned mirroraddr;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
|
||||
bus.lookup[mirroraddr] = 0xff;
|
||||
|
||||
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
|
||||
bus.lookup[mirroraddr] = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ void Cheat::synchronize() {
|
|||
cheat_enabled = system_enabled && code_enabled;
|
||||
}
|
||||
|
||||
bool Cheat::read(unsigned addr, uint8 &data) const {
|
||||
uint8 Cheat::read(unsigned addr) const {
|
||||
addr = mirror(addr);
|
||||
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
|
@ -53,20 +53,37 @@ bool Cheat::read(unsigned addr, uint8 &data) const {
|
|||
|
||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
||||
if(addr == mirror(code.addr[n])) {
|
||||
data = code.data[n];
|
||||
return true;
|
||||
return code.data[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void Cheat::init() {
|
||||
bus.reader[0xff] = [](unsigned addr) {
|
||||
bus.reader[cheat.lookup[addr]](bus.target[addr]);
|
||||
return cheat.read(addr);
|
||||
};
|
||||
|
||||
bus.writer[0xff] = [](unsigned addr, uint8 data) {
|
||||
return bus.writer[cheat.lookup[addr]](bus.target[addr], data);
|
||||
};
|
||||
|
||||
memcpy(lookup, bus.lookup, 16 * 1024 * 1024);
|
||||
}
|
||||
|
||||
Cheat::Cheat() {
|
||||
lookup = new uint8[16 * 1024 * 1024];
|
||||
system_enabled = true;
|
||||
synchronize();
|
||||
}
|
||||
|
||||
Cheat::~Cheat() {
|
||||
delete[] lookup;
|
||||
}
|
||||
|
||||
//===============
|
||||
//encode / decode
|
||||
//===============
|
||||
|
|
|
@ -14,18 +14,17 @@ public:
|
|||
bool enabled() const;
|
||||
void enable(bool);
|
||||
void synchronize();
|
||||
bool read(unsigned, uint8&) const;
|
||||
|
||||
inline bool active() const;
|
||||
inline bool exists(unsigned addr) const;
|
||||
uint8 read(unsigned) const;
|
||||
void init();
|
||||
|
||||
Cheat();
|
||||
~Cheat();
|
||||
|
||||
static bool decode(const char*, unsigned&, uint8&, Type&);
|
||||
static bool encode(string&, unsigned, uint8, Type);
|
||||
|
||||
private:
|
||||
uint8 bitmask[0x200000];
|
||||
uint8 *lookup;
|
||||
bool system_enabled;
|
||||
bool code_enabled;
|
||||
bool cheat_enabled;
|
||||
|
|
|
@ -52,28 +52,9 @@ MappedRAM::MappedRAM() : data_(0), size_(-1U), write_protect_(false) {}
|
|||
//Bus
|
||||
|
||||
uint8 Bus::read(unsigned addr) {
|
||||
#if defined(CHEAT_SYSTEM)
|
||||
if(cheat.active() && cheat.exists(addr)) {
|
||||
uint8 r;
|
||||
if(cheat.read(addr, r)) return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
linear_vector<MappedRead> &map = rdpage[addr >> 13];
|
||||
for(signed n = map.size() - 1; n >= 0; n--) {
|
||||
if(addr - map[n].lo < map[n].hi) {
|
||||
return map[n].read(addr + map[n].offset);
|
||||
}
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
return reader[lookup[addr]](target[addr]);
|
||||
}
|
||||
|
||||
void Bus::write(unsigned addr, uint8 data) {
|
||||
linear_vector<MappedWrite> &map = wrpage[addr >> 13];
|
||||
for(signed n = map.size() - 1; n >= 0; n--) {
|
||||
if(addr - map[n].lo < map[n].hi) {
|
||||
return map[n].write(addr + map[n].offset, data);
|
||||
}
|
||||
}
|
||||
return writer[lookup[addr]](target[addr], data);
|
||||
}
|
||||
|
|
|
@ -43,35 +43,25 @@ void Bus::map(
|
|||
) {
|
||||
assert(bank_lo <= bank_hi && bank_lo <= 0xff);
|
||||
assert(addr_lo <= addr_hi && addr_lo <= 0xffff);
|
||||
unsigned id = idcount++;
|
||||
assert(id < 255);
|
||||
reader[id] = rd;
|
||||
writer[id] = wr;
|
||||
|
||||
unsigned page_lo = addr_lo >> 13;
|
||||
unsigned page_hi = addr_hi >> 13;
|
||||
if(length == 0) length = (bank_hi - bank_lo + 1) * (addr_hi - addr_lo + 1);
|
||||
|
||||
unsigned offset = 0;
|
||||
for(unsigned bank = bank_lo; bank <= bank_hi; bank++) {
|
||||
for(unsigned page = page_lo; page <= page_hi; page++) {
|
||||
unsigned map_addr = (bank << 16) | (page << 13);
|
||||
unsigned map_page = map_addr >> 13;
|
||||
unsigned map_lo = map_addr | (page == page_lo ? addr_lo & 0x1fff : 0x0000);
|
||||
unsigned map_hi = map_addr | (page == page_hi ? addr_hi & 0x1fff : 0x1fff);
|
||||
|
||||
unsigned out_adjust, out_length, out_offset;
|
||||
if(mode == MapMode::Direct) {
|
||||
out_offset = 0;
|
||||
out_length = map_hi - map_lo + 1;
|
||||
} else if(mode == MapMode::Linear) {
|
||||
out_offset = base + mirror(offset, length) - map_lo;
|
||||
out_length = min(map_hi - map_lo + 1, length - offset);
|
||||
offset += map_hi - map_lo + 1;
|
||||
offset %= length;
|
||||
for(unsigned addr = addr_lo; addr <= addr_hi; addr++) {
|
||||
unsigned destaddr = (bank << 16) | addr;
|
||||
if(mode == MapMode::Linear) {
|
||||
destaddr = base + mirror(offset, length);
|
||||
offset = (offset + 1) % length;
|
||||
} else if(mode == MapMode::Shadow) {
|
||||
out_offset = base + mirror(map_addr, length) - map_lo;
|
||||
out_length = map_hi - map_lo + 1;
|
||||
destaddr = base + mirror(destaddr, length);
|
||||
}
|
||||
|
||||
if(rd) rdpage[map_page].append({ rd, map_lo, out_length, out_offset });
|
||||
if(wr) wrpage[map_page].append({ wr, map_lo, out_length, out_offset });
|
||||
lookup[(bank << 16) | addr] = id;
|
||||
target[(bank << 16) | addr] = destaddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,10 +79,11 @@ void Bus::unload_cart() {
|
|||
}
|
||||
|
||||
void Bus::map_reset() {
|
||||
for(unsigned n = 0; n < 2048; n++) {
|
||||
rdpage[n].reset();
|
||||
wrpage[n].reset();
|
||||
}
|
||||
function<uint8 (unsigned)> reader = [](unsigned) { return cpu.regs.mdr; };
|
||||
function<void (unsigned, uint8)> writer = [](unsigned, uint8) {};
|
||||
|
||||
idcount = 0;
|
||||
map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, reader, writer);
|
||||
}
|
||||
|
||||
void Bus::map_xml() {
|
||||
|
@ -140,4 +131,14 @@ void Bus::reset() {
|
|||
map(MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, { &CPU::mmio_read, &cpu }, { &CPU::mmio_write, &cpu });
|
||||
}
|
||||
|
||||
Bus::Bus() {
|
||||
lookup = new uint8 [16 * 1024 * 1024];
|
||||
target = new uint32[16 * 1024 * 1024];
|
||||
}
|
||||
|
||||
Bus::~Bus() {
|
||||
delete[] lookup;
|
||||
delete[] target;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,20 +53,12 @@ struct Bus {
|
|||
void power();
|
||||
void reset();
|
||||
|
||||
struct MappedRead {
|
||||
function<uint8 (unsigned)> read;
|
||||
unsigned lo, hi;
|
||||
unsigned offset;
|
||||
};
|
||||
uint8 *lookup;
|
||||
uint32 *target;
|
||||
|
||||
struct MappedWrite {
|
||||
function<void (unsigned, uint8)> write;
|
||||
unsigned lo, hi;
|
||||
unsigned offset;
|
||||
};
|
||||
|
||||
linear_vector<MappedRead> rdpage[2048];
|
||||
linear_vector<MappedWrite> wrpage[2048];
|
||||
unsigned idcount;
|
||||
function<uint8 (unsigned)> reader[256];
|
||||
function<void (unsigned, uint8)> writer[256];
|
||||
|
||||
enum class MapMode : unsigned { Direct, Linear, Shadow };
|
||||
void map(
|
||||
|
@ -79,6 +71,8 @@ struct Bus {
|
|||
);
|
||||
|
||||
void serialize(serializer&);
|
||||
Bus();
|
||||
~Bus();
|
||||
|
||||
private:
|
||||
void map_reset();
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
public:
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
private:
|
||||
|
||||
struct {
|
||||
uint8 ppu1_mdr;
|
||||
uint8 ppu2_mdr;
|
||||
|
@ -154,5 +160,3 @@ uint8 mmio_r213e(); //STAT77
|
|||
uint8 mmio_r213f(); //STAT78
|
||||
|
||||
void mmio_reset();
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "074.05";
|
||||
static const char Version[] = "074.06";
|
||||
static const unsigned SerializerVersion = 17;
|
||||
}
|
||||
}
|
||||
|
||||
//#define DEBUGGER
|
||||
#define CHEAT_SYSTEM
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
|
@ -130,7 +129,6 @@ namespace SNES {
|
|||
|
||||
#include <snes/memory/memory-inline.hpp>
|
||||
#include <snes/ppu/counter/counter-inline.hpp>
|
||||
#include <snes/cheat/cheat-inline.hpp>
|
||||
}
|
||||
|
||||
namespace nall {
|
||||
|
|
|
@ -156,6 +156,7 @@ void System::power() {
|
|||
|
||||
scheduler.init();
|
||||
serialize_init();
|
||||
cheat.init();
|
||||
|
||||
input.update();
|
||||
//video.update();
|
||||
|
@ -194,6 +195,7 @@ void System::reset() {
|
|||
|
||||
scheduler.init();
|
||||
serialize_init();
|
||||
cheat.init();
|
||||
|
||||
input.port_set_device(0, config.controller_port1);
|
||||
input.port_set_device(1, config.controller_port2);
|
||||
|
|
|
@ -28,6 +28,10 @@ void InputMapper::poll_hotkeys(unsigned scancode, int16_t value) {
|
|||
utility.showMessage({ "Slot ", activeSlot, " selected" });
|
||||
}
|
||||
|
||||
if(scancode == keyboard(0)[Keyboard::F11]) {
|
||||
utility.setFullscreen(!mainWindow.fullscreen());
|
||||
}
|
||||
|
||||
//pause
|
||||
if(scancode == keyboard(0)[Keyboard::P]) {
|
||||
application.pause = !application.pause;
|
||||
|
|
|
@ -75,6 +75,10 @@ void Utility::setScale(unsigned scale) {
|
|||
mainWindow.setGeometry(geom.x, geom.y, width, height);
|
||||
}
|
||||
|
||||
void Utility::setFullscreen(bool fullscreen) {
|
||||
mainWindow.setFullscreen(fullscreen);
|
||||
}
|
||||
|
||||
void Utility::setFilter() {
|
||||
if(filter.opened()) filter.close();
|
||||
if(config.video.filter == "") return;
|
||||
|
|
|
@ -6,6 +6,7 @@ struct Utility : property<Utility> {
|
|||
|
||||
void setControllers();
|
||||
void setScale(unsigned scale = 0);
|
||||
void setFullscreen(bool fullscreen = true);
|
||||
void setFilter();
|
||||
void setShader();
|
||||
|
||||
|
|
Loading…
Reference in New Issue