mirror of https://github.com/bsnes-emu/bsnes.git
Update to v091r11 release.
byuu says: This release refines HSU1 support as a bidirectional protocol, nests SFC manifests as "release/cartridge" and "release/information" (but release/ is not guaranteed to be finalized just yet), removes the database integration, and adds support for ananke. ananke represents inevitability. It's a library that, when installed, higan can use to load files from the command-line, and also from a new File -> Load Game menu option. I need to change the build rules a bit for it to work on Windows (need to make phoenix a DLL, basically), but it works now on Linux. Right now, it only takes *.sfc file names, looks them up in the included database, converts them to game folders, and returns the game folder path for higan to load. The idea is to continue expanding it to support everything we can that I don't want in the higan core: - load *.sfc, *.smc, *.swc, *.fig files - remove SNES copier headers - split apart merged firmware files - pull in external firmware files (eg dsp1b.rom - these are staying merged, just as SPC7110 prg+dat are merged) - load *.zip and *.7z archives - prompt for selection on multi-file archives - generate manifest files based on heuristics - apply BPS patches The "Load" menu option has been renamed to "Library", to represent games in your library. I'm going to add some sort of suffix to indicate unverified games, and use a different folder icon for those (eg manifests built on heuristics rather than from the database.) So basically, to future end users: File -> Load Game will be how they play games. Library -> (specific system) can be thought of as an infinitely-sized recent games list. purify will likely become a simple stub that invokes ananke's functions. No reason to duplicate all that code.
This commit is contained in:
parent
d4751c5244
commit
84e98833ca
|
@ -1,3 +1,5 @@
|
||||||
purify/*.o
|
purify/*.o
|
||||||
purify/purify
|
purify/purify
|
||||||
purify/analyze-gba
|
purify/analyze-gba
|
||||||
|
ananke/ananke.o
|
||||||
|
ananke/libananke.so
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
include nall/Makefile
|
||||||
|
include phoenix/Makefile
|
||||||
|
|
||||||
|
path := /usr/local/lib
|
||||||
|
flags := -std=gnu++11 -I. -O3 -fomit-frame-pointer
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(cpp) $(flags) -fPIC -o ananke.o -c ananke.cpp
|
||||||
|
$(cpp) $(flags) -shared -Wl,-soname,libananke.so.1 -o libananke.so ananke.o
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-@$(call delete,*.o)
|
||||||
|
-@$(call delete,*.so)
|
||||||
|
|
||||||
|
install: uninstall
|
||||||
|
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
|
||||||
|
if [ -d ~/.config/ananke/database ]; then rm -r ~/.config/ananke/database; fi
|
||||||
|
cp -r database ~/.config/ananke/database
|
||||||
|
sudo cp libananke.so $(path)/libananke.so.1
|
||||||
|
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
|
||||||
|
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
|
||||||
|
|
||||||
|
sync:
|
||||||
|
ifeq ($(shell id -un),byuu)
|
||||||
|
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||||
|
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
|
||||||
|
cp -r ../nall ./nall
|
||||||
|
cp -r ../phoenix ./phoenix
|
||||||
|
rm -r nall/test
|
||||||
|
rm -r phoenix/nall
|
||||||
|
rm -r phoenix/test
|
||||||
|
endif
|
||||||
|
|
||||||
|
force:
|
|
@ -0,0 +1,85 @@
|
||||||
|
#include <nall/nall.hpp>
|
||||||
|
using namespace nall;
|
||||||
|
|
||||||
|
#include <phoenix/phoenix.hpp>
|
||||||
|
using namespace phoenix;
|
||||||
|
|
||||||
|
struct Ananke {
|
||||||
|
struct Configuration : configuration {
|
||||||
|
string path;
|
||||||
|
|
||||||
|
Configuration() {
|
||||||
|
append(path = userpath(), "Path");
|
||||||
|
directory::create({configpath(), "ananke/"});
|
||||||
|
load({configpath(), "ananke/settings.cfg"});
|
||||||
|
}
|
||||||
|
|
||||||
|
~Configuration() {
|
||||||
|
save({configpath(), "ananke/settings.cfg"});
|
||||||
|
}
|
||||||
|
} config;
|
||||||
|
|
||||||
|
string createSuperFamicomDatabase(const string &filename, Markup::Node &document, const string &manifest) {
|
||||||
|
string pathname = {
|
||||||
|
userpath(), "Emulation/Super Famicom/",
|
||||||
|
document["release/information/name"].text(),
|
||||||
|
" (", document["release/information/region"].text(), ")",
|
||||||
|
" (", document["release/information/revision"].text(), ")",
|
||||||
|
".sfc/"
|
||||||
|
};
|
||||||
|
directory::create(pathname);
|
||||||
|
|
||||||
|
file::write({pathname, "manifest.bml"}, (const uint8_t*)(const char*)manifest, manifest.length());
|
||||||
|
auto buffer = file::read(filename);
|
||||||
|
unsigned offset = 0;
|
||||||
|
|
||||||
|
for(auto &node : document["release/information/configuration"]) {
|
||||||
|
if(node.name != "rom") continue;
|
||||||
|
string name = node["name"].text();
|
||||||
|
unsigned size = node["size"].decimal();
|
||||||
|
if(file::exists({pathname, name}) == false) {
|
||||||
|
file::write({pathname, name}, buffer.data() + offset, size);
|
||||||
|
}
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
string openSuperFamicom(const string &filename) {
|
||||||
|
string sha256 = file::sha256(filename);
|
||||||
|
|
||||||
|
string databaseText = string::read({configpath(), "ananke/database/Super Famicom.bml"}).rtrim("\n");
|
||||||
|
lstring databaseItem = databaseText.split("\n\n");
|
||||||
|
|
||||||
|
for(auto &item : databaseItem) {
|
||||||
|
item.append("\n");
|
||||||
|
auto document = Markup::Document(item);
|
||||||
|
|
||||||
|
if(document["release/information/sha256"].text() == sha256) {
|
||||||
|
return createSuperFamicomDatabase(filename, document, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
string open(string filename = "") {
|
||||||
|
if(filename.empty()) filename = DialogWindow::fileOpen(Window::none(), config.path);
|
||||||
|
if(filename.empty()) return "";
|
||||||
|
config.path = dir(filename);
|
||||||
|
|
||||||
|
if(filename.endswith(".sfc")) return openSuperFamicom(filename);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" string ananke_browse(const string &filename) {
|
||||||
|
Ananke ananke;
|
||||||
|
return ananke.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" string ananke_open(const string &filename) {
|
||||||
|
Ananke ananke;
|
||||||
|
return ananke.open(filename);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,118 @@
|
||||||
|
# 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 Windows,$(uname)),)
|
||||||
|
platform := win
|
||||||
|
delete = del $(subst /,\,$1)
|
||||||
|
else ifneq ($(findstring CYGWIN,$(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.7
|
||||||
|
else
|
||||||
|
compiler := gcc-4.7
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
c := $(compiler) -std=gnu99
|
||||||
|
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||||
|
|
||||||
|
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,73 @@
|
||||||
|
#ifndef NALL_ANY_HPP
|
||||||
|
#define NALL_ANY_HPP
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <nall/traits.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
struct any {
|
||||||
|
bool empty() const { return container; }
|
||||||
|
const std::type_info& type() const { return container ? container->type() : typeid(void); }
|
||||||
|
|
||||||
|
template<typename T> any& operator=(const T& value_) {
|
||||||
|
typedef typename type_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(nullptr) {}
|
||||||
|
~any() { if(container) delete container; }
|
||||||
|
template<typename T> any(const T& value_) : container(nullptr) { 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 nullptr;
|
||||||
|
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 nullptr;
|
||||||
|
return &static_cast<any::holder<T>*>(value->container)->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,103 @@
|
||||||
|
#ifndef NALL_ATOI_HPP
|
||||||
|
#define NALL_ATOI_HPP
|
||||||
|
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
//note: this header is intended to form the base for user-defined literals;
|
||||||
|
//once they are supported by GCC. eg:
|
||||||
|
//unsigned operator "" b(const char *s) { return binary(s); }
|
||||||
|
//-> signed data = 1001b;
|
||||||
|
//(0b1001 is nicer, but is not part of the C++ standard)
|
||||||
|
|
||||||
|
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
|
||||||
|
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
|
||||||
|
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
constexpr inline uintmax_t binary(const char *s) {
|
||||||
|
return (
|
||||||
|
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||||
|
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||||
|
*s == '%' ? binary_(s + 1) :
|
||||||
|
binary_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t octal(const char *s) {
|
||||||
|
return (
|
||||||
|
octal_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline intmax_t integer(const char *s) {
|
||||||
|
return (
|
||||||
|
*s == '+' ? +decimal_(s + 1) :
|
||||||
|
*s == '-' ? -decimal_(s + 1) :
|
||||||
|
decimal_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t decimal(const char *s) {
|
||||||
|
return (
|
||||||
|
decimal_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t hex(const char *s) {
|
||||||
|
return (
|
||||||
|
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||||
|
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||||
|
*s == '$' ? hex_(s + 1) :
|
||||||
|
hex_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline intmax_t numeral(const char *s) {
|
||||||
|
return (
|
||||||
|
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||||
|
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||||
|
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||||
|
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||||
|
*s == '0' ? octal_(s + 1) :
|
||||||
|
*s == '+' ? +decimal_(s + 1) :
|
||||||
|
*s == '-' ? -decimal_(s + 1) :
|
||||||
|
decimal_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double fp(const char *s) {
|
||||||
|
return atof(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,118 @@
|
||||||
|
#ifndef NALL_BASE64_HPP
|
||||||
|
#define NALL_BASE64_HPP
|
||||||
|
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
struct base64 {
|
||||||
|
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||||
|
output = new char[inlength * 8 / 6 + 8]();
|
||||||
|
|
||||||
|
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 string encode(const string &data) {
|
||||||
|
char *buffer = nullptr;
|
||||||
|
encode(buffer, (const uint8_t*)(const char*)data, data.length());
|
||||||
|
string result = buffer;
|
||||||
|
delete[] buffer;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
|
||||||
|
unsigned inlength = strlen(input), infix = 0;
|
||||||
|
output = new uint8_t[inlength + 1]();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string decode(const string &data) {
|
||||||
|
uint8_t *buffer = nullptr;
|
||||||
|
unsigned size = 0;
|
||||||
|
decode(buffer, size, (const char*)data);
|
||||||
|
string result = (const char*)buffer;
|
||||||
|
delete[] buffer;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static char enc(uint8_t n) {
|
||||||
|
//base64 for URL encodings (URL = -_, MIME = +/)
|
||||||
|
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,84 @@
|
||||||
|
#ifndef NALL_BEAT_ARCHIVE_HPP
|
||||||
|
#define NALL_BEAT_ARCHIVE_HPP
|
||||||
|
|
||||||
|
#include <nall/beat/base.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct beatArchive : beatBase {
|
||||||
|
bool create(const string &beatname, string pathname, const string &metadata = "") {
|
||||||
|
if(fp.open(beatname, file::mode::write) == false) return false;
|
||||||
|
if(pathname.endswith("/") == false) pathname.append("/");
|
||||||
|
|
||||||
|
checksum = ~0;
|
||||||
|
writeString("BPA1");
|
||||||
|
writeNumber(metadata.length());
|
||||||
|
writeString(metadata);
|
||||||
|
|
||||||
|
lstring list;
|
||||||
|
ls(list, pathname, pathname);
|
||||||
|
for(auto &name : list) {
|
||||||
|
if(name.endswith("/")) {
|
||||||
|
name.rtrim<1>("/");
|
||||||
|
writeNumber(0 | ((name.length() - 1) << 1));
|
||||||
|
writeString(name);
|
||||||
|
} else {
|
||||||
|
file stream;
|
||||||
|
if(stream.open({pathname, name}, file::mode::read) == false) return false;
|
||||||
|
writeNumber(1 | ((name.length() - 1) << 1));
|
||||||
|
writeString(name);
|
||||||
|
unsigned size = stream.size();
|
||||||
|
writeNumber(size);
|
||||||
|
uint32_t checksum = ~0;
|
||||||
|
while(size--) {
|
||||||
|
uint8_t data = stream.read();
|
||||||
|
write(data);
|
||||||
|
checksum = crc32_adjust(checksum, data);
|
||||||
|
}
|
||||||
|
writeChecksum(~checksum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeChecksum(~checksum);
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unpack(const string &beatname, string pathname) {
|
||||||
|
if(fp.open(beatname, file::mode::read) == false) return false;
|
||||||
|
if(pathname.endswith("/") == false) pathname.append("/");
|
||||||
|
|
||||||
|
checksum = ~0;
|
||||||
|
if(readString(4) != "BPA1") return false;
|
||||||
|
unsigned length = readNumber();
|
||||||
|
while(length--) read();
|
||||||
|
|
||||||
|
directory::create(pathname);
|
||||||
|
while(fp.offset() < fp.size() - 4) {
|
||||||
|
unsigned data = readNumber();
|
||||||
|
string name = readString((data >> 1) + 1);
|
||||||
|
if(name.position("\\") || name.position("../")) return false; //block path exploits
|
||||||
|
|
||||||
|
if((data & 1) == 0) {
|
||||||
|
directory::create({pathname, name});
|
||||||
|
} else {
|
||||||
|
file stream;
|
||||||
|
if(stream.open({pathname, name}, file::mode::write) == false) return false;
|
||||||
|
unsigned size = readNumber();
|
||||||
|
uint32_t checksum = ~0;
|
||||||
|
while(size--) {
|
||||||
|
uint8_t data = read();
|
||||||
|
stream.write(data);
|
||||||
|
checksum = crc32_adjust(checksum, data);
|
||||||
|
}
|
||||||
|
if(readChecksum(~checksum) == false) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return readChecksum(~checksum);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,92 @@
|
||||||
|
#ifndef NALL_BEAT_BASE_HPP
|
||||||
|
#define NALL_BEAT_BASE_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct beatBase {
|
||||||
|
protected:
|
||||||
|
file fp;
|
||||||
|
uint32_t checksum;
|
||||||
|
|
||||||
|
void ls(lstring &list, const string &path, const string &basepath) {
|
||||||
|
lstring paths = directory::folders(path);
|
||||||
|
for(auto &pathname : paths) {
|
||||||
|
list.append(string{path, pathname}.ltrim<1>(basepath));
|
||||||
|
ls(list, {path, pathname}, basepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring files = directory::files(path);
|
||||||
|
for(auto &filename : files) {
|
||||||
|
list.append(string{path, filename}.ltrim<1>(basepath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(uint8_t data) {
|
||||||
|
fp.write(data);
|
||||||
|
checksum = crc32_adjust(checksum, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeNumber(uint64_t data) {
|
||||||
|
while(true) {
|
||||||
|
uint64_t x = data & 0x7f;
|
||||||
|
data >>= 7;
|
||||||
|
if(data == 0) return write(0x80 | x);
|
||||||
|
write(x);
|
||||||
|
data--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeString(const string &text) {
|
||||||
|
unsigned length = text.length();
|
||||||
|
for(unsigned n = 0; n < length; n++) write(text[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeChecksum(uint32_t checksum) {
|
||||||
|
write(checksum >> 0);
|
||||||
|
write(checksum >> 8);
|
||||||
|
write(checksum >> 16);
|
||||||
|
write(checksum >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t read() {
|
||||||
|
uint8_t data = fp.read();
|
||||||
|
checksum = crc32_adjust(checksum, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t readNumber() {
|
||||||
|
uint64_t data = 0, shift = 1;
|
||||||
|
while(true) {
|
||||||
|
uint8_t x = read();
|
||||||
|
data += (x & 0x7f) * shift;
|
||||||
|
if(x & 0x80) break;
|
||||||
|
shift <<= 7;
|
||||||
|
data += shift;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
string readString(unsigned length) {
|
||||||
|
string text;
|
||||||
|
text.reserve(length + 1);
|
||||||
|
for(unsigned n = 0; n < length; n++) {
|
||||||
|
text[n] = fp.read();
|
||||||
|
checksum = crc32_adjust(checksum, text[n]);
|
||||||
|
}
|
||||||
|
text[length] = 0;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readChecksum(uint32_t source) {
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
checksum |= read() << 0;
|
||||||
|
checksum |= read() << 8;
|
||||||
|
checksum |= read() << 16;
|
||||||
|
checksum |= read() << 24;
|
||||||
|
return checksum == source;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef NALL_BPS_DELTA_HPP
|
#ifndef NALL_BEAT_DELTA_HPP
|
||||||
#define NALL_BPS_DELTA_HPP
|
#define NALL_BEAT_DELTA_HPP
|
||||||
|
|
||||||
#include <nall/crc32.hpp>
|
#include <nall/crc32.hpp>
|
||||||
#include <nall/file.hpp>
|
#include <nall/file.hpp>
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef NALL_BPS_LINEAR_HPP
|
#ifndef NALL_BEAT_LINEAR_HPP
|
||||||
#define NALL_BPS_LINEAR_HPP
|
#define NALL_BEAT_LINEAR_HPP
|
||||||
|
|
||||||
#include <nall/crc32.hpp>
|
#include <nall/crc32.hpp>
|
||||||
#include <nall/file.hpp>
|
#include <nall/file.hpp>
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef NALL_BPS_METADATA_HPP
|
#ifndef NALL_BEAT_METADATA_HPP
|
||||||
#define NALL_BPS_METADATA_HPP
|
#define NALL_BEAT_METADATA_HPP
|
||||||
|
|
||||||
#include <nall/crc32.hpp>
|
#include <nall/crc32.hpp>
|
||||||
#include <nall/file.hpp>
|
#include <nall/file.hpp>
|
|
@ -1,9 +1,9 @@
|
||||||
#ifndef NALL_BPS_MULTI_HPP
|
#ifndef NALL_BEAT_MULTI_HPP
|
||||||
#define NALL_BPS_MULTI_HPP
|
#define NALL_BEAT_MULTI_HPP
|
||||||
|
|
||||||
#include <nall/bps/patch.hpp>
|
#include <nall/beat/patch.hpp>
|
||||||
#include <nall/bps/linear.hpp>
|
#include <nall/beat/linear.hpp>
|
||||||
#include <nall/bps/delta.hpp>
|
#include <nall/beat/delta.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef NALL_BPS_PATCH_HPP
|
#ifndef NALL_BEAT_PATCH_HPP
|
||||||
#define NALL_BPS_PATCH_HPP
|
#define NALL_BEAT_PATCH_HPP
|
||||||
|
|
||||||
#include <nall/crc32.hpp>
|
#include <nall/crc32.hpp>
|
||||||
#include <nall/file.hpp>
|
#include <nall/file.hpp>
|
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef NALL_BIT_HPP
|
||||||
|
#define NALL_BIT_HPP
|
||||||
|
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
template<unsigned bits>
|
||||||
|
inline uintmax_t uclamp(const uintmax_t x) {
|
||||||
|
enum : uintmax_t { b = 1ull << (bits - 1), y = b * 2 - 1 };
|
||||||
|
return y + ((x - y) & -(x < y)); //min(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned bits>
|
||||||
|
inline uintmax_t uclip(const uintmax_t x) {
|
||||||
|
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
|
||||||
|
return (x & m);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned bits>
|
||||||
|
inline intmax_t sclamp(const intmax_t x) {
|
||||||
|
enum : intmax_t { b = 1ull << (bits - 1), m = b - 1 };
|
||||||
|
return (x > m) ? m : (x < -b) ? -b : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned bits>
|
||||||
|
inline intmax_t sclip(const intmax_t x) {
|
||||||
|
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
|
||||||
|
return ((x & m) ^ b) - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace bit {
|
||||||
|
constexpr inline uintmax_t mask(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s == '0' || *s == '1' ? mask(s + 1, (sum << 1) | 1) :
|
||||||
|
*s == ' ' || *s == '_' ? mask(s + 1, sum) :
|
||||||
|
*s ? mask(s + 1, sum << 1) :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t test(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s == '0' || *s == '1' ? test(s + 1, (sum << 1) | (*s - '0')) :
|
||||||
|
*s == ' ' || *s == '_' ? test(s + 1, sum) :
|
||||||
|
*s ? test(s + 1, sum << 1) :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//lowest(0b1110) == 0b0010
|
||||||
|
constexpr inline uintmax_t lowest(const uintmax_t x) {
|
||||||
|
return x & -x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear_lowest(0b1110) == 0b1100
|
||||||
|
constexpr inline uintmax_t clear_lowest(const uintmax_t x) {
|
||||||
|
return x & (x - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set_lowest(0b0101) == 0b0111
|
||||||
|
constexpr inline uintmax_t set_lowest(const uintmax_t x) {
|
||||||
|
return x | (x + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//count number of bits set in a byte
|
||||||
|
inline unsigned count(uintmax_t x) {
|
||||||
|
unsigned count = 0;
|
||||||
|
do count += x & 1; while(x >>= 1);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//round up to next highest single bit:
|
||||||
|
//round(15) == 16, round(16) == 16, round(17) == 32
|
||||||
|
inline uintmax_t round(uintmax_t x) {
|
||||||
|
if((x & (x - 1)) == 0) return x;
|
||||||
|
while(x & (x - 1)) x &= x - 1;
|
||||||
|
return x << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,101 @@
|
||||||
|
#ifndef NALL_BMP_HPP
|
||||||
|
#define NALL_BMP_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
|
||||||
|
//BMP reader / writer
|
||||||
|
//author: byuu
|
||||||
|
//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct bmp {
|
||||||
|
inline static bool read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height);
|
||||||
|
inline static bool write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool bmp::read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, file::mode::read) == false) return false;
|
||||||
|
if(fp.size() < 0x36) return false;
|
||||||
|
|
||||||
|
if(fp.readm(2) != 0x424d) return false;
|
||||||
|
fp.seek(0x000a);
|
||||||
|
unsigned offset = fp.readl(4);
|
||||||
|
unsigned dibsize = fp.readl(4);
|
||||||
|
if(dibsize != 40) return false;
|
||||||
|
signed headerWidth = fp.readl(4);
|
||||||
|
if(headerWidth < 0) return false;
|
||||||
|
signed headerHeight = fp.readl(4);
|
||||||
|
fp.readl(2);
|
||||||
|
unsigned bitsPerPixel = fp.readl(2);
|
||||||
|
if(bitsPerPixel != 24 && bitsPerPixel != 32) return false;
|
||||||
|
unsigned compression = fp.readl(4);
|
||||||
|
if(compression != 0) return false;
|
||||||
|
fp.seek(offset);
|
||||||
|
|
||||||
|
bool noFlip = headerHeight < 0;
|
||||||
|
width = headerWidth, height = abs(headerHeight);
|
||||||
|
data = new uint32_t[width * height];
|
||||||
|
|
||||||
|
unsigned bytesPerPixel = bitsPerPixel / 8;
|
||||||
|
unsigned alignedWidth = width * bytesPerPixel;
|
||||||
|
unsigned paddingLength = 0;
|
||||||
|
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
uint32_t *p = noFlip ? data + y * width : data + (height - 1 - y) * width;
|
||||||
|
for(unsigned x = 0; x < width; x++, p++) {
|
||||||
|
*p = fp.readl(bytesPerPixel);
|
||||||
|
if(bytesPerPixel == 3) *p |= 255 << 24;
|
||||||
|
}
|
||||||
|
if(paddingLength) fp.readl(paddingLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bmp::write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, file::mode::write) == false) return false;
|
||||||
|
|
||||||
|
unsigned bitsPerPixel = alpha ? 32 : 24;
|
||||||
|
unsigned bytesPerPixel = bitsPerPixel / 8;
|
||||||
|
unsigned alignedWidth = width * bytesPerPixel;
|
||||||
|
unsigned paddingLength = 0;
|
||||||
|
unsigned imageSize = alignedWidth * height;
|
||||||
|
unsigned fileSize = 0x36 + imageSize;
|
||||||
|
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||||
|
|
||||||
|
fp.writem(0x424d, 2); //signature
|
||||||
|
fp.writel(fileSize, 4); //file size
|
||||||
|
fp.writel(0, 2); //reserved
|
||||||
|
fp.writel(0, 2); //reserved
|
||||||
|
fp.writel(0x36, 4); //offset
|
||||||
|
|
||||||
|
fp.writel(40, 4); //DIB size
|
||||||
|
fp.writel(width, 4); //width
|
||||||
|
fp.writel(-height, 4); //height
|
||||||
|
fp.writel(1, 2); //color planes
|
||||||
|
fp.writel(bitsPerPixel, 2); //bits per pixel
|
||||||
|
fp.writel(0, 4); //compression method (BI_RGB)
|
||||||
|
fp.writel(imageSize, 4); //image data size
|
||||||
|
fp.writel(3780, 4); //horizontal resolution
|
||||||
|
fp.writel(3780, 4); //vertical resolution
|
||||||
|
fp.writel(0, 4); //palette size
|
||||||
|
fp.writel(0, 4); //important color count
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
const uint32_t *p = (const uint32_t*)((const uint8_t*)data + y * pitch);
|
||||||
|
for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel);
|
||||||
|
if(paddingLength) fp.writel(0, paddingLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,152 @@
|
||||||
|
#ifndef NALL_COMPOSITOR_HPP
|
||||||
|
#define NALL_COMPOSITOR_HPP
|
||||||
|
|
||||||
|
#include <nall/intrinsics.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct compositor {
|
||||||
|
inline static bool enabled();
|
||||||
|
inline static bool enable(bool status);
|
||||||
|
|
||||||
|
#if defined(PLATFORM_X)
|
||||||
|
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
|
||||||
|
inline static Compositor detect();
|
||||||
|
|
||||||
|
inline static bool enabled_metacity();
|
||||||
|
inline static bool enable_metacity(bool status);
|
||||||
|
|
||||||
|
inline static bool enabled_xfwm4();
|
||||||
|
inline static bool enable_xfwm4(bool status);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(PLATFORM_X)
|
||||||
|
|
||||||
|
//Metacity
|
||||||
|
|
||||||
|
bool compositor::enabled_metacity() {
|
||||||
|
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
|
||||||
|
if(fp == 0) return false;
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||||
|
|
||||||
|
if(!memcmp(buffer, "true", 4)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable_metacity(bool status) {
|
||||||
|
FILE *fp;
|
||||||
|
if(status) {
|
||||||
|
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
|
||||||
|
} else {
|
||||||
|
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
|
||||||
|
}
|
||||||
|
if(fp == 0) return false;
|
||||||
|
pclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Xfwm4
|
||||||
|
|
||||||
|
bool compositor::enabled_xfwm4() {
|
||||||
|
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
|
||||||
|
if(fp == 0) return false;
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||||
|
|
||||||
|
if(!memcmp(buffer, "true", 4)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable_xfwm4(bool status) {
|
||||||
|
FILE *fp;
|
||||||
|
if(status) {
|
||||||
|
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
|
||||||
|
} else {
|
||||||
|
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
|
||||||
|
}
|
||||||
|
if(fp == 0) return false;
|
||||||
|
pclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//General
|
||||||
|
|
||||||
|
compositor::Compositor compositor::detect() {
|
||||||
|
Compositor result = Compositor::Unknown;
|
||||||
|
|
||||||
|
FILE *fp;
|
||||||
|
char buffer[512];
|
||||||
|
|
||||||
|
fp = popen("pidof metacity", "r");
|
||||||
|
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
fp = popen("pidof xfwm4", "r");
|
||||||
|
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enabled() {
|
||||||
|
switch(detect()) {
|
||||||
|
case Compositor::Metacity: return enabled_metacity();
|
||||||
|
case Compositor::Xfwm4: return enabled_xfwm4();
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable(bool status) {
|
||||||
|
switch(detect()) {
|
||||||
|
case Compositor::Metacity: return enable_metacity(status);
|
||||||
|
case Compositor::Xfwm4: return enable_xfwm4(status);
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(PLATFORM_WINDOWS)
|
||||||
|
|
||||||
|
bool compositor::enabled() {
|
||||||
|
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||||
|
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||||
|
if(module == 0) return false;
|
||||||
|
|
||||||
|
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
|
||||||
|
if(pDwmIsCompositionEnabled == 0) return false;
|
||||||
|
|
||||||
|
BOOL result;
|
||||||
|
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable(bool status) {
|
||||||
|
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||||
|
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||||
|
if(module == 0) return false;
|
||||||
|
|
||||||
|
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
|
||||||
|
if(pDwmEnableComposition == 0) return false;
|
||||||
|
|
||||||
|
if(pDwmEnableComposition(status) != S_OK) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool compositor::enabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable(bool) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,126 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
inline string get() const {
|
||||||
|
switch(type) {
|
||||||
|
case boolean_t: return { *(bool*)data };
|
||||||
|
case signed_t: return { *(signed*)data };
|
||||||
|
case unsigned_t: return { *(unsigned*)data };
|
||||||
|
case double_t: return { *(double*)data };
|
||||||
|
case string_t: return { "\"", *(string*)data, "\"" };
|
||||||
|
}
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
vector<item_t> list;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void append(T &data, const char *name, const char *desc = "") {
|
||||||
|
item_t item = { (uintptr_t)&data, name, desc };
|
||||||
|
if(configuration_traits::is_boolean<T>::value) item.type = boolean_t;
|
||||||
|
else if(configuration_traits::is_signed<T>::value) item.type = signed_t;
|
||||||
|
else if(configuration_traits::is_unsigned<T>::value) item.type = unsigned_t;
|
||||||
|
else if(configuration_traits::is_double<T>::value) item.type = double_t;
|
||||||
|
else if(configuration_traits::is_string<T>::value) item.type = string_t;
|
||||||
|
else item.type = unknown_t;
|
||||||
|
list.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
//deprecated
|
||||||
|
template<typename T>
|
||||||
|
inline void attach(T &data, const char *name, const char *desc = "") {
|
||||||
|
append(data, name, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline virtual bool load(const string &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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline virtual bool save(const string &filename) const {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, file::mode::write)) {
|
||||||
|
for(unsigned i = 0; i < list.size(); i++) {
|
||||||
|
string output;
|
||||||
|
output.append(list[i].name, " = ", list[i].get());
|
||||||
|
if(list[i].desc != "") output.append(" # ", list[i].desc);
|
||||||
|
output.append("\r\n");
|
||||||
|
fp.print(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef NALL_CRC16_HPP
|
||||||
|
#define NALL_CRC16_HPP
|
||||||
|
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
inline uint16_t crc16_adjust(uint16_t crc16, uint8_t data) {
|
||||||
|
for(unsigned n = 0; n < 8; n++) {
|
||||||
|
if((crc16 & 1) ^ (data & 1)) crc16 = (crc16 >> 1) ^ 0x8408;
|
||||||
|
else crc16 >>= 1;
|
||||||
|
data >>= 1;
|
||||||
|
}
|
||||||
|
return crc16;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t crc16_calculate(const uint8_t *data, unsigned length) {
|
||||||
|
uint16_t crc16 = ~0;
|
||||||
|
for(unsigned n = 0; n < length; n++) {
|
||||||
|
crc16 = crc16_adjust(crc16, data[n]);
|
||||||
|
}
|
||||||
|
return ~crc16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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,198 @@
|
||||||
|
#ifndef NALL_DIRECTORY_HPP
|
||||||
|
#define NALL_DIRECTORY_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/intrinsics.hpp>
|
||||||
|
#include <nall/sort.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
#else
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct directory {
|
||||||
|
static bool create(const string &pathname, unsigned permissions = 0755); //recursive
|
||||||
|
static bool remove(const string &pathname); //recursive
|
||||||
|
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(PLATFORM_WINDOWS)
|
||||||
|
inline bool directory::create(const string &pathname, unsigned permissions) {
|
||||||
|
string path;
|
||||||
|
lstring list = string{pathname}.transform("\\", "/").rtrim<1>("/").split("/");
|
||||||
|
bool result = true;
|
||||||
|
for(auto &part : list) {
|
||||||
|
path.append(part, "/");
|
||||||
|
result &= (_wmkdir(utf16_t(path)) == 0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool directory::remove(const string &pathname) {
|
||||||
|
lstring list = directory::contents(pathname);
|
||||||
|
for(auto &name : list) {
|
||||||
|
if(name.endswith("/")) directory::remove({pathname, name});
|
||||||
|
else file::remove({pathname, name});
|
||||||
|
}
|
||||||
|
return _wrmdir(utf16_t(pathname)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool directory::exists(const string &pathname) {
|
||||||
|
string name = pathname;
|
||||||
|
name.trim<1>("\"");
|
||||||
|
DWORD result = GetFileAttributes(utf16_t(name));
|
||||||
|
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 = (const char*)utf8_t(data.cFileName);
|
||||||
|
if(wildcard(name, pattern)) list.append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(FindNextFile(handle, &data) != false) {
|
||||||
|
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||||
|
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
string name = (const char*)utf8_t(data.cFileName);
|
||||||
|
if(wildcard(name, pattern)) list.append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FindClose(handle);
|
||||||
|
}
|
||||||
|
if(list.size() > 0) list.sort();
|
||||||
|
for(auto &name : list) name.append("/"); //must append after sorting
|
||||||
|
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 = (const char*)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 = (const char*)utf8_t(data.cFileName);
|
||||||
|
if(wildcard(name, pattern)) list.append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FindClose(handle);
|
||||||
|
}
|
||||||
|
if(list.size() > 0) list.sort();
|
||||||
|
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);
|
||||||
|
for(auto &file : files) folders.append(file);
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline bool directory::create(const string &pathname, unsigned permissions) {
|
||||||
|
string path;
|
||||||
|
lstring list = string{pathname}.rtrim<1>("/").split("/");
|
||||||
|
bool result = true;
|
||||||
|
for(auto &part : list) {
|
||||||
|
path.append(part, "/");
|
||||||
|
result &= (mkdir(path, permissions) == 0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool directory::remove(const string &pathname) {
|
||||||
|
lstring list = directory::contents(pathname);
|
||||||
|
for(auto &name : list) {
|
||||||
|
if(name.endswith("/")) directory::remove({pathname, name});
|
||||||
|
else file::remove({pathname, name});
|
||||||
|
}
|
||||||
|
return rmdir(pathname) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(ep->d_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dp);
|
||||||
|
}
|
||||||
|
if(list.size() > 0) list.sort();
|
||||||
|
for(auto &name : list) name.append("/"); //must append after sorting
|
||||||
|
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) list.sort();
|
||||||
|
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);
|
||||||
|
for(auto &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/intrinsics.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_WINDOWS)
|
||||||
|
#include <windows.h>
|
||||||
|
#include <nall/windows/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_WINDOWS)
|
||||||
|
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,13 @@
|
||||||
|
#ifndef NALL_DSP_HPP
|
||||||
|
#define NALL_DSP_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#ifdef __SSE__
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NALL_DSP_INTERNAL_HPP
|
||||||
|
#include <nall/dsp/core.hpp>
|
||||||
|
#undef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct Buffer {
|
||||||
|
double **sample;
|
||||||
|
uint16_t rdoffset;
|
||||||
|
uint16_t wroffset;
|
||||||
|
unsigned channels;
|
||||||
|
|
||||||
|
void setChannels(unsigned channels) {
|
||||||
|
for(unsigned c = 0; c < this->channels; c++) {
|
||||||
|
if(sample[c]) delete[] sample[c];
|
||||||
|
}
|
||||||
|
if(sample) delete[] sample;
|
||||||
|
|
||||||
|
this->channels = channels;
|
||||||
|
if(channels == 0) return;
|
||||||
|
|
||||||
|
sample = new double*[channels];
|
||||||
|
for(unsigned c = 0; c < channels; c++) {
|
||||||
|
sample[c] = new double[65536]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double& read(unsigned channel, signed offset = 0) {
|
||||||
|
return sample[channel][(uint16_t)(rdoffset + offset)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double& write(unsigned channel, signed offset = 0) {
|
||||||
|
return sample[channel][(uint16_t)(wroffset + offset)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear() {
|
||||||
|
for(unsigned c = 0; c < channels; c++) {
|
||||||
|
for(unsigned n = 0; n < 65536; n++) {
|
||||||
|
sample[c][n] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rdoffset = 0;
|
||||||
|
wroffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer() {
|
||||||
|
channels = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Buffer() {
|
||||||
|
setChannels(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,167 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
//precision: can be float, double or long double
|
||||||
|
#define real float
|
||||||
|
|
||||||
|
struct DSP;
|
||||||
|
|
||||||
|
struct Resampler {
|
||||||
|
DSP &dsp;
|
||||||
|
real frequency;
|
||||||
|
|
||||||
|
virtual void setFrequency() = 0;
|
||||||
|
virtual void clear() = 0;
|
||||||
|
virtual void sample() = 0;
|
||||||
|
Resampler(DSP &dsp) : dsp(dsp) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DSP {
|
||||||
|
enum class ResampleEngine : unsigned {
|
||||||
|
Nearest,
|
||||||
|
Linear,
|
||||||
|
Cosine,
|
||||||
|
Cubic,
|
||||||
|
Hermite,
|
||||||
|
Average,
|
||||||
|
Sinc,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void setChannels(unsigned channels);
|
||||||
|
inline void setPrecision(unsigned precision);
|
||||||
|
inline void setFrequency(real frequency); //inputFrequency
|
||||||
|
inline void setVolume(real volume);
|
||||||
|
inline void setBalance(real balance);
|
||||||
|
|
||||||
|
inline void setResampler(ResampleEngine resamplingEngine);
|
||||||
|
inline void setResamplerFrequency(real frequency); //outputFrequency
|
||||||
|
|
||||||
|
inline void sample(signed channel[]);
|
||||||
|
inline bool pending();
|
||||||
|
inline void read(signed channel[]);
|
||||||
|
|
||||||
|
inline void clear();
|
||||||
|
inline DSP();
|
||||||
|
inline ~DSP();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class ResampleNearest;
|
||||||
|
friend class ResampleLinear;
|
||||||
|
friend class ResampleCosine;
|
||||||
|
friend class ResampleCubic;
|
||||||
|
friend class ResampleAverage;
|
||||||
|
friend class ResampleHermite;
|
||||||
|
friend class ResampleSinc;
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
unsigned channels;
|
||||||
|
unsigned precision;
|
||||||
|
real frequency;
|
||||||
|
real volume;
|
||||||
|
real balance;
|
||||||
|
|
||||||
|
//internal
|
||||||
|
real intensity;
|
||||||
|
real intensityInverse;
|
||||||
|
} settings;
|
||||||
|
|
||||||
|
Resampler *resampler;
|
||||||
|
inline void write(real channel[]);
|
||||||
|
|
||||||
|
#include "buffer.hpp"
|
||||||
|
Buffer buffer;
|
||||||
|
Buffer output;
|
||||||
|
|
||||||
|
inline void adjustVolume();
|
||||||
|
inline void adjustBalance();
|
||||||
|
inline signed clamp(const unsigned bits, const signed x);
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "resample/nearest.hpp"
|
||||||
|
#include "resample/linear.hpp"
|
||||||
|
#include "resample/cosine.hpp"
|
||||||
|
#include "resample/cubic.hpp"
|
||||||
|
#include "resample/hermite.hpp"
|
||||||
|
#include "resample/average.hpp"
|
||||||
|
#include "resample/sinc.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
|
||||||
|
void DSP::sample(signed channel[]) {
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
|
||||||
|
}
|
||||||
|
buffer.wroffset++;
|
||||||
|
resampler->sample();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DSP::pending() {
|
||||||
|
return output.rdoffset != output.wroffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::read(signed channel[]) {
|
||||||
|
adjustVolume();
|
||||||
|
adjustBalance();
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
|
||||||
|
}
|
||||||
|
output.rdoffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::write(real channel[]) {
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
output.write(c) = channel[c];
|
||||||
|
}
|
||||||
|
output.wroffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::adjustVolume() {
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
output.read(c) *= settings.volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::adjustBalance() {
|
||||||
|
if(settings.channels != 2) return; //TODO: support > 2 channels
|
||||||
|
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
|
||||||
|
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
signed DSP::clamp(const unsigned bits, const signed x) {
|
||||||
|
const signed b = 1U << (bits - 1);
|
||||||
|
const signed m = (1U << (bits - 1)) - 1;
|
||||||
|
return (x > m) ? m : (x < -b) ? -b : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::clear() {
|
||||||
|
buffer.clear();
|
||||||
|
output.clear();
|
||||||
|
resampler->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DSP::DSP() {
|
||||||
|
setResampler(ResampleEngine::Hermite);
|
||||||
|
setResamplerFrequency(44100.0);
|
||||||
|
|
||||||
|
setChannels(2);
|
||||||
|
setPrecision(16);
|
||||||
|
setFrequency(44100.0);
|
||||||
|
setVolume(1.0);
|
||||||
|
setBalance(0.0);
|
||||||
|
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DSP::~DSP() {
|
||||||
|
if(resampler) delete resampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef real
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,72 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleAverage : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
inline void sampleLinear();
|
||||||
|
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleAverage::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleAverage::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleAverage::sample() {
|
||||||
|
//can only average if input frequency >= output frequency
|
||||||
|
if(step < 1.0) return sampleLinear();
|
||||||
|
|
||||||
|
fraction += 1.0;
|
||||||
|
|
||||||
|
real scalar = 1.0;
|
||||||
|
if(fraction > step) scalar = 1.0 - (fraction - step);
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fraction >= step) {
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) /= step;
|
||||||
|
}
|
||||||
|
dsp.output.wroffset++;
|
||||||
|
|
||||||
|
fraction -= step;
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleAverage::sampleLinear() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
channel[n] = a * (1.0 - mu) + b * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleCosine : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleCosine::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCosine::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCosine::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||||
|
|
||||||
|
channel[n] = a * (1.0 - mu) + b * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleCubic : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleCubic::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCubic::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCubic::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -3);
|
||||||
|
real b = dsp.buffer.read(n, -2);
|
||||||
|
real c = dsp.buffer.read(n, -1);
|
||||||
|
real d = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
real A = d - c - a + b;
|
||||||
|
real B = a - b - A;
|
||||||
|
real C = c - a;
|
||||||
|
real D = b;
|
||||||
|
|
||||||
|
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleHermite : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleHermite::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleHermite::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleHermite::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -3);
|
||||||
|
real b = dsp.buffer.read(n, -2);
|
||||||
|
real c = dsp.buffer.read(n, -1);
|
||||||
|
real d = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||||
|
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||||
|
|
||||||
|
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||||
|
|
||||||
|
mu1 = fraction;
|
||||||
|
mu2 = mu1 * mu1;
|
||||||
|
mu3 = mu2 * mu1;
|
||||||
|
|
||||||
|
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||||
|
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||||
|
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||||
|
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||||
|
|
||||||
|
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||||
|
a1 = mu3 - 2 * mu2 + mu1;
|
||||||
|
a2 = mu3 - mu2;
|
||||||
|
a3 = -2 * mu3 + 3 * mu2;
|
||||||
|
|
||||||
|
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,600 @@
|
||||||
|
// If these types are changed to anything other than "float", you should comment out the SSE detection directives below
|
||||||
|
// so that the SSE code is not used.
|
||||||
|
|
||||||
|
typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16
|
||||||
|
typedef float resample_samp_t;
|
||||||
|
|
||||||
|
|
||||||
|
// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE.
|
||||||
|
#define RESAMPLE_SSEREGPARM
|
||||||
|
|
||||||
|
#if defined(__SSE__)
|
||||||
|
#define SINCRESAMPLE_USE_SSE 1
|
||||||
|
#ifndef __x86_64__
|
||||||
|
#undef RESAMPLE_SSEREGPARM
|
||||||
|
#define RESAMPLE_SSEREGPARM __attribute__((sseregparm))
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// TODO: altivec here
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ResampleUtility
|
||||||
|
{
|
||||||
|
inline void kaiser_window(double* io, int count, double beta);
|
||||||
|
inline void gen_sinc(double* out, int size, double cutoff, double kaiser);
|
||||||
|
inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser);
|
||||||
|
inline void normalize(double* io, int size, double gain = 1.0);
|
||||||
|
|
||||||
|
inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class SincResampleHR
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d);
|
||||||
|
|
||||||
|
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||||
|
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||||
|
inline bool output_avail(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count);
|
||||||
|
|
||||||
|
unsigned ratio;
|
||||||
|
unsigned num_convolutions;
|
||||||
|
|
||||||
|
resample_coeff_t *coeffs;
|
||||||
|
std::vector<unsigned char> coeffs_mem;
|
||||||
|
|
||||||
|
// second half of ringbuffer should be copy of first half.
|
||||||
|
resample_samp_t *rb;
|
||||||
|
std::vector<unsigned char> rb_mem;
|
||||||
|
|
||||||
|
signed rb_readpos;
|
||||||
|
signed rb_writepos;
|
||||||
|
signed rb_in;
|
||||||
|
signed rb_eff_size;
|
||||||
|
|
||||||
|
friend class SincResample;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SincResample
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
QUALITY_LOW = 0,
|
||||||
|
QUALITY_MEDIUM = 2,
|
||||||
|
QUALITY_HIGH = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH);
|
||||||
|
|
||||||
|
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||||
|
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||||
|
inline bool output_avail(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min);
|
||||||
|
|
||||||
|
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM;
|
||||||
|
|
||||||
|
unsigned num_convolutions;
|
||||||
|
unsigned num_phases;
|
||||||
|
|
||||||
|
unsigned step_int;
|
||||||
|
double step_fract;
|
||||||
|
|
||||||
|
double input_pos_fract;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<resample_coeff_t *> coeffs; // Pointers into coeff_mem.
|
||||||
|
std::vector<unsigned char> coeff_mem;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<resample_samp_t> rb; // second half should be copy of first half.
|
||||||
|
signed rb_readpos;
|
||||||
|
signed rb_writepos;
|
||||||
|
signed rb_in;
|
||||||
|
|
||||||
|
bool hr_used;
|
||||||
|
SincResampleHR hr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Code:
|
||||||
|
//
|
||||||
|
//#include "resample.hpp"
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
namespace bit
|
||||||
|
{
|
||||||
|
inline unsigned round(unsigned x) {
|
||||||
|
if((x & (x - 1)) == 0) return x;
|
||||||
|
while(x & (x - 1)) x &= x - 1;
|
||||||
|
return x << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d)
|
||||||
|
{
|
||||||
|
const unsigned align_boundary = 16;
|
||||||
|
std::vector<double> coeffs_tmp;
|
||||||
|
double cutoff; // 1.0 = f/2
|
||||||
|
|
||||||
|
ratio = ratio_arg;
|
||||||
|
|
||||||
|
//num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even
|
||||||
|
num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1);
|
||||||
|
|
||||||
|
cutoff = (1.0 / ratio) - (d / num_convolutions);
|
||||||
|
|
||||||
|
//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff);
|
||||||
|
assert(num_convolutions > ratio);
|
||||||
|
|
||||||
|
|
||||||
|
// Generate windowed sinc of POWER
|
||||||
|
coeffs_tmp.resize(num_convolutions);
|
||||||
|
//ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||||
|
ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||||
|
ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions);
|
||||||
|
|
||||||
|
// Copy from coeffs_tmp to coeffs~
|
||||||
|
// We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really
|
||||||
|
// exist are allocated, zero'd mem.
|
||||||
|
|
||||||
|
coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1));
|
||||||
|
coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary);
|
||||||
|
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < num_convolutions; i++)
|
||||||
|
coeffs[i] = coeffs_tmp[i];
|
||||||
|
|
||||||
|
rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1;
|
||||||
|
rb_readpos = 0;
|
||||||
|
rb_writepos = 0;
|
||||||
|
rb_in = 0;
|
||||||
|
|
||||||
|
rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1));
|
||||||
|
rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool SincResampleHR::output_avail(void)
|
||||||
|
{
|
||||||
|
return(rb_in >= (signed)num_convolutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SincResampleHR::write(resample_samp_t sample)
|
||||||
|
{
|
||||||
|
assert(!output_avail());
|
||||||
|
|
||||||
|
rb[rb_writepos] = sample;
|
||||||
|
rb[rb_writepos + rb_eff_size] = sample;
|
||||||
|
rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1);
|
||||||
|
rb_in++;
|
||||||
|
}
|
||||||
|
|
||||||
|
resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count)
|
||||||
|
{
|
||||||
|
#if SINCRESAMPLE_USE_SSE
|
||||||
|
__m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||||
|
|
||||||
|
resample_samp_t accum;
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c += 8)
|
||||||
|
{
|
||||||
|
for(unsigned i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
__m128 co[2];
|
||||||
|
__m128 w[2];
|
||||||
|
|
||||||
|
co[i] = _mm_load_ps(&coeff[c + i * 4]);
|
||||||
|
w[i] = _mm_load_ps(&wave[c + i * 4]);
|
||||||
|
|
||||||
|
w[i] = _mm_mul_ps(w[i], co[i]);
|
||||||
|
|
||||||
|
accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3]));
|
||||||
|
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
|
||||||
|
_mm_store_ss(&accum, accum_vec);
|
||||||
|
|
||||||
|
return accum;
|
||||||
|
#else
|
||||||
|
resample_samp_t accum[4] = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c+= 4)
|
||||||
|
{
|
||||||
|
accum[0] += wave[c + 0] * coeff[c + 0];
|
||||||
|
accum[1] += wave[c + 1] * coeff[c + 1];
|
||||||
|
accum[2] += wave[c + 2] * coeff[c + 2];
|
||||||
|
accum[3] += wave[c + 3] * coeff[c + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may...
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
resample_samp_t SincResampleHR::read(void)
|
||||||
|
{
|
||||||
|
assert(output_avail());
|
||||||
|
resample_samp_t ret;
|
||||||
|
|
||||||
|
ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions);
|
||||||
|
|
||||||
|
rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1);
|
||||||
|
rb_in -= ratio;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality)
|
||||||
|
{
|
||||||
|
const struct
|
||||||
|
{
|
||||||
|
double beta;
|
||||||
|
double d;
|
||||||
|
unsigned pn_nume;
|
||||||
|
unsigned phases_min;
|
||||||
|
} qtab[5] =
|
||||||
|
{
|
||||||
|
{ 5.658, 3.62, 4096, 4 },
|
||||||
|
{ 6.764, 4.32, 8192, 4 },
|
||||||
|
{ 7.865, 5.0, 16384, 8 },
|
||||||
|
{ 8.960, 5.7, 32768, 16 },
|
||||||
|
{ 10.056, 6.4, 65536, 32 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
assert(ceil(input_rate) > 0);
|
||||||
|
assert(ceil(output_rate) > 0);
|
||||||
|
assert(ceil(input_rate / output_rate) <= 1024);
|
||||||
|
assert(ceil(output_rate / input_rate) <= 1024);
|
||||||
|
|
||||||
|
// The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when
|
||||||
|
// upsampling.
|
||||||
|
assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96);
|
||||||
|
assert(quality >= 0 && quality <= 4);
|
||||||
|
|
||||||
|
hr_used = false;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
// Round down to the nearest multiple of 4(so wave buffer remains aligned)
|
||||||
|
// It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2
|
||||||
|
// aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately
|
||||||
|
// to virtually eliminate over-attenuation.
|
||||||
|
unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3;
|
||||||
|
|
||||||
|
if(ioratio_rd >= 8)
|
||||||
|
{
|
||||||
|
hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4);
|
||||||
|
hr_used = true;
|
||||||
|
|
||||||
|
input_rate /= ioratio_rd;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min)
|
||||||
|
{
|
||||||
|
const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2.
|
||||||
|
const unsigned max_mult_minus1 = (max_mult_atatime - 1);
|
||||||
|
const unsigned conv_alignment_bytes = 16; // must be power of 2
|
||||||
|
const double input_to_output_ratio = input_rate / output_rate;
|
||||||
|
const double output_to_input_ratio = output_rate / input_rate;
|
||||||
|
double cutoff; // 1.0 = input_rate / 2
|
||||||
|
std::vector<double> coeff_init_buffer;
|
||||||
|
|
||||||
|
// Round up num_convolutions to be even.
|
||||||
|
if(output_rate > input_rate)
|
||||||
|
num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1;
|
||||||
|
else
|
||||||
|
num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1;
|
||||||
|
|
||||||
|
if(output_rate > input_rate) // Upsampling
|
||||||
|
cutoff = desired_bandwidth;
|
||||||
|
else // Downsampling
|
||||||
|
cutoff = output_to_input_ratio * desired_bandwidth;
|
||||||
|
|
||||||
|
// Round up to be even.
|
||||||
|
num_phases = (std::max<unsigned>(pn_nume / num_convolutions, phases_min) + 1) &~1;
|
||||||
|
|
||||||
|
// Adjust cutoff to account for the multiple phases.
|
||||||
|
cutoff = cutoff / num_phases;
|
||||||
|
|
||||||
|
assert((num_convolutions & 1) == 0);
|
||||||
|
assert((num_phases & 1) == 0);
|
||||||
|
|
||||||
|
// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases,
|
||||||
|
// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes));
|
||||||
|
|
||||||
|
coeff_init_buffer.resize(num_phases * num_convolutions);
|
||||||
|
|
||||||
|
coeffs.resize(num_phases + 1 + 1);
|
||||||
|
|
||||||
|
coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes);
|
||||||
|
|
||||||
|
// Assign aligned pointers into coeff_mem
|
||||||
|
{
|
||||||
|
resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes);
|
||||||
|
|
||||||
|
for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++)
|
||||||
|
{
|
||||||
|
coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta);
|
||||||
|
ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases);
|
||||||
|
|
||||||
|
// Reorder coefficients to allow for more efficient convolution.
|
||||||
|
for(int phase = -1; phase < ((int)num_phases + 1); phase++)
|
||||||
|
{
|
||||||
|
for(int conv = 0; conv < (int)num_convolutions; conv++)
|
||||||
|
{
|
||||||
|
double coeff;
|
||||||
|
|
||||||
|
if(phase == -1 && conv == 0)
|
||||||
|
coeff = 0;
|
||||||
|
else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1))
|
||||||
|
coeff = 0;
|
||||||
|
else
|
||||||
|
coeff = coeff_init_buffer[conv * num_phases + phase];
|
||||||
|
|
||||||
|
coeffs[phase + 1][conv] = coeff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a bit of mem
|
||||||
|
coeff_init_buffer.resize(0);
|
||||||
|
|
||||||
|
step_int = floor(input_to_output_ratio);
|
||||||
|
step_fract = input_to_output_ratio - step_int;
|
||||||
|
|
||||||
|
input_pos_fract = 0;
|
||||||
|
|
||||||
|
// Do NOT use rb.size() later in the code, since it'll include the padding.
|
||||||
|
// We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time
|
||||||
|
// rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half,
|
||||||
|
// since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to
|
||||||
|
// prevent illegal memory accesses.
|
||||||
|
rb.resize(num_convolutions * 2 + max_mult_minus1);
|
||||||
|
|
||||||
|
rb_readpos = 0;
|
||||||
|
rb_writepos = 0;
|
||||||
|
rb_in = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count)
|
||||||
|
{
|
||||||
|
resample_samp_t accum = 0;
|
||||||
|
#if SINCRESAMPLE_USE_SSE
|
||||||
|
__m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||||
|
__m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c += 8) //8) //4)
|
||||||
|
{
|
||||||
|
__m128 coeff_a[2];
|
||||||
|
__m128 coeff_b[2];
|
||||||
|
__m128 w[2];
|
||||||
|
__m128 result_a[2], result_b[2];
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]);
|
||||||
|
coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]);
|
||||||
|
w[i] = _mm_loadu_ps(&wave[c + (i * 4)]);
|
||||||
|
|
||||||
|
result_a[i] = _mm_mul_ps(coeff_a[i], w[i]);
|
||||||
|
result_b[i] = _mm_mul_ps(coeff_b[i], w[i]);
|
||||||
|
|
||||||
|
accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]);
|
||||||
|
accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 accum_vec, av_a, av_b;
|
||||||
|
__m128 mult_a_vec = _mm_set1_ps(1.0 - ffract);
|
||||||
|
__m128 mult_b_vec = _mm_set1_ps(ffract);
|
||||||
|
|
||||||
|
av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1]));
|
||||||
|
av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1]));
|
||||||
|
|
||||||
|
accum_vec = _mm_add_ps(av_a, av_b);
|
||||||
|
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
|
||||||
|
_mm_store_ss(&accum, accum_vec);
|
||||||
|
#else
|
||||||
|
resample_coeff_t mult_a = 1.0 - ffract;
|
||||||
|
resample_coeff_t mult_b = ffract;
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c += 4)
|
||||||
|
{
|
||||||
|
accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b);
|
||||||
|
accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b);
|
||||||
|
accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b);
|
||||||
|
accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool SincResample::output_avail(void)
|
||||||
|
{
|
||||||
|
return(rb_in >= (int)num_convolutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
resample_samp_t SincResample::read(void)
|
||||||
|
{
|
||||||
|
assert(output_avail());
|
||||||
|
double phase = input_pos_fract * num_phases - 0.5;
|
||||||
|
signed phase_int = (signed)floor(phase);
|
||||||
|
double phase_fract = phase - phase_int;
|
||||||
|
unsigned phase_a = num_phases - 1 - phase_int;
|
||||||
|
unsigned phase_b = phase_a - 1;
|
||||||
|
resample_samp_t ret;
|
||||||
|
|
||||||
|
ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions);
|
||||||
|
|
||||||
|
unsigned int_increment = step_int;
|
||||||
|
|
||||||
|
input_pos_fract += step_fract;
|
||||||
|
int_increment += floor(input_pos_fract);
|
||||||
|
input_pos_fract -= floor(input_pos_fract);
|
||||||
|
|
||||||
|
rb_readpos = (rb_readpos + int_increment) % num_convolutions;
|
||||||
|
rb_in -= int_increment;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SincResample::write(resample_samp_t sample)
|
||||||
|
{
|
||||||
|
assert(!output_avail());
|
||||||
|
|
||||||
|
if(hr_used)
|
||||||
|
{
|
||||||
|
hr.write(sample);
|
||||||
|
|
||||||
|
if(hr.output_avail())
|
||||||
|
{
|
||||||
|
sample = hr.read();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb[rb_writepos + 0 * num_convolutions] = sample;
|
||||||
|
rb[rb_writepos + 1 * num_convolutions] = sample;
|
||||||
|
rb_writepos = (rb_writepos + 1) % num_convolutions;
|
||||||
|
rb_in++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::kaiser_window( double* io, int count, double beta)
|
||||||
|
{
|
||||||
|
int const accuracy = 24; //16; //12;
|
||||||
|
|
||||||
|
double* end = io + count;
|
||||||
|
|
||||||
|
double beta2 = beta * beta * (double) -0.25;
|
||||||
|
double to_fract = beta2 / ((double) count * count);
|
||||||
|
double i = 0;
|
||||||
|
double rescale = 0; // Doesn't need an initializer, to shut up gcc
|
||||||
|
|
||||||
|
for ( ; io < end; ++io, i += 1 )
|
||||||
|
{
|
||||||
|
double x = i * i * to_fract - beta2;
|
||||||
|
double u = x;
|
||||||
|
double k = x + 1;
|
||||||
|
|
||||||
|
double n = 2;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
u *= x / (n * n);
|
||||||
|
n += 1;
|
||||||
|
k += u;
|
||||||
|
}
|
||||||
|
while ( k <= u * (1 << accuracy) );
|
||||||
|
|
||||||
|
if ( !i )
|
||||||
|
rescale = 1 / k; // otherwise values get large
|
||||||
|
|
||||||
|
*io *= k * rescale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser)
|
||||||
|
{
|
||||||
|
assert( size % 2 == 0 ); // size must be even
|
||||||
|
|
||||||
|
int const half_size = size / 2;
|
||||||
|
double* const mid = &out [half_size];
|
||||||
|
|
||||||
|
// Generate right half of sinc
|
||||||
|
for ( int i = 0; i < half_size; i++ )
|
||||||
|
{
|
||||||
|
double angle = (i * 2 + 1) * (M_PI / 2);
|
||||||
|
mid [i] = sin( angle * cutoff ) / angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
kaiser_window( mid, half_size, kaiser );
|
||||||
|
|
||||||
|
// Mirror for left half
|
||||||
|
for ( int i = 0; i < half_size; i++ )
|
||||||
|
out [i] = mid [half_size - 1 - i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser)
|
||||||
|
{
|
||||||
|
assert( size % 2 == 1); // size must be odd
|
||||||
|
|
||||||
|
for(int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
if(i == (size / 2))
|
||||||
|
out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||||
|
else
|
||||||
|
out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||||
|
|
||||||
|
// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1));
|
||||||
|
//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1));
|
||||||
|
|
||||||
|
// printf("%d %f\n", i, out[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
kaiser_window(&out[size / 2], size / 2 + 1, kaiser);
|
||||||
|
|
||||||
|
// Mirror for left half
|
||||||
|
for ( int i = 0; i < size / 2; i++ )
|
||||||
|
out [i] = out [size - 1 - i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::normalize(double* io, int size, double gain)
|
||||||
|
{
|
||||||
|
double sum = 0;
|
||||||
|
for ( int i = 0; i < size; i++ )
|
||||||
|
sum += io [i];
|
||||||
|
|
||||||
|
double scale = gain / sum;
|
||||||
|
for ( int i = 0; i < size; i++ )
|
||||||
|
io [i] *= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ResampleUtility::make_aligned(void* ptr, unsigned boundary)
|
||||||
|
{
|
||||||
|
unsigned char* null_ptr = (unsigned char *)NULL;
|
||||||
|
unsigned char* uc_ptr = (unsigned char *)ptr;
|
||||||
|
|
||||||
|
uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1);
|
||||||
|
|
||||||
|
//while((uc_ptr - null_ptr) & (boundary - 1))
|
||||||
|
// uc_ptr++;
|
||||||
|
|
||||||
|
//printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr);
|
||||||
|
|
||||||
|
assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr));
|
||||||
|
|
||||||
|
return uc_ptr;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleLinear : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleLinear::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleLinear::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleLinear::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
channel[n] = a * (1.0 - mu) + b * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleNearest : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleNearest(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleNearest::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleNearest::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleNearest::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
channel[n] = mu < 0.5 ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
#include "lib/sinc.hpp"
|
||||||
|
|
||||||
|
struct ResampleSinc : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
inline ResampleSinc(DSP &dsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void remakeSinc();
|
||||||
|
SincResample *sinc_resampler[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleSinc::setFrequency() {
|
||||||
|
remakeSinc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleSinc::clear() {
|
||||||
|
remakeSinc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleSinc::sample() {
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
sinc_resampler[c]->write(dsp.buffer.read(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sinc_resampler[0]->output_avail()) {
|
||||||
|
do {
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) = sinc_resampler[c]->read();
|
||||||
|
}
|
||||||
|
dsp.output.wroffset++;
|
||||||
|
} while(sinc_resampler[0]->output_avail());
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) {
|
||||||
|
for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleSinc::remakeSinc() {
|
||||||
|
assert(dsp.settings.channels < 8);
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
if(sinc_resampler[c]) delete sinc_resampler[c];
|
||||||
|
sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
void DSP::setChannels(unsigned channels) {
|
||||||
|
assert(channels > 0);
|
||||||
|
buffer.setChannels(channels);
|
||||||
|
output.setChannels(channels);
|
||||||
|
settings.channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setPrecision(unsigned precision) {
|
||||||
|
settings.precision = precision;
|
||||||
|
settings.intensity = 1 << (settings.precision - 1);
|
||||||
|
settings.intensityInverse = 1.0 / settings.intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setFrequency(real frequency) {
|
||||||
|
settings.frequency = frequency;
|
||||||
|
resampler->setFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setVolume(real volume) {
|
||||||
|
settings.volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setBalance(real balance) {
|
||||||
|
settings.balance = balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setResampler(ResampleEngine engine) {
|
||||||
|
if(resampler) delete resampler;
|
||||||
|
|
||||||
|
switch(engine) {
|
||||||
|
case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return;
|
||||||
|
case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return;
|
||||||
|
case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return;
|
||||||
|
case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return;
|
||||||
|
case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return;
|
||||||
|
case ResampleEngine::Average: resampler = new ResampleAverage(*this); return;
|
||||||
|
case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setResamplerFrequency(real frequency) {
|
||||||
|
resampler->frequency = frequency;
|
||||||
|
resampler->setFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,182 @@
|
||||||
|
#ifndef NALL_EMULATION_FAMICOM_HPP
|
||||||
|
#define NALL_EMULATION_FAMICOM_HPP
|
||||||
|
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct FamicomCartridge {
|
||||||
|
string markup;
|
||||||
|
inline FamicomCartridge(const uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
//private:
|
||||||
|
unsigned mapper;
|
||||||
|
unsigned mirror;
|
||||||
|
unsigned prgrom;
|
||||||
|
unsigned prgram;
|
||||||
|
unsigned chrrom;
|
||||||
|
unsigned chrram;
|
||||||
|
};
|
||||||
|
|
||||||
|
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
|
||||||
|
markup = "";
|
||||||
|
if(size < 16) return;
|
||||||
|
if(data[0] != 'N') return;
|
||||||
|
if(data[1] != 'E') return;
|
||||||
|
if(data[2] != 'S') return;
|
||||||
|
if(data[3] != 26) return;
|
||||||
|
|
||||||
|
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||||
|
|
||||||
|
mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
|
||||||
|
mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
|
||||||
|
prgrom = data[4] * 0x4000;
|
||||||
|
chrrom = data[5] * 0x2000;
|
||||||
|
prgram = 0u;
|
||||||
|
chrram = chrrom == 0u ? 8192u : 0u;
|
||||||
|
|
||||||
|
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
|
||||||
|
|
||||||
|
switch(mapper) {
|
||||||
|
default:
|
||||||
|
markup.append(" <board type='NES-NROM-256'/>\n");
|
||||||
|
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
markup.append(" <board type='NES-SXROM'/>\n");
|
||||||
|
markup.append(" <chip type='MMC1B2'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
markup.append(" <board type='NES-UOROM'/>\n");
|
||||||
|
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
markup.append(" <board type='NES-CNROM'/>\n");
|
||||||
|
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
//MMC3
|
||||||
|
markup.append(" <board type='NES-TLROM'/>\n");
|
||||||
|
markup.append(" <chip type='MMC3B'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
//MMC6
|
||||||
|
//markup.append(" <board type='NES-HKROM'/>\n");
|
||||||
|
//markup.append(" <chip type='MMC6'/>\n");
|
||||||
|
//prgram = 1024;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
markup.append(" <board type='NES-ELROM'/>\n");
|
||||||
|
markup.append(" <chip type='MMC5'/>\n");
|
||||||
|
prgram = 65536;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
markup.append(" <board type='NES-AOROM'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 9:
|
||||||
|
markup.append(" <board type='NES-PNROM'/>\n");
|
||||||
|
markup.append(" <chip type='MMC2'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
markup.append(" <board type='NES-FKROM'/>\n");
|
||||||
|
markup.append(" <chip type='MMC4'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
markup.append(" <board type='BANDAI-FCG'/>\n");
|
||||||
|
markup.append(" <chip type='LZ93D50'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 21:
|
||||||
|
case 23:
|
||||||
|
case 25:
|
||||||
|
//VRC4
|
||||||
|
markup.append(" <board type='KONAMI-VRC-4'/>\n");
|
||||||
|
markup.append(" <chip type='VRC4'>\n");
|
||||||
|
markup.append(" <pinout a0='1' a1='0'/>\n");
|
||||||
|
markup.append(" </chip>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 22:
|
||||||
|
//VRC2
|
||||||
|
markup.append(" <board type='KONAMI-VRC-2'/>\n");
|
||||||
|
markup.append(" <chip type='VRC2'>\n");
|
||||||
|
markup.append(" <pinout a0='0' a1='1'/>\n");
|
||||||
|
markup.append(" </chip>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 24:
|
||||||
|
markup.append(" <board type='KONAMI-VRC-6'/>\n");
|
||||||
|
markup.append(" <chip type='VRC6'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 26:
|
||||||
|
markup.append(" <board type='KONAMI-VRC-6'/>\n");
|
||||||
|
markup.append(" <chip type='VRC6'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 34:
|
||||||
|
markup.append(" <board type='NES-BNROM'/>\n");
|
||||||
|
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 66:
|
||||||
|
markup.append(" <board type='NES-GNROM'/>\n");
|
||||||
|
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 69:
|
||||||
|
markup.append(" <board type='SUNSOFT-5B'/>\n");
|
||||||
|
markup.append(" <chip type='5B'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 73:
|
||||||
|
markup.append(" <board type='KONAMI-VRC-3'/>\n");
|
||||||
|
markup.append(" <chip type='VRC3'/>\n");
|
||||||
|
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 75:
|
||||||
|
markup.append(" <board type='KONAMI-VRC-1'/>\n");
|
||||||
|
markup.append(" <chip type='VRC1'/>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 85:
|
||||||
|
markup.append(" <board type='KONAMI-VRC-7/'>\n");
|
||||||
|
markup.append(" <chip type='VRC7'/>\n");
|
||||||
|
prgram = 8192;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
markup.append(" <prg>\n");
|
||||||
|
if(prgrom) markup.append(" <rom name='program.rom' size='0x", hex(prgrom), "'/>\n");
|
||||||
|
if(prgram) markup.append(" <ram name='save.ram' size='0x", hex(prgram), "'/>\n");
|
||||||
|
markup.append(" </prg>\n");
|
||||||
|
|
||||||
|
markup.append(" <chr>\n");
|
||||||
|
if(chrrom) markup.append(" <rom name='character.rom' size='0x", hex(chrrom), "'/>\n");
|
||||||
|
if(chrram) markup.append(" <ram size='0x", hex(chrram), "'/>\n");
|
||||||
|
markup.append(" </chr>\n");
|
||||||
|
|
||||||
|
markup.append("</cartridge>\n");
|
||||||
|
markup.transform("'", "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,68 @@
|
||||||
|
#ifndef NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||||
|
#define NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||||
|
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct GameBoyAdvanceCartridge {
|
||||||
|
string markup;
|
||||||
|
string identifiers;
|
||||||
|
inline GameBoyAdvanceCartridge(const uint8_t *data, unsigned size);
|
||||||
|
};
|
||||||
|
|
||||||
|
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned size) {
|
||||||
|
struct Identifier {
|
||||||
|
string name;
|
||||||
|
unsigned size;
|
||||||
|
};
|
||||||
|
vector<Identifier> idlist;
|
||||||
|
idlist.append({"SRAM_V", 6});
|
||||||
|
idlist.append({"SRAM_F_V", 8});
|
||||||
|
idlist.append({"EEPROM_V", 8});
|
||||||
|
idlist.append({"FLASH_V", 7});
|
||||||
|
idlist.append({"FLASH512_V", 10});
|
||||||
|
idlist.append({"FLASH1M_V", 9});
|
||||||
|
|
||||||
|
lstring list;
|
||||||
|
for(auto &id : idlist) {
|
||||||
|
for(signed n = 0; n < size - 16; n++) {
|
||||||
|
if(!memcmp(data + n, (const char*)id.name, id.size)) {
|
||||||
|
const char *p = (const char*)data + n + id.size;
|
||||||
|
if(p[0] >= '0' && p[0] <= '9'
|
||||||
|
&& p[1] >= '0' && p[1] <= '9'
|
||||||
|
&& p[2] >= '0' && p[2] <= '9'
|
||||||
|
) {
|
||||||
|
char text[16];
|
||||||
|
memcpy(text, data + n, id.size + 3);
|
||||||
|
text[id.size + 3] = 0;
|
||||||
|
list.appendonce(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
identifiers = list.concatenate(",");
|
||||||
|
|
||||||
|
markup = "";
|
||||||
|
|
||||||
|
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||||
|
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
|
||||||
|
markup.append(" <rom name='program.rom' size='0x", hex(size), "'/>\n");
|
||||||
|
if(0);
|
||||||
|
else if(identifiers.beginswith("SRAM_V" )) markup.append(" <ram name='save.ram' type='SRAM' size='0x8000'/>\n");
|
||||||
|
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" <ram name='save.ram' type='FRAM' size='0x8000'/>\n");
|
||||||
|
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" <ram name='save.ram' type='EEPROM' size='0x0'/>\n");
|
||||||
|
else if(identifiers.beginswith("FLASH_V" )) markup.append(" <ram name='save.ram' type='FlashROM' size='0x10000'/>\n");
|
||||||
|
else if(identifiers.beginswith("FLASH512_V")) markup.append(" <ram name='save.ram' type='FlashROM' size='0x10000'/>\n");
|
||||||
|
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" <ram name='save.ram' type='FlashROM' size='0x20000'/>\n");
|
||||||
|
if(identifiers.empty() == false) markup.append(" <!-- detected: ", identifiers, " -->\n");
|
||||||
|
|
||||||
|
markup.append("</cartridge>\n");
|
||||||
|
markup.transform("'", "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef NALL_EMULATION_GAME_BOY_HPP
|
||||||
|
#define NALL_EMULATION_GAME_BOY_HPP
|
||||||
|
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct GameBoyCartridge {
|
||||||
|
string markup;
|
||||||
|
inline GameBoyCartridge(uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
//private:
|
||||||
|
struct Information {
|
||||||
|
string mapper;
|
||||||
|
bool ram;
|
||||||
|
bool battery;
|
||||||
|
bool rtc;
|
||||||
|
bool rumble;
|
||||||
|
|
||||||
|
unsigned romsize;
|
||||||
|
unsigned ramsize;
|
||||||
|
|
||||||
|
bool cgb;
|
||||||
|
bool cgbonly;
|
||||||
|
} info;
|
||||||
|
};
|
||||||
|
|
||||||
|
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||||
|
markup = "";
|
||||||
|
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;
|
||||||
|
|
||||||
|
unsigned base = romsize - 0x8000;
|
||||||
|
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
|
||||||
|
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
|
||||||
|
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
|
||||||
|
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
|
||||||
|
) {
|
||||||
|
//MMM01 stores header at bottom of image
|
||||||
|
//flip this around for consistency with all other mappers
|
||||||
|
uint8_t header[0x8000];
|
||||||
|
memcpy(header, romdata + base, 0x8000);
|
||||||
|
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
|
||||||
|
memcpy(romdata, header, 0x8000);
|
||||||
|
}
|
||||||
|
|
||||||
|
info.cgb = (romdata[0x0143] & 0x80) == 0x80;
|
||||||
|
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||||
|
markup.append("<cartridge>\n");
|
||||||
|
markup.append(" <board type='", info.mapper, "'/>\n");
|
||||||
|
markup.append(" <rom name='program.rom' size='0x", hex(romsize), "'/>\n");
|
||||||
|
if(info.ramsize > 0) markup.append(" <ram name='save.ram' size='0x", hex(info.ramsize), "'/>\n");
|
||||||
|
markup.append("</cartridge>\n");
|
||||||
|
markup.transform("'", "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef NALL_EMULATION_SATELLAVIEW_HPP
|
||||||
|
#define NALL_EMULATION_SATELLAVIEW_HPP
|
||||||
|
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct SatellaviewCartridge {
|
||||||
|
string markup;
|
||||||
|
inline SatellaviewCartridge(const uint8_t *data, unsigned size);
|
||||||
|
};
|
||||||
|
|
||||||
|
SatellaviewCartridge::SatellaviewCartridge(const uint8_t *data, unsigned size) {
|
||||||
|
markup = "";
|
||||||
|
|
||||||
|
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||||
|
markup.append("<cartridge sha256='", sha256(data, size) ,"'>\n");
|
||||||
|
markup.append(" <rom name='program.rom' size='0x", hex(size), "'/>\n");
|
||||||
|
markup.append("</cartridge>\n");
|
||||||
|
markup.transform("'", "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||||
|
#define NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||||
|
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct SufamiTurboCartridge {
|
||||||
|
string markup;
|
||||||
|
inline SufamiTurboCartridge(const uint8_t *data, unsigned size);
|
||||||
|
};
|
||||||
|
|
||||||
|
SufamiTurboCartridge::SufamiTurboCartridge(const uint8_t *data, unsigned size) {
|
||||||
|
markup = "";
|
||||||
|
|
||||||
|
if(size < 0x20000) return; //too small to be a valid game?
|
||||||
|
if(memcmp(data, "BANDAI SFC-ADX", 14)) return; //missing required header?
|
||||||
|
unsigned romsize = data[0x36] * 0x20000; //128KB
|
||||||
|
unsigned ramsize = data[0x37] * 0x800; //2KB
|
||||||
|
bool linkable = data[0x35] != 0x00; //TODO: unconfirmed
|
||||||
|
|
||||||
|
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||||
|
markup.append("<cartridge linkable='", linkable, "' sha256='", sha256(data, size) ,"'>\n");
|
||||||
|
markup.append(" <rom name='program.rom' size='0x", hex(romsize), "'/>\n");
|
||||||
|
markup.append(" <ram name='save.ram' size='0x", hex(ramsize), "'/>\n");
|
||||||
|
markup.append("</cartridge>\n");
|
||||||
|
markup.transform("'", "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,103 @@
|
||||||
|
#ifndef NALL_EMULATION_SUPER_FAMICOM_USART_HPP
|
||||||
|
#define NALL_EMULATION_SUPER_FAMICOM_USART_HPP
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/function.hpp>
|
||||||
|
#include <nall/serial.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#define usartproc dllexport
|
||||||
|
|
||||||
|
static nall::function<bool ()> usart_quit;
|
||||||
|
static nall::function<void (unsigned milliseconds)> usart_usleep;
|
||||||
|
static nall::function<bool ()> usart_readable;
|
||||||
|
static nall::function<uint8_t ()> usart_read;
|
||||||
|
static nall::function<bool ()> usart_writable;
|
||||||
|
static nall::function<void (uint8_t data)> usart_write;
|
||||||
|
|
||||||
|
extern "C" usartproc void usart_init(
|
||||||
|
nall::function<bool ()> quit,
|
||||||
|
nall::function<void (unsigned milliseconds)> usleep,
|
||||||
|
nall::function<bool ()> readable,
|
||||||
|
nall::function<uint8_t ()> read,
|
||||||
|
nall::function<bool ()> writable,
|
||||||
|
nall::function<void (uint8_t data)> write
|
||||||
|
) {
|
||||||
|
usart_quit = quit;
|
||||||
|
usart_usleep = usleep;
|
||||||
|
usart_readable = readable;
|
||||||
|
usart_read = read;
|
||||||
|
usart_writable = writable;
|
||||||
|
usart_write = write;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" usartproc void usart_main(int, char**);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
static nall::serial usart;
|
||||||
|
static bool usart_is_virtual = true;
|
||||||
|
static bool usart_sigint = false;
|
||||||
|
|
||||||
|
static bool usart_virtual() {
|
||||||
|
return usart_is_virtual;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
static bool usarthw_quit() {
|
||||||
|
return usart_sigint;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usarthw_usleep(unsigned milliseconds) {
|
||||||
|
usleep(milliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool usarthw_readable() {
|
||||||
|
return usart.readable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t usarthw_read() {
|
||||||
|
while(true) {
|
||||||
|
uint8_t buffer[1];
|
||||||
|
signed length = usart.read((uint8_t*)&buffer, 1);
|
||||||
|
if(length > 0) return buffer[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool usarthw_writable() {
|
||||||
|
return usart.writable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usarthw_write(uint8_t data) {
|
||||||
|
uint8_t buffer[1] = { data };
|
||||||
|
usart.write((uint8_t*)&buffer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigint(int) {
|
||||||
|
signal(SIGINT, SIG_DFL);
|
||||||
|
usart_sigint = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
setpriority(PRIO_PROCESS, 0, -20); //requires superuser privileges; otherwise priority = +0
|
||||||
|
signal(SIGINT, sigint);
|
||||||
|
|
||||||
|
if(usart.open("/dev/ttyACM0", 57600, true) == false) {
|
||||||
|
printf("error: unable to open USART hardware device\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
usart_is_virtual = false;
|
||||||
|
usart_init(usarthw_quit, usarthw_usleep, usarthw_readable, usarthw_read, usarthw_writable, usarthw_write);
|
||||||
|
usart_main(argc, argv);
|
||||||
|
usart.close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,803 @@
|
||||||
|
#ifndef NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||||
|
#define NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||||
|
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct SuperFamicomCartridge {
|
||||||
|
string markup;
|
||||||
|
inline SuperFamicomCartridge(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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
|
||||||
|
//skip copier header
|
||||||
|
if((size & 0x7fff) == 512) data += 512, size -= 512;
|
||||||
|
|
||||||
|
markup = "";
|
||||||
|
if(size < 0x8000) return;
|
||||||
|
|
||||||
|
read_header(data, size);
|
||||||
|
|
||||||
|
markup = "";
|
||||||
|
if(type == TypeGameBoy) return;
|
||||||
|
if(type == TypeBsx) return;
|
||||||
|
if(type == TypeSufamiTurbo) return;
|
||||||
|
|
||||||
|
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||||
|
|
||||||
|
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
|
||||||
|
markup.append("<cartridge region='", region == NTSC ? "NTSC" : "PAL", "'>\n");
|
||||||
|
|
||||||
|
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-7f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='80-ff:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <icd2 revision='1'>\n"
|
||||||
|
" <firmware name='boot.rom' size='256' sha256='0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360'/>\n"
|
||||||
|
" <map address='00-3f:6000-7fff' id='io'/>\n"
|
||||||
|
" <map address='80-bf:6000-7fff' id='io'/>\n"
|
||||||
|
" </icd2>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
else if(has_cx4) markup.append(
|
||||||
|
" <hitachidsp model='HG51B169' frequency='20000000'>\n"
|
||||||
|
" <firmware name='cx4.rom' size='3072' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'/>\n"
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-3f:6000-7fff' id='io'/>\n"
|
||||||
|
" <map address='80-bf:6000-7fff' id='io'/>\n"
|
||||||
|
" <map address='00-7f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='80-ff:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" </hitachidsp>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
else if(has_spc7110) markup.append(
|
||||||
|
" <spc7110>\n"
|
||||||
|
" <prom name='program.rom' size='0x100000'/>\n"
|
||||||
|
" <drom name='data.rom' size='0x", hex(rom_size - 0x100000), "'/>\n"
|
||||||
|
" <ram name='save.rwm' size='0x2000'/>\n"
|
||||||
|
" <map address='00-3f:4800-483f' id='io'/>\n"
|
||||||
|
" <map address='80-bf:4800-483f' id='io'/>\n"
|
||||||
|
" <map address='50:0000-ffff' id='io'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='00-3f:6000-7fff' id='ram'/>\n"
|
||||||
|
" <map address='80-bf:6000-7fff' id='ram'/>\n"
|
||||||
|
" </spc7110>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
else if(has_sdd1) markup.append(
|
||||||
|
" <sdd1>\n"
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='00-3f:4800-4807' id='io'/>\n"
|
||||||
|
" <map address='80-bf:4800-4807' id='io'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='40-7f:0000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='20-3f:6000-7fff' id='ram'/>\n"
|
||||||
|
" <map address='a0-bf:6000-7fff' id='ram'/>\n"
|
||||||
|
" <map address='70-7f:0000-7fff' id='ram'/>\n"
|
||||||
|
" </sdd1>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
else if(mapper == LoROM) {
|
||||||
|
markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-7f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='80-ff:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
if(ram_size > 0) markup.append(
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='70-7f:", range, "' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='f0-ff:", range, "' id='ram' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(mapper == HiROM) {
|
||||||
|
markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom' mode='shadow'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom' mode='shadow'/>\n"
|
||||||
|
" <map address='40-7f:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='c0-ff:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
if(ram_size > 0) markup.append(
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='70-7f:", range, "' id='ram' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(mapper == ExLoROM) {
|
||||||
|
markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='40-7f:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
if(ram_size > 0) markup.append(
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='70-7f:0000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(mapper == ExHiROM) {
|
||||||
|
markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom' mode='shadow' offset='0x400000'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom' mode='shadow' offset='0x000000'/>\n"
|
||||||
|
" <map address='40-7f:0000-ffff' id='rom' mode='linear' offset='0x400000'/>\n"
|
||||||
|
" <map address='c0-ff:0000-ffff' id='rom' mode='linear' offset='0x000000'/>\n"
|
||||||
|
);
|
||||||
|
if(ram_size > 0) markup.append(
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='70-7f:", range, "' id='ram' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(mapper == SuperFXROM) {
|
||||||
|
markup.append(
|
||||||
|
" <superfx revision='2'>\n"
|
||||||
|
" <map address='00-3f:3000-32ff' id='io'/>\n"
|
||||||
|
" <map address='80-bf:3000-32ff' id='io'/>\n"
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='40-5f:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='c0-df:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
if(ram_size > 0) markup.append(
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='00-3f:6000-7fff' id='ram' mode='linear' size='0x2000'/>\n"
|
||||||
|
" <map address='80-bf:6000-7fff' id='ram' mode='linear' size='0x2000'/>\n"
|
||||||
|
" <map address='60-7f:0000-ffff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='e0-ff:0000-ffff' id='ram' mode='linear'/>\n"
|
||||||
|
);
|
||||||
|
markup.append(
|
||||||
|
" </superfx>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(mapper == SA1ROM) {
|
||||||
|
markup.append(
|
||||||
|
" <sa1>\n"
|
||||||
|
" <map address='00-3f:2200-23ff' id='io'/>\n"
|
||||||
|
" <map address='80-bf:2200-23ff' id='io'/>\n"
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
|
||||||
|
" <iram size='0x800'/>\n"
|
||||||
|
" <map address='00-3f:3000-37ff' id='iram'/>\n"
|
||||||
|
" <map address='80-bf:3000-37ff' id='iram'/>\n"
|
||||||
|
);
|
||||||
|
if(ram_size > 0) markup.append(
|
||||||
|
" <bwram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='00-3f:6000-7fff' id='bwram'/>\n"
|
||||||
|
" <map address='80-bf:6000-7fff' id='bwram'/>\n"
|
||||||
|
" <map address='40-4f:0000-ffff' id='bwram'/>\n"
|
||||||
|
);
|
||||||
|
markup.append(
|
||||||
|
" </sa1>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(mapper == BSCLoROM) markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='00-1f:8000-ffff' id='rom' mode='linear' offset='0x000000'/>\n"
|
||||||
|
" <map address='20-3f:8000-ffff' id='rom' mode='linear' offset='0x100000'/>\n"
|
||||||
|
" <map address='80-9f:8000-ffff' id='rom' mode='linear' offset='0x200000'/>\n"
|
||||||
|
" <map address='a0-bf:8000-ffff' id='rom' mode='linear' offset='0x100000'/>\n"
|
||||||
|
" <map address='70-7f:0000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='f0-ff:0000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <bsxslot>\n"
|
||||||
|
" <map address='c0-ef:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" </bsxslot>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
else if(mapper == BSCHiROM) markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <map address='00-1f:8000-ffff' id='rom' mode='shadow'/>\n"
|
||||||
|
" <map address='80-9f:8000-ffff' id='rom' mode='shadow'/>\n"
|
||||||
|
" <map address='40-5f:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='c0-df:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
|
||||||
|
" <bsxslot>\n"
|
||||||
|
" <map address='20-3f:8000-ffff' id='rom' mode='shadow'/>\n"
|
||||||
|
" <map address='a0-bf:8000-ffff' id='rom' mode='shadow'/>\n"
|
||||||
|
" <map address='60-7f:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='e0-ff:0000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" </bsxslot>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
else if(mapper == BSXROM) markup.append(
|
||||||
|
" <bsx>\n"
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
|
||||||
|
" <psram name='bsx.rwm' size='0x40000'/>\n"
|
||||||
|
" <map address='00-3f:5000-5fff' id='io'/>\n"
|
||||||
|
" <map address='80-bf:5000-5fff' id='io'/>\n"
|
||||||
|
" <map address='20-3f:6000-7fff' id='ram'/>\n"
|
||||||
|
" <map address='00-3f:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='80-bf:8000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='40-7f:0000-ffff' id='rom'/>\n"
|
||||||
|
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
|
||||||
|
" </bsx>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
else if(mapper == STROM) markup.append(
|
||||||
|
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
|
||||||
|
" <map address='00-1f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='80-9f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <sufamiturbo>\n"
|
||||||
|
" <slot id='A'>\n"
|
||||||
|
" <map address='20-3f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='a0-bf:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='60-63:8000-ffff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='e0-e3:8000-ffff' id='ram' mode='linear'/>\n"
|
||||||
|
" </slot>\n"
|
||||||
|
" <slot id='B'>\n"
|
||||||
|
" <map address='40-5f:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='c0-df:8000-ffff' id='rom' mode='linear'/>\n"
|
||||||
|
" <map address='70-73:8000-ffff' id='ram' mode='linear'/>\n"
|
||||||
|
" <map address='f0-f3:8000-ffff' id='ram' mode='linear'/>\n"
|
||||||
|
" </slot>\n"
|
||||||
|
" </sufamiturbo>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_spc7110rtc) markup.append(
|
||||||
|
" <epsonrtc>\n"
|
||||||
|
" <ram name='rtc.rwm' size='0x10'/>\n"
|
||||||
|
" <map address='00-3f:4840-4842' id='io'/>\n"
|
||||||
|
" <map address='80-bf:4840-4842' id='io'/>\n"
|
||||||
|
" </epsonrtc>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_srtc) markup.append(
|
||||||
|
" <sharprtc>\n"
|
||||||
|
" <ram name='rtc.rwm' size='0x10'/>\n"
|
||||||
|
" <map address='00-3f:2800-2801' id='io'/>\n"
|
||||||
|
" <map address='80-bf:2800-2801' id='io'/>\n"
|
||||||
|
" </sharprtc>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_obc1) markup.append(
|
||||||
|
" <obc1>\n"
|
||||||
|
" <ram name='save.rwm' size='0x2000'/>\n"
|
||||||
|
" <map address='00-3f:6000-7fff' id='io'/>\n"
|
||||||
|
" <map address='80-bf:6000-7fff' id='io'/>\n"
|
||||||
|
" </obc1>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_dsp1) {
|
||||||
|
//91e87d11e1c30d172556bed2211cce2efa94ba595f58c5d264809ef4d363a97b dsp1.rom
|
||||||
|
markup.append(
|
||||||
|
" <necdsp model='uPD7725' frequency='8000000'>\n"
|
||||||
|
" <firmware name='dsp1b.rom' size='8192' sha256='d789cb3c36b05c0b23b6c6f23be7aa37c6e78b6ee9ceac8d2d2aa9d8c4d35fa9'/>\n"
|
||||||
|
);
|
||||||
|
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
|
||||||
|
" <map address='20-3f:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='a0-bf:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='20-3f:c000-ffff' id='sr'/>\n"
|
||||||
|
" <map address='a0-bf:c000-ffff' id='sr'/>\n"
|
||||||
|
);
|
||||||
|
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
|
||||||
|
" <map address='60-6f:0000-3fff' id='dr'/>\n"
|
||||||
|
" <map address='e0-ef:0000-3fff' id='dr'/>\n"
|
||||||
|
" <map address='60-6f:4000-7fff' id='sr'/>\n"
|
||||||
|
" <map address='e0-ef:4000-7fff' id='sr'/>\n"
|
||||||
|
);
|
||||||
|
if(dsp1_mapper == DSP1HiROM) markup.append(
|
||||||
|
" <map address='00-1f:6000-6fff' id='dr'/>\n"
|
||||||
|
" <map address='80-9f:6000-6fff' id='dr'/>\n"
|
||||||
|
" <map address='00-1f:7000-7fff' id='sr'/>\n"
|
||||||
|
" <map address='80-9f:7000-7fff' id='sr'/>\n"
|
||||||
|
);
|
||||||
|
markup.append(
|
||||||
|
" </necdsp>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(has_dsp2) markup.append(
|
||||||
|
" <necdsp model='uPD7725' frequency='8000000'>\n"
|
||||||
|
" <firmware name='dsp2.rom' size='8192' sha256='03ef4ef26c9f701346708cb5d07847b5203cf1b0818bf2930acd34510ffdd717'/>\n"
|
||||||
|
" <map address='20-3f:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='a0-bf:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='20-3f:c000-ffff' id='sr'/>\n"
|
||||||
|
" <map address='a0-bf:c000-ffff' id='sr'/>\n"
|
||||||
|
" </necdsp>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_dsp3) markup.append(
|
||||||
|
" <necdsp model='uPD7725' frequency='8000000'>\n"
|
||||||
|
" <firmware name='dsp3.rom' size='8192' sha256='0971b08f396c32e61989d1067dddf8e4b14649d548b2188f7c541b03d7c69e4e'/>\n"
|
||||||
|
" <map address='20-3f:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='a0-bf:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='20-3f:c000-ffff' id='sr'/>\n"
|
||||||
|
" <map address='a0-bf:c000-ffff' id='sr'/>\n"
|
||||||
|
" </necdsp>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_dsp4) markup.append(
|
||||||
|
" <necdsp model='uPD7725' frequency='8000000'>\n"
|
||||||
|
" <firmware name='dsp4.rom' size='8192' sha256='752d03b2d74441e430b7f713001fa241f8bbcfc1a0d890ed4143f174dbe031da'/>\n"
|
||||||
|
" <map address='30-3f:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='b0-bf:8000-bfff' id='dr'/>\n"
|
||||||
|
" <map address='30-3f:c000-ffff' id='sr'/>\n"
|
||||||
|
" <map address='b0-bf:c000-ffff' id='sr'/>\n"
|
||||||
|
" </necdsp>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_st010) markup.append(
|
||||||
|
" <necdsp model='uPD96050' frequency='10000000'>\n"
|
||||||
|
" <firmware name='st010.rom' size='53248' sha256='fa9bced838fedea11c6f6ace33d1878024bdd0d02cc9485899d0bdd4015ec24c'/>\n"
|
||||||
|
" <ram name='save.rwm' size='0x1000'/>\n"
|
||||||
|
" <map address='60:0000' id='dr'/>\n"
|
||||||
|
" <map address='e0:0000' id='dr'/>\n"
|
||||||
|
" <map address='60:0001' id='sr'/>\n"
|
||||||
|
" <map address='e0:0001' id='sr'/>\n"
|
||||||
|
" <map address='68-6f:0000-0fff' id='ram'/>\n"
|
||||||
|
" <map address='e8-ef:0000-0fff' id='ram'/>\n"
|
||||||
|
" </necdsp>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_st011) markup.append(
|
||||||
|
" <necdsp model='uPD96050' frequency='15000000'>\n"
|
||||||
|
" <firmware name='st011.rom' size='53248' sha256='8b2b3f3f3e6e29f4d21d8bc736b400bc988b7d2214ebee15643f01c1fee2f364'/>\n"
|
||||||
|
" <ram name='save.rwm' size='0x1000'/>\n"
|
||||||
|
" <map address='60:0000' id='dr'/>\n"
|
||||||
|
" <map address='e0:0000' id='dr'/>\n"
|
||||||
|
" <map address='60:0001' id='sr'/>\n"
|
||||||
|
" <map address='e0:0001' id='sr'/>\n"
|
||||||
|
" <map address='68-6f:0000-0fff' id='ram'/>\n"
|
||||||
|
" <map address='e8-ef:0000-0fff' id='ram'/>\n"
|
||||||
|
" </necdsp>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(has_st018) markup.append(
|
||||||
|
" <armdsp frequency='21477272'>\n"
|
||||||
|
" <firmware name='st018.rom' size='163840' sha256='6df209ab5d2524d1839c038be400ae5eb20dafc14a3771a3239cd9e8acd53806'/>\n"
|
||||||
|
" <map address='00-3f:3800-38ff' id='io'/>\n"
|
||||||
|
" <map address='80-bf:3800-38ff' id='io'/>\n"
|
||||||
|
" </armdsp>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
markup.append("</cartridge>\n");
|
||||||
|
markup.transform("'", "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuperFamicomCartridge::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 SuperFamicomCartridge::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 SuperFamicomCartridge::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef NALL_ENDIAN_HPP
|
||||||
|
#define NALL_ENDIAN_HPP
|
||||||
|
|
||||||
|
#include <nall/intrinsics.hpp>
|
||||||
|
|
||||||
|
#if defined(ENDIAN_LSB)
|
||||||
|
//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
|
||||||
|
#elif defined(ENDIAN_MSB)
|
||||||
|
//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
|
||||||
|
#else
|
||||||
|
#error "Unknown endian. Please specify in nall/intrinsics.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,335 @@
|
||||||
|
#ifndef NALL_FILE_HPP
|
||||||
|
#define NALL_FILE_HPP
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/utility.hpp>
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
#include <nall/stream/memory.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
return fopen(utf8_filename, mode);
|
||||||
|
#else
|
||||||
|
return _wfopen(utf16_t(utf8_filename), utf16_t(mode));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file {
|
||||||
|
enum class mode : unsigned { read, write, modify, append, readwrite = modify, writeread = append };
|
||||||
|
enum class index : unsigned { absolute, relative };
|
||||||
|
enum class time : unsigned { create, modify, access };
|
||||||
|
|
||||||
|
static bool copy(const string &sourcename, const string &targetname) {
|
||||||
|
file rd, wr;
|
||||||
|
if(rd.open(sourcename, mode::read) == false) return false;
|
||||||
|
if(wr.open(targetname, mode::write) == false) return false;
|
||||||
|
for(unsigned n = 0; n < rd.size(); n++) wr.write(rd.read());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool move(const string &sourcename, const string &targetname) {
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
return rename(sourcename, targetname) == 0;
|
||||||
|
#else
|
||||||
|
return _wrename(utf16_t(sourcename), utf16_t(targetname)) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool remove(const string &filename) {
|
||||||
|
return unlink(filename) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool truncate(const string &filename, unsigned size) {
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
return truncate(filename, size) == 0;
|
||||||
|
#else
|
||||||
|
bool result = false;
|
||||||
|
FILE *fp = fopen(filename, "rb+");
|
||||||
|
if(fp) {
|
||||||
|
result = _chsize(fileno(fp), size) == 0;
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static vector<uint8_t> read(const string &filename) {
|
||||||
|
vector<uint8_t> memory;
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, mode::read)) {
|
||||||
|
memory.resize(fp.size());
|
||||||
|
fp.read(memory.data(), memory.size());
|
||||||
|
}
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read(const string &filename, uint8_t *data, unsigned size) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, mode::read) == false) return false;
|
||||||
|
fp.read(data, size);
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write(const string &filename, const vector<uint8_t> &buffer) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, mode::write) == false) return false;
|
||||||
|
fp.write(buffer.data(), buffer.size());
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write(const string &filename, const uint8_t *data, unsigned size) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, mode::write) == false) return false;
|
||||||
|
fp.write(data, size);
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string sha256(const string &filename) {
|
||||||
|
auto buffer = read(filename);
|
||||||
|
return nall::sha256(buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned offset() const {
|
||||||
|
if(!fp) return 0; //file not open
|
||||||
|
return file_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned size() const {
|
||||||
|
if(!fp) return 0; //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 string &filename) {
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
struct stat64 data;
|
||||||
|
return stat64(filename, &data) == 0;
|
||||||
|
#else
|
||||||
|
struct __stat64 data;
|
||||||
|
return _wstat64(utf16_t(filename), &data) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static uintmax_t size(const string &filename) {
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
struct stat64 data;
|
||||||
|
stat64(filename, &data);
|
||||||
|
#else
|
||||||
|
struct __stat64 data;
|
||||||
|
_wstat64(utf16_t(filename), &data);
|
||||||
|
#endif
|
||||||
|
return S_ISREG(data.st_mode) ? data.st_size : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static time_t timestamp(const string &filename, file::time mode = file::time::create) {
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
struct stat64 data;
|
||||||
|
stat64(filename, &data);
|
||||||
|
#else
|
||||||
|
struct __stat64 data;
|
||||||
|
_wstat64(utf16_t(filename), &data);
|
||||||
|
#endif
|
||||||
|
switch(mode) { default:
|
||||||
|
case file::time::create: return data.st_ctime;
|
||||||
|
case file::time::modify: return data.st_mtime;
|
||||||
|
case file::time::access: return data.st_atime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool open() const {
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool open(const string &filename, mode mode_) {
|
||||||
|
if(fp) return false;
|
||||||
|
|
||||||
|
switch(file_mode = mode_) {
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
case mode::read: fp = fopen(filename, "rb" ); break;
|
||||||
|
case mode::write: fp = fopen(filename, "wb+"); break; //need read permission for buffering
|
||||||
|
case mode::readwrite: fp = fopen(filename, "rb+"); break;
|
||||||
|
case mode::writeread: fp = fopen(filename, "wb+"); break;
|
||||||
|
#else
|
||||||
|
case mode::read: fp = _wfopen(utf16_t(filename), L"rb" ); break;
|
||||||
|
case mode::write: fp = _wfopen(utf16_t(filename), L"wb+"); break;
|
||||||
|
case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break;
|
||||||
|
case mode::writeread: fp = _wfopen(utf16_t(filename), 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; //invalidate buffer
|
||||||
|
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,213 @@
|
||||||
|
#ifndef NALL_FILEMAP_HPP
|
||||||
|
#define NALL_FILEMAP_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/windows/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 open() const { return p_open(); }
|
||||||
|
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_open() const {
|
||||||
|
return p_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool p_open(const char *filename, mode mode_) {
|
||||||
|
if(file::exists(filename) && file::size(filename) == 0) {
|
||||||
|
p_handle = 0;
|
||||||
|
p_size = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_open() const {
|
||||||
|
return p_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool p_open(const char *filename, mode mode_) {
|
||||||
|
if(file::exists(filename) && file::size(filename) == 0) {
|
||||||
|
p_handle = 0;
|
||||||
|
p_size = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,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 = nullptr; } }
|
||||||
|
|
||||||
|
function& operator=(const function &source) {
|
||||||
|
if(this != &source) {
|
||||||
|
if(callback) { delete callback; callback = nullptr; }
|
||||||
|
if(source.callback) callback = source.callback->copy();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function(const function &source) : callback(nullptr) { operator=(source); }
|
||||||
|
function() : callback(nullptr) {}
|
||||||
|
function(void *function) : callback(nullptr) { 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,85 @@
|
||||||
|
#ifndef NALL_GZIP_HPP
|
||||||
|
#define NALL_GZIP_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/inflate.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct gzip {
|
||||||
|
string filename;
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
|
||||||
|
inline bool decompress(const string &filename);
|
||||||
|
inline bool decompress(const uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
inline gzip();
|
||||||
|
inline ~gzip();
|
||||||
|
};
|
||||||
|
|
||||||
|
bool gzip::decompress(const string &filename) {
|
||||||
|
if(auto memory = file::read(filename)) {
|
||||||
|
return decompress(memory.data(), memory.size());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gzip::decompress(const uint8_t *data, unsigned size) {
|
||||||
|
if(size < 18) return false;
|
||||||
|
if(data[0] != 0x1f) return false;
|
||||||
|
if(data[1] != 0x8b) return false;
|
||||||
|
unsigned cm = data[2];
|
||||||
|
unsigned flg = data[3];
|
||||||
|
unsigned mtime = data[4];
|
||||||
|
mtime |= data[5] << 8;
|
||||||
|
mtime |= data[6] << 16;
|
||||||
|
mtime |= data[7] << 24;
|
||||||
|
unsigned xfl = data[8];
|
||||||
|
unsigned os = data[9];
|
||||||
|
unsigned p = 10;
|
||||||
|
unsigned isize = data[size - 4];
|
||||||
|
isize |= data[size - 3] << 8;
|
||||||
|
isize |= data[size - 2] << 16;
|
||||||
|
isize |= data[size - 1] << 24;
|
||||||
|
filename = "";
|
||||||
|
|
||||||
|
if(flg & 0x04) { //FEXTRA
|
||||||
|
unsigned xlen = data[p + 0];
|
||||||
|
xlen |= data[p + 1] << 8;
|
||||||
|
p += 2 + xlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flg & 0x08) { //FNAME
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
for(unsigned n = 0; n < PATH_MAX; n++, p++) {
|
||||||
|
buffer[n] = data[p];
|
||||||
|
if(data[p] == 0) break;
|
||||||
|
}
|
||||||
|
if(data[p++]) return false;
|
||||||
|
filename = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flg & 0x10) { //FCOMMENT
|
||||||
|
while(data[p++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flg & 0x02) { //FHCRC
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->size = isize;
|
||||||
|
this->data = new uint8_t[this->size];
|
||||||
|
return inflate(this->data, this->size, data + p, size - p - 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip::gzip() : data(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip::~gzip() {
|
||||||
|
if(data) delete[] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,176 @@
|
||||||
|
#ifndef NALL_HTTP_HPP
|
||||||
|
#define NALL_HTTP_HPP
|
||||||
|
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#else
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct http {
|
||||||
|
string hostname;
|
||||||
|
addrinfo *serverinfo;
|
||||||
|
int serversocket;
|
||||||
|
string header;
|
||||||
|
|
||||||
|
inline void download(const string &path, uint8_t *&data, unsigned &size) {
|
||||||
|
data = 0;
|
||||||
|
size = 0;
|
||||||
|
|
||||||
|
send({
|
||||||
|
"GET ", path, " HTTP/1.1\r\n"
|
||||||
|
"Host: ", hostname, "\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n"
|
||||||
|
});
|
||||||
|
|
||||||
|
header = downloadHeader();
|
||||||
|
downloadContent(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool connect(string host, unsigned port) {
|
||||||
|
hostname = host;
|
||||||
|
|
||||||
|
addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(addrinfo));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
|
||||||
|
int status = getaddrinfo(hostname, string(port), &hints, &serverinfo);
|
||||||
|
if(status != 0) return false;
|
||||||
|
|
||||||
|
serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol);
|
||||||
|
if(serversocket == -1) return false;
|
||||||
|
|
||||||
|
int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen);
|
||||||
|
if(result == -1) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool send(const string &data) {
|
||||||
|
return send((const uint8_t*)(const char*)data, data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool send(const uint8_t *data, unsigned size) {
|
||||||
|
while(size) {
|
||||||
|
int length = ::send(serversocket, (const char*)data, size, 0);
|
||||||
|
if(length == -1) return false;
|
||||||
|
data += length;
|
||||||
|
size -= length;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline string downloadHeader() {
|
||||||
|
string output;
|
||||||
|
do {
|
||||||
|
char buffer[2];
|
||||||
|
int length = recv(serversocket, buffer, 1, 0);
|
||||||
|
if(length <= 0) return output;
|
||||||
|
buffer[1] = 0;
|
||||||
|
output.append(buffer);
|
||||||
|
} while(output.endswith("\r\n\r\n") == false);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline string downloadChunkLength() {
|
||||||
|
string output;
|
||||||
|
do {
|
||||||
|
char buffer[2];
|
||||||
|
int length = recv(serversocket, buffer, 1, 0);
|
||||||
|
if(length <= 0) return output;
|
||||||
|
buffer[1] = 0;
|
||||||
|
output.append(buffer);
|
||||||
|
} while(output.endswith("\r\n") == false);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void downloadContent(uint8_t *&data, unsigned &size) {
|
||||||
|
unsigned capacity = 0;
|
||||||
|
|
||||||
|
if(header.iposition("\r\nTransfer-Encoding: chunked\r\n")) {
|
||||||
|
while(true) {
|
||||||
|
unsigned length = hex(downloadChunkLength());
|
||||||
|
if(length == 0) break;
|
||||||
|
capacity += length;
|
||||||
|
data = (uint8_t*)realloc(data, capacity);
|
||||||
|
|
||||||
|
char buffer[length];
|
||||||
|
while(length) {
|
||||||
|
int packetlength = recv(serversocket, buffer, length, 0);
|
||||||
|
if(packetlength <= 0) break;
|
||||||
|
memcpy(data + size, buffer, packetlength);
|
||||||
|
size += packetlength;
|
||||||
|
length -= packetlength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(auto position = header.iposition("\r\nContent-Length: ")) {
|
||||||
|
unsigned length = decimal((const char*)header + position() + 18);
|
||||||
|
while(length) {
|
||||||
|
char buffer[256];
|
||||||
|
int packetlength = recv(serversocket, buffer, min(256, length), 0);
|
||||||
|
if(packetlength <= 0) break;
|
||||||
|
capacity += packetlength;
|
||||||
|
data = (uint8_t*)realloc(data, capacity);
|
||||||
|
memcpy(data + size, buffer, packetlength);
|
||||||
|
size += packetlength;
|
||||||
|
length -= packetlength;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while(true) {
|
||||||
|
char buffer[256];
|
||||||
|
int packetlength = recv(serversocket, buffer, 256, 0);
|
||||||
|
if(packetlength <= 0) break;
|
||||||
|
capacity += packetlength;
|
||||||
|
data = (uint8_t*)realloc(data, capacity);
|
||||||
|
memcpy(data + size, buffer, packetlength);
|
||||||
|
size += packetlength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = (uint8_t*)realloc(data, capacity + 1);
|
||||||
|
data[capacity] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void disconnect() {
|
||||||
|
close(serversocket);
|
||||||
|
freeaddrinfo(serverinfo);
|
||||||
|
serverinfo = 0;
|
||||||
|
serversocket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
inline int close(int sock) {
|
||||||
|
return closesocket(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline http() {
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
|
||||||
|
WSADATA wsaData;
|
||||||
|
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,539 @@
|
||||||
|
#ifndef NALL_IMAGE_HPP
|
||||||
|
#define NALL_IMAGE_HPP
|
||||||
|
|
||||||
|
#include <nall/bmp.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/interpolation.hpp>
|
||||||
|
#include <nall/png.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct image {
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned width;
|
||||||
|
unsigned height;
|
||||||
|
unsigned pitch;
|
||||||
|
|
||||||
|
bool endian; //0 = little, 1 = big
|
||||||
|
unsigned depth;
|
||||||
|
unsigned stride;
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
uint64_t mask;
|
||||||
|
unsigned depth;
|
||||||
|
unsigned shift;
|
||||||
|
|
||||||
|
inline bool operator==(const Channel &source) {
|
||||||
|
return mask == source.mask && depth == source.depth && shift == source.shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(const Channel &source) {
|
||||||
|
return !operator==(source);
|
||||||
|
}
|
||||||
|
} alpha, red, green, blue;
|
||||||
|
|
||||||
|
typedef double (*interpolation)(double, double, double, double, double);
|
||||||
|
static inline unsigned bitDepth(uint64_t color);
|
||||||
|
static inline unsigned bitShift(uint64_t color);
|
||||||
|
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
|
||||||
|
|
||||||
|
inline bool operator==(const image &source);
|
||||||
|
inline bool operator!=(const image &source);
|
||||||
|
|
||||||
|
inline image& operator=(const image &source);
|
||||||
|
inline image& operator=(image &&source);
|
||||||
|
inline image(const image &source);
|
||||||
|
inline image(image &&source);
|
||||||
|
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||||
|
inline image(const string &filename);
|
||||||
|
inline image(const uint8_t *data, unsigned size);
|
||||||
|
inline image();
|
||||||
|
inline ~image();
|
||||||
|
|
||||||
|
inline uint64_t read(const uint8_t *data) const;
|
||||||
|
inline void write(uint8_t *data, uint64_t value) const;
|
||||||
|
|
||||||
|
inline void free();
|
||||||
|
inline bool empty() const;
|
||||||
|
inline void allocate(unsigned width, unsigned height);
|
||||||
|
inline void clear(uint64_t color);
|
||||||
|
inline bool load(const string &filename);
|
||||||
|
//inline bool loadBMP(const uint8_t *data, unsigned size);
|
||||||
|
inline bool loadPNG(const uint8_t *data, unsigned size);
|
||||||
|
inline void scale(unsigned width, unsigned height, interpolation op);
|
||||||
|
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||||
|
inline void alphaBlend(uint64_t alphaColor);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline uint64_t interpolate(double mu, const uint64_t *s, interpolation op);
|
||||||
|
inline void scaleX(unsigned width, interpolation op);
|
||||||
|
inline void scaleY(unsigned height, interpolation op);
|
||||||
|
inline bool loadBMP(const string &filename);
|
||||||
|
inline bool loadPNG(const string &filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
//static
|
||||||
|
|
||||||
|
unsigned image::bitDepth(uint64_t color) {
|
||||||
|
unsigned depth = 0;
|
||||||
|
if(color) while((color & 1) == 0) color >>= 1;
|
||||||
|
while((color & 1) == 1) { color >>= 1; depth++; }
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned image::bitShift(uint64_t color) {
|
||||||
|
unsigned shift = 0;
|
||||||
|
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
|
||||||
|
return shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
|
||||||
|
while(sourceDepth < targetDepth) {
|
||||||
|
color = (color << sourceDepth) | color;
|
||||||
|
sourceDepth += sourceDepth;
|
||||||
|
}
|
||||||
|
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public
|
||||||
|
|
||||||
|
bool image::operator==(const image &source) {
|
||||||
|
if(width != source.width) return false;
|
||||||
|
if(height != source.height) return false;
|
||||||
|
if(pitch != source.pitch) return false;
|
||||||
|
|
||||||
|
if(endian != source.endian) return false;
|
||||||
|
if(stride != source.stride) return false;
|
||||||
|
|
||||||
|
if(alpha != source.alpha) return false;
|
||||||
|
if(red != source.red) return false;
|
||||||
|
if(green != source.green) return false;
|
||||||
|
if(blue != source.blue) return false;
|
||||||
|
|
||||||
|
return memcmp(data, source.data, width * height * stride) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image::operator!=(const image &source) {
|
||||||
|
return !operator==(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
image& image::operator=(const image &source) {
|
||||||
|
free();
|
||||||
|
|
||||||
|
width = source.width;
|
||||||
|
height = source.height;
|
||||||
|
pitch = source.pitch;
|
||||||
|
|
||||||
|
endian = source.endian;
|
||||||
|
stride = source.stride;
|
||||||
|
|
||||||
|
alpha = source.alpha;
|
||||||
|
red = source.red;
|
||||||
|
green = source.green;
|
||||||
|
blue = source.blue;
|
||||||
|
|
||||||
|
data = new uint8_t[width * height * stride];
|
||||||
|
memcpy(data, source.data, width * height * stride);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
image& image::operator=(image &&source) {
|
||||||
|
free();
|
||||||
|
|
||||||
|
width = source.width;
|
||||||
|
height = source.height;
|
||||||
|
pitch = source.pitch;
|
||||||
|
|
||||||
|
endian = source.endian;
|
||||||
|
stride = source.stride;
|
||||||
|
|
||||||
|
alpha = source.alpha;
|
||||||
|
red = source.red;
|
||||||
|
green = source.green;
|
||||||
|
blue = source.blue;
|
||||||
|
|
||||||
|
data = source.data;
|
||||||
|
source.data = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
image::image(const image &source) : data(nullptr) {
|
||||||
|
operator=(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
image::image(image &&source) : data(nullptr) {
|
||||||
|
operator=(std::forward<image>(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) : data(nullptr) {
|
||||||
|
width = 0, height = 0, pitch = 0;
|
||||||
|
|
||||||
|
this->endian = endian;
|
||||||
|
this->depth = depth;
|
||||||
|
this->stride = (depth / 8) + ((depth & 7) > 0);
|
||||||
|
|
||||||
|
alpha.mask = alphaMask, red.mask = redMask, green.mask = greenMask, blue.mask = blueMask;
|
||||||
|
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||||
|
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||||
|
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||||
|
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
image::image(const string &filename) : data(nullptr) {
|
||||||
|
width = 0, height = 0, pitch = 0;
|
||||||
|
|
||||||
|
this->endian = 0;
|
||||||
|
this->depth = 32;
|
||||||
|
this->stride = 4;
|
||||||
|
|
||||||
|
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
|
||||||
|
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||||
|
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||||
|
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||||
|
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||||
|
|
||||||
|
load(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
image::image(const uint8_t *data, unsigned size) : data(nullptr) {
|
||||||
|
width = 0, height = 0, pitch = 0;
|
||||||
|
|
||||||
|
this->endian = 0;
|
||||||
|
this->depth = 32;
|
||||||
|
this->stride = 4;
|
||||||
|
|
||||||
|
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
|
||||||
|
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||||
|
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||||
|
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||||
|
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||||
|
|
||||||
|
loadPNG(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
image::image() : data(nullptr) {
|
||||||
|
width = 0, height = 0, pitch = 0;
|
||||||
|
|
||||||
|
this->endian = 0;
|
||||||
|
this->depth = 32;
|
||||||
|
this->stride = 4;
|
||||||
|
|
||||||
|
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
|
||||||
|
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||||
|
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||||
|
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||||
|
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
image::~image() {
|
||||||
|
free();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t image::read(const uint8_t *data) const {
|
||||||
|
uint64_t result = 0;
|
||||||
|
if(endian == 0) {
|
||||||
|
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
|
||||||
|
} else {
|
||||||
|
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::write(uint8_t *data, uint64_t value) const {
|
||||||
|
if(endian == 0) {
|
||||||
|
for(signed n = 0; n < stride; n++) { data[n] = value; value >>= 8; }
|
||||||
|
} else {
|
||||||
|
for(signed n = stride - 1; n >= 0; n--) { data[n] = value; value >>= 8; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::free() {
|
||||||
|
if(data) delete[] data;
|
||||||
|
data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image::empty() const {
|
||||||
|
if(data == nullptr) return true;
|
||||||
|
if(width == 0 || height == 0) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::allocate(unsigned width, unsigned height) {
|
||||||
|
if(data != nullptr && this->width == width && this->height == height) return;
|
||||||
|
free();
|
||||||
|
data = new uint8_t[width * height * stride]();
|
||||||
|
pitch = width * stride;
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::clear(uint64_t color) {
|
||||||
|
uint8_t *dp = data;
|
||||||
|
for(unsigned n = 0; n < width * height; n++) {
|
||||||
|
write(dp, color);
|
||||||
|
dp += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image::load(const string &filename) {
|
||||||
|
if(loadBMP(filename) == true) return true;
|
||||||
|
if(loadPNG(filename) == true) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::scale(unsigned outputWidth, unsigned outputHeight, interpolation op) {
|
||||||
|
if(width != outputWidth) scaleX(outputWidth, op);
|
||||||
|
if(height != outputHeight) scaleY(outputHeight, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
|
||||||
|
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
|
||||||
|
output.allocate(width, height);
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
uint8_t *dp = output.data + output.pitch * y;
|
||||||
|
uint8_t *sp = data + pitch * y;
|
||||||
|
for(unsigned x = 0; x < width; x++) {
|
||||||
|
uint64_t color = read(sp);
|
||||||
|
sp += stride;
|
||||||
|
|
||||||
|
uint64_t a = (color & alpha.mask) >> alpha.shift;
|
||||||
|
uint64_t r = (color & red.mask) >> red.shift;
|
||||||
|
uint64_t g = (color & green.mask) >> green.shift;
|
||||||
|
uint64_t b = (color & blue.mask) >> blue.shift;
|
||||||
|
|
||||||
|
a = normalize(a, alpha.depth, output.alpha.depth);
|
||||||
|
r = normalize(r, red.depth, output.red.depth);
|
||||||
|
g = normalize(g, green.depth, output.green.depth);
|
||||||
|
b = normalize(b, blue.depth, output.blue.depth);
|
||||||
|
|
||||||
|
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
|
||||||
|
dp += output.stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator=(std::move(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::alphaBlend(uint64_t alphaColor) {
|
||||||
|
uint64_t alphaR = (alphaColor & red.mask) >> red.shift;
|
||||||
|
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
|
||||||
|
uint64_t alphaB = (alphaColor & blue.mask) >> blue.shift;
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
uint8_t *dp = data + pitch * y;
|
||||||
|
for(unsigned x = 0; x < width; x++) {
|
||||||
|
uint64_t color = read(dp);
|
||||||
|
|
||||||
|
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
|
||||||
|
uint64_t colorR = (color & red.mask) >> red.shift;
|
||||||
|
uint64_t colorG = (color & green.mask) >> green.shift;
|
||||||
|
uint64_t colorB = (color & blue.mask) >> blue.shift;
|
||||||
|
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
|
||||||
|
|
||||||
|
colorA = (1 << alpha.depth) - 1;
|
||||||
|
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
|
||||||
|
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
|
||||||
|
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
|
||||||
|
|
||||||
|
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
|
||||||
|
dp += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//protected
|
||||||
|
|
||||||
|
uint64_t image::interpolate(double mu, const uint64_t *s, double (*op)(double, double, double, double, double)) {
|
||||||
|
uint64_t aa = (s[0] & alpha.mask) >> alpha.shift, ar = (s[0] & red.mask) >> red.shift,
|
||||||
|
ag = (s[0] & green.mask) >> green.shift, ab = (s[0] & blue.mask) >> blue.shift;
|
||||||
|
uint64_t ba = (s[1] & alpha.mask) >> alpha.shift, br = (s[1] & red.mask) >> red.shift,
|
||||||
|
bg = (s[1] & green.mask) >> green.shift, bb = (s[1] & blue.mask) >> blue.shift;
|
||||||
|
uint64_t ca = (s[2] & alpha.mask) >> alpha.shift, cr = (s[2] & red.mask) >> red.shift,
|
||||||
|
cg = (s[2] & green.mask) >> green.shift, cb = (s[2] & blue.mask) >> blue.shift;
|
||||||
|
uint64_t da = (s[3] & alpha.mask) >> alpha.shift, dr = (s[3] & red.mask) >> red.shift,
|
||||||
|
dg = (s[3] & green.mask) >> green.shift, db = (s[3] & blue.mask) >> blue.shift;
|
||||||
|
|
||||||
|
int64_t A = op(mu, aa, ba, ca, da);
|
||||||
|
int64_t R = op(mu, ar, br, cr, dr);
|
||||||
|
int64_t G = op(mu, ag, bg, cg, dg);
|
||||||
|
int64_t B = op(mu, ab, bb, cb, db);
|
||||||
|
|
||||||
|
A = max(0, min(A, (1 << alpha.depth) - 1));
|
||||||
|
R = max(0, min(R, (1 << red.depth) - 1));
|
||||||
|
G = max(0, min(G, (1 << green.depth) - 1));
|
||||||
|
B = max(0, min(B, (1 << blue.depth) - 1));
|
||||||
|
|
||||||
|
return (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::scaleX(unsigned outputWidth, interpolation op) {
|
||||||
|
uint8_t *outputData = new uint8_t[outputWidth * height * stride];
|
||||||
|
unsigned outputPitch = outputWidth * stride;
|
||||||
|
double step = (double)width / (double)outputWidth;
|
||||||
|
const uint8_t *terminal = data + pitch * height;
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
uint8_t *dp = outputData + outputPitch * y;
|
||||||
|
uint8_t *sp = data + pitch * y;
|
||||||
|
|
||||||
|
double fraction = 0.0;
|
||||||
|
uint64_t s[4] = { sp < terminal ? read(sp) : 0 }; //B,C (0,1) = center of kernel { 0, 0, 1, 2 }
|
||||||
|
s[1] = s[0];
|
||||||
|
s[2] = sp + stride < terminal ? read(sp += stride) : s[1];
|
||||||
|
s[3] = sp + stride < terminal ? read(sp += stride) : s[2];
|
||||||
|
|
||||||
|
for(unsigned x = 0; x < width; x++) {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
if(dp >= outputData + outputPitch * height) break;
|
||||||
|
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
|
||||||
|
dp += stride;
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
|
||||||
|
if(sp + stride < terminal) s[3] = read(sp += stride);
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free();
|
||||||
|
data = outputData;
|
||||||
|
width = outputWidth;
|
||||||
|
pitch = width * stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
void image::scaleY(unsigned outputHeight, interpolation op) {
|
||||||
|
uint8_t *outputData = new uint8_t[width * outputHeight * stride];
|
||||||
|
double step = (double)height / (double)outputHeight;
|
||||||
|
const uint8_t *terminal = data + pitch * height;
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(unsigned x = 0; x < width; x++) {
|
||||||
|
uint8_t *dp = outputData + stride * x;
|
||||||
|
uint8_t *sp = data + stride * x;
|
||||||
|
|
||||||
|
double fraction = 0.0;
|
||||||
|
uint64_t s[4] = { sp < terminal ? read(sp) : 0 };
|
||||||
|
s[1] = s[0];
|
||||||
|
s[2] = sp + pitch < terminal ? read(sp += pitch) : s[1];
|
||||||
|
s[3] = sp + pitch < terminal ? read(sp += pitch) : s[2];
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
if(dp >= outputData + pitch * outputHeight) break;
|
||||||
|
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
|
||||||
|
dp += pitch;
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
|
||||||
|
if(sp + pitch < terminal) s[3] = read(sp += pitch);
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free();
|
||||||
|
data = outputData;
|
||||||
|
height = outputHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image::loadBMP(const string &filename) {
|
||||||
|
uint32_t *outputData;
|
||||||
|
unsigned outputWidth, outputHeight;
|
||||||
|
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
|
||||||
|
|
||||||
|
allocate(outputWidth, outputHeight);
|
||||||
|
const uint32_t *sp = outputData;
|
||||||
|
uint8_t *dp = data;
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < outputHeight; y++) {
|
||||||
|
for(unsigned x = 0; x < outputWidth; x++) {
|
||||||
|
uint32_t color = *sp++;
|
||||||
|
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
|
||||||
|
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
|
||||||
|
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
|
||||||
|
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
|
||||||
|
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
|
||||||
|
dp += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] outputData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image::loadPNG(const uint8_t *pngData, unsigned pngSize) {
|
||||||
|
png source;
|
||||||
|
if(source.decode(pngData, pngSize) == false) return false;
|
||||||
|
|
||||||
|
allocate(source.info.width, source.info.height);
|
||||||
|
const uint8_t *sp = source.data;
|
||||||
|
uint8_t *dp = data;
|
||||||
|
|
||||||
|
auto decode = [&]() -> uint64_t {
|
||||||
|
uint64_t p, r, g, b, a;
|
||||||
|
|
||||||
|
switch(source.info.colorType) {
|
||||||
|
case 0: //L
|
||||||
|
r = g = b = source.readbits(sp);
|
||||||
|
a = (1 << source.info.bitDepth) - 1;
|
||||||
|
break;
|
||||||
|
case 2: //R,G,B
|
||||||
|
r = source.readbits(sp);
|
||||||
|
g = source.readbits(sp);
|
||||||
|
b = source.readbits(sp);
|
||||||
|
a = (1 << source.info.bitDepth) - 1;
|
||||||
|
break;
|
||||||
|
case 3: //P
|
||||||
|
p = source.readbits(sp);
|
||||||
|
r = source.info.palette[p][0];
|
||||||
|
g = source.info.palette[p][1];
|
||||||
|
b = source.info.palette[p][2];
|
||||||
|
a = (1 << source.info.bitDepth) - 1;
|
||||||
|
break;
|
||||||
|
case 4: //L,A
|
||||||
|
r = g = b = source.readbits(sp);
|
||||||
|
a = source.readbits(sp);
|
||||||
|
break;
|
||||||
|
case 6: //R,G,B,A
|
||||||
|
r = source.readbits(sp);
|
||||||
|
g = source.readbits(sp);
|
||||||
|
b = source.readbits(sp);
|
||||||
|
a = source.readbits(sp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = normalize(a, source.info.bitDepth, alpha.depth);
|
||||||
|
r = normalize(r, source.info.bitDepth, red.depth);
|
||||||
|
g = normalize(g, source.info.bitDepth, green.depth);
|
||||||
|
b = normalize(b, source.info.bitDepth, blue.depth);
|
||||||
|
|
||||||
|
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
|
||||||
|
};
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
for(unsigned x = 0; x < width; x++) {
|
||||||
|
write(dp, decode());
|
||||||
|
dp += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image::loadPNG(const string &filename) {
|
||||||
|
filemap map;
|
||||||
|
if(map.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
return loadPNG(map.data(), map.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,358 @@
|
||||||
|
#ifndef NALL_INFLATE_HPP
|
||||||
|
#define NALL_INFLATE_HPP
|
||||||
|
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
namespace puff {
|
||||||
|
inline int puff(
|
||||||
|
unsigned char *dest, unsigned long *destlen,
|
||||||
|
unsigned char *source, unsigned long *sourcelen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool inflate(
|
||||||
|
uint8_t *target, unsigned targetLength,
|
||||||
|
const uint8_t *source, unsigned sourceLength
|
||||||
|
) {
|
||||||
|
unsigned long tl = targetLength, sl = sourceLength;
|
||||||
|
int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl);
|
||||||
|
return result == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace puff {
|
||||||
|
|
||||||
|
//zlib/contrib/puff.c
|
||||||
|
//version 2.1*
|
||||||
|
//author: Mark Adler
|
||||||
|
//license: zlib
|
||||||
|
//ported by: byuu
|
||||||
|
|
||||||
|
//* I have corrected a bug in fixed(), where it was accessing uninitialized
|
||||||
|
// memory: calling construct() with lencode prior to initializing lencode.count
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MAXBITS = 15,
|
||||||
|
MAXLCODES = 286,
|
||||||
|
MAXDCODES = 30,
|
||||||
|
FIXLCODES = 288,
|
||||||
|
MAXCODES = MAXLCODES + MAXDCODES,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct state {
|
||||||
|
unsigned char *out;
|
||||||
|
unsigned long outlen;
|
||||||
|
unsigned long outcnt;
|
||||||
|
|
||||||
|
unsigned char *in;
|
||||||
|
unsigned long inlen;
|
||||||
|
unsigned long incnt;
|
||||||
|
int bitbuf;
|
||||||
|
int bitcnt;
|
||||||
|
|
||||||
|
jmp_buf env;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct huffman {
|
||||||
|
short *count;
|
||||||
|
short *symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline int bits(state *s, int need) {
|
||||||
|
long val;
|
||||||
|
|
||||||
|
val = s->bitbuf;
|
||||||
|
while(s->bitcnt < need) {
|
||||||
|
if(s->incnt == s->inlen) longjmp(s->env, 1);
|
||||||
|
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
|
||||||
|
s->bitcnt += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->bitbuf = (int)(val >> need);
|
||||||
|
s->bitcnt -= need;
|
||||||
|
|
||||||
|
return (int)(val & ((1L << need) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int stored(state *s) {
|
||||||
|
unsigned len;
|
||||||
|
|
||||||
|
s->bitbuf = 0;
|
||||||
|
s->bitcnt = 0;
|
||||||
|
|
||||||
|
if(s->incnt + 4 > s->inlen) return 2;
|
||||||
|
len = s->in[s->incnt++];
|
||||||
|
len |= s->in[s->incnt++] << 8;
|
||||||
|
if(s->in[s->incnt++] != (~len & 0xff) ||
|
||||||
|
s->in[s->incnt++] != ((~len >> 8) & 0xff)
|
||||||
|
) return 2;
|
||||||
|
|
||||||
|
if(s->incnt + len > s->inlen) return 2;
|
||||||
|
if(s->out != 0) {
|
||||||
|
if(s->outcnt + len > s->outlen) return 1;
|
||||||
|
while(len--) s->out[s->outcnt++] = s->in[s->incnt++];
|
||||||
|
} else {
|
||||||
|
s->outcnt += len;
|
||||||
|
s->incnt += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int decode(state *s, huffman *h) {
|
||||||
|
int len, code, first, count, index, bitbuf, left;
|
||||||
|
short *next;
|
||||||
|
|
||||||
|
bitbuf = s->bitbuf;
|
||||||
|
left = s->bitcnt;
|
||||||
|
code = first = index = 0;
|
||||||
|
len = 1;
|
||||||
|
next = h->count + 1;
|
||||||
|
while(true) {
|
||||||
|
while(left--) {
|
||||||
|
code |= bitbuf & 1;
|
||||||
|
bitbuf >>= 1;
|
||||||
|
count = *next++;
|
||||||
|
if(code - count < first) {
|
||||||
|
s->bitbuf = bitbuf;
|
||||||
|
s->bitcnt = (s->bitcnt - len) & 7;
|
||||||
|
return h->symbol[index + (code - first)];
|
||||||
|
}
|
||||||
|
index += count;
|
||||||
|
first += count;
|
||||||
|
first <<= 1;
|
||||||
|
code <<= 1;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
left = (MAXBITS + 1) - len;
|
||||||
|
if(left == 0) break;
|
||||||
|
if(s->incnt == s->inlen) longjmp(s->env, 1);
|
||||||
|
bitbuf = s->in[s->incnt++];
|
||||||
|
if(left > 8) left = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int construct(huffman *h, short *length, int n) {
|
||||||
|
int symbol, len, left;
|
||||||
|
short offs[MAXBITS + 1];
|
||||||
|
|
||||||
|
for(len = 0; len <= MAXBITS; len++) h->count[len] = 0;
|
||||||
|
for(symbol = 0; symbol < n; symbol++) h->count[length[symbol]]++;
|
||||||
|
if(h->count[0] == n) return 0;
|
||||||
|
|
||||||
|
left = 1;
|
||||||
|
for(len = 1; len <= MAXBITS; len++) {
|
||||||
|
left <<= 1;
|
||||||
|
left -= h->count[len];
|
||||||
|
if(left < 0) return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
offs[1] = 0;
|
||||||
|
for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len];
|
||||||
|
|
||||||
|
for(symbol = 0; symbol < n; symbol++) {
|
||||||
|
if(length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int codes(state *s, huffman *lencode, huffman *distcode) {
|
||||||
|
int symbol, len;
|
||||||
|
unsigned dist;
|
||||||
|
static const short lens[29] = {
|
||||||
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||||
|
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
||||||
|
};
|
||||||
|
static const short lext[29] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||||
|
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
||||||
|
};
|
||||||
|
static const short dists[30] = {
|
||||||
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||||
|
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||||
|
8193, 12289, 16385, 24577
|
||||||
|
};
|
||||||
|
static const short dext[30] = {
|
||||||
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||||
|
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||||
|
12, 12, 13, 13
|
||||||
|
};
|
||||||
|
|
||||||
|
do {
|
||||||
|
symbol = decode(s, lencode);
|
||||||
|
if(symbol < 0) return symbol;
|
||||||
|
if(symbol < 256) {
|
||||||
|
if(s->out != 0) {
|
||||||
|
if(s->outcnt == s->outlen) return 1;
|
||||||
|
s->out[s->outcnt] = symbol;
|
||||||
|
}
|
||||||
|
s->outcnt++;
|
||||||
|
} else if(symbol > 256) {
|
||||||
|
symbol -= 257;
|
||||||
|
if(symbol >= 29) return -10;
|
||||||
|
len = lens[symbol] + bits(s, lext[symbol]);
|
||||||
|
|
||||||
|
symbol = decode(s, distcode);
|
||||||
|
if(symbol < 0) return symbol;
|
||||||
|
dist = dists[symbol] + bits(s, dext[symbol]);
|
||||||
|
#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
|
||||||
|
if(dist > s->outcnt) return -11;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(s->out != 0) {
|
||||||
|
if(s->outcnt + len > s->outlen) return 1;
|
||||||
|
while(len--) {
|
||||||
|
s->out[s->outcnt] =
|
||||||
|
#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
|
||||||
|
dist > s->outcnt ? 0 :
|
||||||
|
#endif
|
||||||
|
s->out[s->outcnt - dist];
|
||||||
|
s->outcnt++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s->outcnt += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(symbol != 256);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int fixed(state *s) {
|
||||||
|
static int virgin = 1;
|
||||||
|
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
|
||||||
|
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
|
||||||
|
static huffman lencode, distcode;
|
||||||
|
|
||||||
|
if(virgin) {
|
||||||
|
int symbol = 0;
|
||||||
|
short lengths[FIXLCODES];
|
||||||
|
|
||||||
|
lencode.count = lencnt;
|
||||||
|
lencode.symbol = lensym;
|
||||||
|
distcode.count = distcnt;
|
||||||
|
distcode.symbol = distsym;
|
||||||
|
|
||||||
|
for(; symbol < 144; symbol++) lengths[symbol] = 8;
|
||||||
|
for(; symbol < 256; symbol++) lengths[symbol] = 9;
|
||||||
|
for(; symbol < 280; symbol++) lengths[symbol] = 7;
|
||||||
|
for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
|
||||||
|
construct(&lencode, lengths, FIXLCODES);
|
||||||
|
|
||||||
|
for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
|
||||||
|
construct(&distcode, lengths, MAXDCODES);
|
||||||
|
|
||||||
|
virgin = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return codes(s, &lencode, &distcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int dynamic(state *s) {
|
||||||
|
int nlen, ndist, ncode, index, err;
|
||||||
|
short lengths[MAXCODES];
|
||||||
|
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
|
||||||
|
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
|
||||||
|
huffman lencode, distcode;
|
||||||
|
static const short order[19] = {
|
||||||
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||||
|
};
|
||||||
|
|
||||||
|
lencode.count = lencnt;
|
||||||
|
lencode.symbol = lensym;
|
||||||
|
distcode.count = distcnt;
|
||||||
|
distcode.symbol = distsym;
|
||||||
|
|
||||||
|
nlen = bits(s, 5) + 257;
|
||||||
|
ndist = bits(s, 5) + 1;
|
||||||
|
ncode = bits(s, 4) + 4;
|
||||||
|
if(nlen > MAXLCODES || ndist > MAXDCODES) return -3;
|
||||||
|
|
||||||
|
for(index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3);
|
||||||
|
for(; index < 19; index++) lengths[order[index]] = 0;
|
||||||
|
|
||||||
|
err = construct(&lencode, lengths, 19);
|
||||||
|
if(err != 0) return -4;
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
while(index < nlen + ndist) {
|
||||||
|
int symbol, len;
|
||||||
|
|
||||||
|
symbol = decode(s, &lencode);
|
||||||
|
if(symbol < 16) {
|
||||||
|
lengths[index++] = symbol;
|
||||||
|
} else {
|
||||||
|
len = 0;
|
||||||
|
if(symbol == 16) {
|
||||||
|
if(index == 0) return -5;
|
||||||
|
len = lengths[index - 1];
|
||||||
|
symbol = 3 + bits(s, 2);
|
||||||
|
} else if(symbol == 17) {
|
||||||
|
symbol = 3 + bits(s, 3);
|
||||||
|
} else {
|
||||||
|
symbol = 11 + bits(s, 7);
|
||||||
|
}
|
||||||
|
if(index + symbol > nlen + ndist) return -6;
|
||||||
|
while(symbol--) lengths[index++] = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lengths[256] == 0) return -9;
|
||||||
|
|
||||||
|
err = construct(&lencode, lengths, nlen);
|
||||||
|
if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7;
|
||||||
|
|
||||||
|
err = construct(&distcode, lengths + nlen, ndist);
|
||||||
|
if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) return -8;
|
||||||
|
|
||||||
|
return codes(s, &lencode, &distcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int puff(
|
||||||
|
unsigned char *dest, unsigned long *destlen,
|
||||||
|
unsigned char *source, unsigned long *sourcelen
|
||||||
|
) {
|
||||||
|
state s;
|
||||||
|
int last, type, err;
|
||||||
|
|
||||||
|
s.out = dest;
|
||||||
|
s.outlen = *destlen;
|
||||||
|
s.outcnt = 0;
|
||||||
|
|
||||||
|
s.in = source;
|
||||||
|
s.inlen = *sourcelen;
|
||||||
|
s.incnt = 0;
|
||||||
|
s.bitbuf = 0;
|
||||||
|
s.bitcnt = 0;
|
||||||
|
|
||||||
|
if(setjmp(s.env) != 0) {
|
||||||
|
err = 2;
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
last = bits(&s, 1);
|
||||||
|
type = bits(&s, 2);
|
||||||
|
err = type == 0 ? stored(&s)
|
||||||
|
: type == 1 ? fixed(&s)
|
||||||
|
: type == 2 ? dynamic(&s)
|
||||||
|
: -1;
|
||||||
|
if(err != 0) break;
|
||||||
|
} while(!last);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(err <= 0) {
|
||||||
|
*destlen = s.outcnt;
|
||||||
|
*sourcelen = s.incnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 { "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 { "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 { "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,59 @@
|
||||||
|
#ifndef NALL_INTERPOLATION_HPP
|
||||||
|
#define NALL_INTERPOLATION_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct Interpolation {
|
||||||
|
static inline double Nearest(double mu, double a, double b, double c, double d) {
|
||||||
|
return (mu <= 0.5 ? b : c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double Sublinear(double mu, double a, double b, double c, double d) {
|
||||||
|
mu = ((mu - 0.5) * 2.0) + 0.5;
|
||||||
|
if(mu < 0) mu = 0;
|
||||||
|
if(mu > 1) mu = 1;
|
||||||
|
return b * (1.0 - mu) + c * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double Linear(double mu, double a, double b, double c, double d) {
|
||||||
|
return b * (1.0 - mu) + c * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double Cosine(double mu, double a, double b, double c, double d) {
|
||||||
|
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||||
|
return b * (1.0 - mu) + c * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double Cubic(double mu, double a, double b, double c, double d) {
|
||||||
|
double A = d - c - a + b;
|
||||||
|
double B = a - b - A;
|
||||||
|
double C = c - a;
|
||||||
|
double D = b;
|
||||||
|
return A * (mu * mu * mu) + B * (mu * mu) + C * mu + D;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double Hermite(double mu1, double a, double b, double c, double d) {
|
||||||
|
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||||
|
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||||
|
double mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||||
|
|
||||||
|
mu2 = mu1 * mu1;
|
||||||
|
mu3 = mu2 * mu1;
|
||||||
|
|
||||||
|
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||||
|
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||||
|
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||||
|
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||||
|
|
||||||
|
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||||
|
a1 = mu3 - 2 * mu2 + mu1;
|
||||||
|
a2 = mu3 - mu2;
|
||||||
|
a3 = -2 * mu3 + 3 * mu2;
|
||||||
|
|
||||||
|
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef NALL_INTRINSICS_HPP
|
||||||
|
#define NALL_INTRINSICS_HPP
|
||||||
|
|
||||||
|
struct Intrinsics {
|
||||||
|
enum class Compiler : unsigned { GCC, VisualC, Unknown };
|
||||||
|
enum class Platform : unsigned { X, OSX, Windows, Unknown };
|
||||||
|
enum class Endian : unsigned { LSB, MSB, Unknown };
|
||||||
|
|
||||||
|
static inline Compiler compiler();
|
||||||
|
static inline Platform platform();
|
||||||
|
static inline Endian endian();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Compiler detection */
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define COMPILER_GCC
|
||||||
|
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::GCC; }
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define COMPILER_VISUALC
|
||||||
|
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::VisualC; }
|
||||||
|
#else
|
||||||
|
#warning "unable to detect compiler"
|
||||||
|
#define COMPILER_UNKNOWN
|
||||||
|
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Platform detection */
|
||||||
|
|
||||||
|
#if defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||||
|
#define PLATFORM_X
|
||||||
|
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::X; }
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define PLATFORM_OSX
|
||||||
|
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::OSX; }
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#define PLATFORM_WINDOWS
|
||||||
|
#define PLATFORM_WIN
|
||||||
|
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; }
|
||||||
|
#else
|
||||||
|
#warning "unable to detect platform"
|
||||||
|
#define PLATFORM_UNKNOWN
|
||||||
|
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Unknown; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Endian detection */
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
|
||||||
|
#define ENDIAN_LSB
|
||||||
|
#define ARCH_LSB
|
||||||
|
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::LSB; }
|
||||||
|
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
|
||||||
|
#define ENDIAN_MSB
|
||||||
|
#define ARCH_MSB
|
||||||
|
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::MSB; }
|
||||||
|
#else
|
||||||
|
#warning "unable to detect endian"
|
||||||
|
#define ENDIAN_UNKNOWN
|
||||||
|
#define ARCH_UNKNOWN
|
||||||
|
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::Unknown; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef NALL_INVOKE_HPP
|
||||||
|
#define NALL_INVOKE_HPP
|
||||||
|
|
||||||
|
//void invoke(const string &name, const string& args...);
|
||||||
|
//if a program is specified, it is executed with the arguments provided
|
||||||
|
//if a file is specified, the file is opened using the program associated with said file type
|
||||||
|
//if a folder is specified, the folder is opened using the associated file explorer
|
||||||
|
//if a URL is specified, the default web browser is opened and pointed at the URL requested
|
||||||
|
//path environment variable is always consulted
|
||||||
|
//execution is asynchronous (non-blocking); use system() for synchronous execution
|
||||||
|
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void invoke(const string &name, Args&&... args) {
|
||||||
|
lstring argl(std::forward<Args>(args)...);
|
||||||
|
for(auto &arg : argl) if(arg.position(" ")) arg = {"\"", arg, "\""};
|
||||||
|
string arguments = argl.concatenate(" ");
|
||||||
|
ShellExecuteW(NULL, NULL, utf16_t(name), utf16_t(arguments), NULL, SW_SHOWNORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void invoke(const string &name, Args&&... args) {
|
||||||
|
pid_t pid = fork();
|
||||||
|
if(pid == 0) {
|
||||||
|
const char *argv[1 + sizeof...(args) + 1], **argp = argv;
|
||||||
|
lstring argl(std::forward<Args>(args)...);
|
||||||
|
*argp++ = (const char*)name;
|
||||||
|
for(auto &arg : argl) *argp++ = (const char*)arg;
|
||||||
|
*argp++ = nullptr;
|
||||||
|
|
||||||
|
if(execvp(name, (char* const*)argv) < 0) {
|
||||||
|
execlp("xdg-open", "xdg-open", (const char*)name, nullptr);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,100 @@
|
||||||
|
#ifndef NALL_IPS_HPP
|
||||||
|
#define NALL_IPS_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct ips {
|
||||||
|
inline bool apply();
|
||||||
|
inline void source(const uint8_t *data, unsigned size);
|
||||||
|
inline void modify(const uint8_t *data, unsigned size);
|
||||||
|
inline ips();
|
||||||
|
inline ~ips();
|
||||||
|
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
const uint8_t *sourceData;
|
||||||
|
unsigned sourceSize;
|
||||||
|
const uint8_t *modifyData;
|
||||||
|
unsigned modifySize;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ips::apply() {
|
||||||
|
if(modifySize < 8) return false;
|
||||||
|
if(modifyData[0] != 'P') return false;
|
||||||
|
if(modifyData[1] != 'A') return false;
|
||||||
|
if(modifyData[2] != 'T') return false;
|
||||||
|
if(modifyData[3] != 'C') return false;
|
||||||
|
if(modifyData[4] != 'H') return false;
|
||||||
|
|
||||||
|
if(data) delete[] data;
|
||||||
|
data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding
|
||||||
|
size = sourceSize;
|
||||||
|
memcpy(data, sourceData, sourceSize);
|
||||||
|
unsigned offset = 5;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
unsigned address, length;
|
||||||
|
|
||||||
|
if(offset > modifySize - 3) break;
|
||||||
|
address = modifyData[offset++] << 16;
|
||||||
|
address |= modifyData[offset++] << 8;
|
||||||
|
address |= modifyData[offset++] << 0;
|
||||||
|
|
||||||
|
if(address == 0x454f46) { //EOF
|
||||||
|
if(offset == modifySize) return true;
|
||||||
|
if(offset == modifySize - 3) {
|
||||||
|
size = modifyData[offset++] << 16;
|
||||||
|
size |= modifyData[offset++] << 8;
|
||||||
|
size |= modifyData[offset++] << 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(offset > modifySize - 2) break;
|
||||||
|
length = modifyData[offset++] << 8;
|
||||||
|
length |= modifyData[offset++] << 0;
|
||||||
|
|
||||||
|
if(length) { //Copy
|
||||||
|
if(offset > modifySize - length) break;
|
||||||
|
while(length--) data[address++] = modifyData[offset++];
|
||||||
|
} else { //RLE
|
||||||
|
if(offset > modifySize - 3) break;
|
||||||
|
length = modifyData[offset++] << 8;
|
||||||
|
length |= modifyData[offset++] << 0;
|
||||||
|
if(length == 0) break; //illegal
|
||||||
|
while(length--) data[address++] = modifyData[offset];
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = max(size, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
|
data = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ips::source(const uint8_t *data, unsigned size) {
|
||||||
|
sourceData = data, sourceSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ips::modify(const uint8_t *data, unsigned size) {
|
||||||
|
modifyData = data, modifySize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ips::~ips() {
|
||||||
|
if(data) delete[] data;
|
||||||
|
if(sourceData) delete[] sourceData;
|
||||||
|
if(modifyData) delete[] modifyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,165 @@
|
||||||
|
#ifndef NALL_LZSS_HPP
|
||||||
|
#define NALL_LZSS_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
//19:5 pulldown
|
||||||
|
//8:1 marker: d7-d0
|
||||||
|
//length: { 4 - 35 }, offset: { 1 - 0x80000 }
|
||||||
|
//4-byte file size header
|
||||||
|
//little-endian encoding
|
||||||
|
struct lzss {
|
||||||
|
inline void source(const uint8_t *data, unsigned size);
|
||||||
|
inline bool source(const string &filename);
|
||||||
|
inline unsigned size() const;
|
||||||
|
inline bool compress(const string &filename);
|
||||||
|
inline bool decompress(uint8_t *targetData, unsigned targetSize);
|
||||||
|
inline bool decompress(const string &filename);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct Node {
|
||||||
|
unsigned offset;
|
||||||
|
Node *next;
|
||||||
|
inline Node() : offset(0), next(nullptr) {}
|
||||||
|
inline ~Node() { if(next) delete next; }
|
||||||
|
} *tree[65536];
|
||||||
|
|
||||||
|
filemap sourceFile;
|
||||||
|
const uint8_t *sourceData;
|
||||||
|
unsigned sourceSize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline lzss() : sourceData(nullptr), sourceSize(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void lzss::source(const uint8_t *data, unsigned size) {
|
||||||
|
sourceData = data;
|
||||||
|
sourceSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lzss::source(const string &filename) {
|
||||||
|
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
sourceData = sourceFile.data();
|
||||||
|
sourceSize = sourceFile.size();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned lzss::size() const {
|
||||||
|
unsigned size = 0;
|
||||||
|
if(sourceSize < 4) return size;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) size |= sourceData[n >> 3] << n;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lzss::compress(const string &filename) {
|
||||||
|
file targetFile;
|
||||||
|
if(targetFile.open(filename, file::mode::write) == false) return false;
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) targetFile.write(sourceSize >> n);
|
||||||
|
for(unsigned n = 0; n < 65536; n++) tree[n] = 0;
|
||||||
|
|
||||||
|
uint8_t buffer[25];
|
||||||
|
unsigned sourceOffset = 0;
|
||||||
|
|
||||||
|
while(sourceOffset < sourceSize) {
|
||||||
|
uint8_t mask = 0x00;
|
||||||
|
unsigned bufferOffset = 1;
|
||||||
|
|
||||||
|
for(unsigned iteration = 0; iteration < 8; iteration++) {
|
||||||
|
if(sourceOffset >= sourceSize) break;
|
||||||
|
|
||||||
|
uint16_t symbol = sourceData[sourceOffset + 0];
|
||||||
|
if(sourceOffset < sourceSize - 1) symbol |= sourceData[sourceOffset + 1] << 8;
|
||||||
|
Node *node = tree[symbol];
|
||||||
|
unsigned maxLength = 0, maxOffset = 0;
|
||||||
|
|
||||||
|
while(node) {
|
||||||
|
if(node->offset < sourceOffset - 0x80000) {
|
||||||
|
//out-of-range: all subsequent nodes will also be, so free up their memory
|
||||||
|
if(node->next) { delete node->next; node->next = 0; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned length = 0, x = sourceOffset, y = node->offset;
|
||||||
|
while(length < 35 && x < sourceSize && sourceData[x++] == sourceData[y++]) length++;
|
||||||
|
if(length > maxLength) maxLength = length, maxOffset = node->offset;
|
||||||
|
if(length == 35) break;
|
||||||
|
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
//attach current symbol to top of tree for subsequent searches
|
||||||
|
node = new Node;
|
||||||
|
node->offset = sourceOffset;
|
||||||
|
node->next = tree[symbol];
|
||||||
|
tree[symbol] = node;
|
||||||
|
|
||||||
|
if(maxLength < 4) {
|
||||||
|
buffer[bufferOffset++] = sourceData[sourceOffset++];
|
||||||
|
} else {
|
||||||
|
unsigned output = ((maxLength - 4) << 19) | (sourceOffset - 1 - maxOffset);
|
||||||
|
for(unsigned n = 0; n < 24; n += 8) buffer[bufferOffset++] = output >> n;
|
||||||
|
mask |= 0x80 >> iteration;
|
||||||
|
sourceOffset += maxLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[0] = mask;
|
||||||
|
targetFile.write(buffer, bufferOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceFile.close();
|
||||||
|
targetFile.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lzss::decompress(uint8_t *targetData, unsigned targetSize) {
|
||||||
|
if(targetSize < size()) return false;
|
||||||
|
|
||||||
|
unsigned sourceOffset = 4, targetOffset = 0;
|
||||||
|
while(sourceOffset < sourceSize) {
|
||||||
|
uint8_t mask = sourceData[sourceOffset++];
|
||||||
|
|
||||||
|
for(unsigned iteration = 0; iteration < 8; iteration++) {
|
||||||
|
if(sourceOffset >= sourceSize) break;
|
||||||
|
|
||||||
|
if((mask & (0x80 >> iteration)) == 0) {
|
||||||
|
targetData[targetOffset++] = sourceData[sourceOffset++];
|
||||||
|
} else {
|
||||||
|
unsigned code = 0;
|
||||||
|
for(unsigned n = 0; n < 24; n += 8) code |= sourceData[sourceOffset++] << n;
|
||||||
|
unsigned length = (code >> 19) + 4;
|
||||||
|
unsigned offset = targetOffset - 1 - (code & 0x7ffff);
|
||||||
|
while(length--) targetData[targetOffset++] = targetData[offset++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lzss::decompress(const string &filename) {
|
||||||
|
if(sourceSize < 4) return false;
|
||||||
|
unsigned targetSize = size();
|
||||||
|
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, file::mode::write) == false) return false;
|
||||||
|
fp.truncate(targetSize);
|
||||||
|
fp.close();
|
||||||
|
|
||||||
|
filemap targetFile;
|
||||||
|
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
|
||||||
|
uint8_t *targetData = targetFile.data();
|
||||||
|
|
||||||
|
bool result = decompress(targetData, targetSize);
|
||||||
|
sourceFile.close();
|
||||||
|
targetFile.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,117 @@
|
||||||
|
#ifndef NALL_MAP_HPP
|
||||||
|
#define NALL_MAP_HPP
|
||||||
|
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
template<typename LHS, typename RHS>
|
||||||
|
struct map {
|
||||||
|
struct pair {
|
||||||
|
LHS name;
|
||||||
|
RHS data;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void reset() {
|
||||||
|
list.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned size() const {
|
||||||
|
return list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//O(log n) find
|
||||||
|
inline optional<unsigned> find(const LHS &name) const {
|
||||||
|
signed first = 0, last = size() - 1;
|
||||||
|
while(first <= last) {
|
||||||
|
signed middle = (first + last) / 2;
|
||||||
|
if(name < list[middle].name) last = middle - 1; //search lower half
|
||||||
|
else if(list[middle].name < name) first = middle + 1; //search upper half
|
||||||
|
else return { true, (unsigned)middle }; //match found
|
||||||
|
}
|
||||||
|
return { false, 0u };
|
||||||
|
}
|
||||||
|
|
||||||
|
//O(n) insert + O(log n) find
|
||||||
|
inline RHS& insert(const LHS &name, const RHS &data) {
|
||||||
|
if(auto position = find(name)) {
|
||||||
|
list[position()].data = data;
|
||||||
|
return list[position()].data;
|
||||||
|
}
|
||||||
|
signed offset = size();
|
||||||
|
for(unsigned n = 0; n < size(); n++) {
|
||||||
|
if(name < list[n].name) { offset = n; break; }
|
||||||
|
}
|
||||||
|
list.insert(offset, { name, data });
|
||||||
|
return list[offset].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
//O(log n) find
|
||||||
|
inline void modify(const LHS &name, const RHS &data) {
|
||||||
|
if(auto position = find(name)) list[position()].data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
//O(n) remove + O(log n) find
|
||||||
|
inline void remove(const LHS &name) {
|
||||||
|
if(auto position = find(name)) list.remove(position());
|
||||||
|
}
|
||||||
|
|
||||||
|
//O(log n) find
|
||||||
|
inline RHS& operator[](const LHS &name) {
|
||||||
|
if(auto position = find(name)) return list[position()].data;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const RHS& operator[](const LHS &name) const {
|
||||||
|
if(auto position = find(name)) return list[position()].data;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RHS& operator()(const LHS &name) {
|
||||||
|
if(auto position = find(name)) return list[position()].data;
|
||||||
|
return insert(name, RHS());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const RHS& operator()(const LHS &name, const RHS &data) const {
|
||||||
|
if(auto position = find(name)) return list[position()].data;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline pair* begin() { return list.begin(); }
|
||||||
|
inline pair* end() { return list.end(); }
|
||||||
|
inline const pair* begin() const { return list.begin(); }
|
||||||
|
inline const pair* end() const { return list.end(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
vector<pair> list;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename LHS, typename RHS>
|
||||||
|
struct bidirectional_map {
|
||||||
|
const map<LHS, RHS> &lhs;
|
||||||
|
const map<RHS, LHS> &rhs;
|
||||||
|
|
||||||
|
inline void reset() {
|
||||||
|
llist.reset();
|
||||||
|
rlist.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned size() const {
|
||||||
|
return llist.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void insert(const LHS &ldata, const RHS &rdata) {
|
||||||
|
llist.insert(ldata, rdata);
|
||||||
|
rlist.insert(rdata, ldata);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bidirectional_map() : lhs(llist), rhs(rlist) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
map<LHS, RHS> llist;
|
||||||
|
map<RHS, LHS> rlist;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef NALL_MOSAIC_HPP
|
||||||
|
#define NALL_MOSAIC_HPP
|
||||||
|
|
||||||
|
#define NALL_MOSAIC_INTERNAL_HPP
|
||||||
|
#include <nall/mosaic/bitstream.hpp>
|
||||||
|
#include <nall/mosaic/context.hpp>
|
||||||
|
#include <nall/mosaic/parser.hpp>
|
||||||
|
#undef NALL_MOSAIC_INTERNAL_HPP
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,55 @@
|
||||||
|
#ifdef NALL_MOSAIC_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace mosaic {
|
||||||
|
|
||||||
|
struct bitstream {
|
||||||
|
filemap fp;
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
bool readonly;
|
||||||
|
bool endian;
|
||||||
|
|
||||||
|
inline bool read(uint64_t addr) const {
|
||||||
|
if(data == nullptr || (addr >> 3) >= size) return 0;
|
||||||
|
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
|
||||||
|
return data[addr >> 3] & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void write(uint64_t addr, bool value) {
|
||||||
|
if(data == nullptr || readonly == true || (addr >> 3) >= size) return;
|
||||||
|
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
|
||||||
|
if(value == 0) data[addr >> 3] &= ~mask;
|
||||||
|
if(value == 1) data[addr >> 3] |= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool open(const string &filename) {
|
||||||
|
readonly = false;
|
||||||
|
if(fp.open(filename, filemap::mode::readwrite) == false) {
|
||||||
|
readonly = true;
|
||||||
|
if(fp.open(filename, filemap::mode::read) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = fp.data();
|
||||||
|
size = fp.size();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void close() {
|
||||||
|
fp.close();
|
||||||
|
data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bitstream() : data(nullptr), endian(1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~bitstream() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,224 @@
|
||||||
|
#ifdef NALL_MOSAIC_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace mosaic {
|
||||||
|
|
||||||
|
struct context {
|
||||||
|
unsigned offset;
|
||||||
|
unsigned width;
|
||||||
|
unsigned height;
|
||||||
|
unsigned count;
|
||||||
|
|
||||||
|
bool endian; //0 = lsb, 1 = msb
|
||||||
|
bool order; //0 = linear, 1 = planar
|
||||||
|
unsigned depth; //1 - 24bpp
|
||||||
|
|
||||||
|
unsigned blockWidth;
|
||||||
|
unsigned blockHeight;
|
||||||
|
unsigned blockStride;
|
||||||
|
unsigned blockOffset;
|
||||||
|
vector<unsigned> block;
|
||||||
|
|
||||||
|
unsigned tileWidth;
|
||||||
|
unsigned tileHeight;
|
||||||
|
unsigned tileStride;
|
||||||
|
unsigned tileOffset;
|
||||||
|
vector<unsigned> tile;
|
||||||
|
|
||||||
|
unsigned mosaicWidth;
|
||||||
|
unsigned mosaicHeight;
|
||||||
|
unsigned mosaicStride;
|
||||||
|
unsigned mosaicOffset;
|
||||||
|
vector<unsigned> mosaic;
|
||||||
|
|
||||||
|
unsigned paddingWidth;
|
||||||
|
unsigned paddingHeight;
|
||||||
|
unsigned paddingColor;
|
||||||
|
vector<unsigned> palette;
|
||||||
|
|
||||||
|
inline unsigned objectWidth() const { return blockWidth * tileWidth * mosaicWidth + paddingWidth; }
|
||||||
|
inline unsigned objectHeight() const { return blockHeight * tileHeight * mosaicHeight + paddingHeight; }
|
||||||
|
inline unsigned objectSize() const {
|
||||||
|
unsigned size = blockStride * tileWidth * tileHeight * mosaicWidth * mosaicHeight
|
||||||
|
+ blockOffset * tileHeight * mosaicWidth * mosaicHeight
|
||||||
|
+ tileStride * mosaicWidth * mosaicHeight
|
||||||
|
+ tileOffset * mosaicHeight;
|
||||||
|
return max(1u, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned eval(const string &expression) {
|
||||||
|
intmax_t result;
|
||||||
|
if(fixedpoint::eval(expression, result) == false) return 0u;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void eval(vector<unsigned> &buffer, const string &expression_) {
|
||||||
|
string expression = expression_;
|
||||||
|
bool function = false;
|
||||||
|
for(auto &c : expression) {
|
||||||
|
if(c == '(') function = true;
|
||||||
|
if(c == ')') function = false;
|
||||||
|
if(c == ',' && function == true) c = ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring list = expression.split(",");
|
||||||
|
for(auto &item : list) {
|
||||||
|
item.trim();
|
||||||
|
if(item.wildcard("f(?*) ?*")) {
|
||||||
|
item.ltrim<1>("f(");
|
||||||
|
lstring part = item.split<1>(") ");
|
||||||
|
lstring args = part[0].split<3>(";");
|
||||||
|
for(auto &item : args) item.trim();
|
||||||
|
|
||||||
|
unsigned length = eval(args(0, "0"));
|
||||||
|
unsigned offset = eval(args(1, "0"));
|
||||||
|
unsigned stride = eval(args(2, "0"));
|
||||||
|
if(args.size() < 2) offset = buffer.size();
|
||||||
|
if(args.size() < 3) stride = 1;
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < length; n++) {
|
||||||
|
string fn = part[1];
|
||||||
|
fn.replace("n", decimal(n));
|
||||||
|
fn.replace("o", decimal(offset));
|
||||||
|
fn.replace("p", decimal(buffer.size()));
|
||||||
|
buffer.resize(offset + 1);
|
||||||
|
buffer[offset] = eval(fn);
|
||||||
|
offset += stride;
|
||||||
|
}
|
||||||
|
} else if(item.wildcard("base64*")) {
|
||||||
|
unsigned offset = 0;
|
||||||
|
item.ltrim<1>("base64");
|
||||||
|
if(item.wildcard("(?*) *")) {
|
||||||
|
item.ltrim<1>("(");
|
||||||
|
lstring part = item.split<1>(") ");
|
||||||
|
offset = eval(part[0]);
|
||||||
|
item = part(1, "");
|
||||||
|
}
|
||||||
|
item.trim();
|
||||||
|
for(auto &c : item) {
|
||||||
|
if(c >= 'A' && c <= 'Z') buffer.append(offset + c - 'A' + 0);
|
||||||
|
if(c >= 'a' && c <= 'z') buffer.append(offset + c - 'a' + 26);
|
||||||
|
if(c >= '0' && c <= '9') buffer.append(offset + c - '0' + 52);
|
||||||
|
if(c == '-') buffer.append(offset + 62);
|
||||||
|
if(c == '_') buffer.append(offset + 63);
|
||||||
|
}
|
||||||
|
} else if(item.wildcard("file *")) {
|
||||||
|
item.ltrim<1>("file ");
|
||||||
|
item.trim();
|
||||||
|
//...
|
||||||
|
} else if(item.empty() == false) {
|
||||||
|
buffer.append(eval(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void parse(const string &data) {
|
||||||
|
reset();
|
||||||
|
|
||||||
|
lstring lines = data.split("\n");
|
||||||
|
for(auto &line : lines) {
|
||||||
|
lstring part = line.split<1>(":");
|
||||||
|
if(part.size() != 2) continue;
|
||||||
|
part[0].trim();
|
||||||
|
part[1].trim();
|
||||||
|
|
||||||
|
if(part[0] == "offset") offset = eval(part[1]);
|
||||||
|
if(part[0] == "width") width = eval(part[1]);
|
||||||
|
if(part[0] == "height") height = eval(part[1]);
|
||||||
|
if(part[0] == "count") count = eval(part[1]);
|
||||||
|
|
||||||
|
if(part[0] == "endian") endian = eval(part[1]);
|
||||||
|
if(part[0] == "order") order = eval(part[1]);
|
||||||
|
if(part[0] == "depth") depth = eval(part[1]);
|
||||||
|
|
||||||
|
if(part[0] == "blockWidth") blockWidth = eval(part[1]);
|
||||||
|
if(part[0] == "blockHeight") blockHeight = eval(part[1]);
|
||||||
|
if(part[0] == "blockStride") blockStride = eval(part[1]);
|
||||||
|
if(part[0] == "blockOffset") blockOffset = eval(part[1]);
|
||||||
|
if(part[0] == "block") eval(block, part[1]);
|
||||||
|
|
||||||
|
if(part[0] == "tileWidth") tileWidth = eval(part[1]);
|
||||||
|
if(part[0] == "tileHeight") tileHeight = eval(part[1]);
|
||||||
|
if(part[0] == "tileStride") tileStride = eval(part[1]);
|
||||||
|
if(part[0] == "tileOffset") tileOffset = eval(part[1]);
|
||||||
|
if(part[0] == "tile") eval(tile, part[1]);
|
||||||
|
|
||||||
|
if(part[0] == "mosaicWidth") mosaicWidth = eval(part[1]);
|
||||||
|
if(part[0] == "mosaicHeight") mosaicHeight = eval(part[1]);
|
||||||
|
if(part[0] == "mosaicStride") mosaicStride = eval(part[1]);
|
||||||
|
if(part[0] == "mosaicOffset") mosaicOffset = eval(part[1]);
|
||||||
|
if(part[0] == "mosaic") eval(mosaic, part[1]);
|
||||||
|
|
||||||
|
if(part[0] == "paddingWidth") paddingWidth = eval(part[1]);
|
||||||
|
if(part[0] == "paddingHeight") paddingHeight = eval(part[1]);
|
||||||
|
if(part[0] == "paddingColor") paddingColor = eval(part[1]);
|
||||||
|
if(part[0] == "palette") eval(palette, part[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitize();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool load(const string &filename) {
|
||||||
|
string filedata;
|
||||||
|
if(filedata.readfile(filename) == false) return false;
|
||||||
|
parse(filedata);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void sanitize() {
|
||||||
|
if(depth < 1) depth = 1;
|
||||||
|
if(depth > 24) depth = 24;
|
||||||
|
|
||||||
|
if(blockWidth < 1) blockWidth = 1;
|
||||||
|
if(blockHeight < 1) blockHeight = 1;
|
||||||
|
|
||||||
|
if(tileWidth < 1) tileWidth = 1;
|
||||||
|
if(tileHeight < 1) tileHeight = 1;
|
||||||
|
|
||||||
|
if(mosaicWidth < 1) mosaicWidth = 1;
|
||||||
|
if(mosaicHeight < 1) mosaicHeight = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void reset() {
|
||||||
|
offset = 0;
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
endian = 1;
|
||||||
|
order = 0;
|
||||||
|
depth = 1;
|
||||||
|
|
||||||
|
blockWidth = 1;
|
||||||
|
blockHeight = 1;
|
||||||
|
blockStride = 0;
|
||||||
|
blockOffset = 0;
|
||||||
|
block.reset();
|
||||||
|
|
||||||
|
tileWidth = 1;
|
||||||
|
tileHeight = 1;
|
||||||
|
tileStride = 0;
|
||||||
|
tileOffset = 0;
|
||||||
|
tile.reset();
|
||||||
|
|
||||||
|
mosaicWidth = 1;
|
||||||
|
mosaicHeight = 1;
|
||||||
|
mosaicStride = 0;
|
||||||
|
mosaicOffset = 0;
|
||||||
|
mosaic.reset();
|
||||||
|
|
||||||
|
paddingWidth = 0;
|
||||||
|
paddingHeight = 0;
|
||||||
|
paddingColor = 0x000000;
|
||||||
|
palette.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline context() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,126 @@
|
||||||
|
#ifdef NALL_MOSAIC_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace mosaic {
|
||||||
|
|
||||||
|
struct parser {
|
||||||
|
image canvas;
|
||||||
|
|
||||||
|
//export from bitstream to canvas
|
||||||
|
inline void load(bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
|
||||||
|
canvas.allocate(width, height);
|
||||||
|
canvas.clear(ctx.paddingColor);
|
||||||
|
parse(1, stream, offset, ctx, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
//import from canvas to bitstream
|
||||||
|
inline bool save(bitstream &stream, uint64_t offset, context &ctx) {
|
||||||
|
if(stream.readonly) return false;
|
||||||
|
parse(0, stream, offset, ctx, canvas.width, canvas.height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline parser() : canvas(0, 32, 0u, 255u << 16, 255u << 8, 255u << 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline uint32_t read(unsigned x, unsigned y) const {
|
||||||
|
unsigned addr = y * canvas.width + x;
|
||||||
|
if(addr >= canvas.width * canvas.height) return 0u;
|
||||||
|
uint32_t *buffer = (uint32_t*)canvas.data;
|
||||||
|
return buffer[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void write(unsigned x, unsigned y, uint32_t data) {
|
||||||
|
unsigned addr = y * canvas.width + x;
|
||||||
|
if(addr >= canvas.width * canvas.height) return;
|
||||||
|
uint32_t *buffer = (uint32_t*)canvas.data;
|
||||||
|
buffer[addr] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void parse(bool load, bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
|
||||||
|
stream.endian = ctx.endian;
|
||||||
|
unsigned canvasWidth = width / (ctx.mosaicWidth * ctx.tileWidth * ctx.blockWidth + ctx.paddingWidth);
|
||||||
|
unsigned canvasHeight = height / (ctx.mosaicHeight * ctx.tileHeight * ctx.blockHeight + ctx.paddingHeight);
|
||||||
|
unsigned bitsPerBlock = ctx.depth * ctx.blockWidth * ctx.blockHeight;
|
||||||
|
|
||||||
|
unsigned objectOffset = 0;
|
||||||
|
for(unsigned objectY = 0; objectY < canvasHeight; objectY++) {
|
||||||
|
for(unsigned objectX = 0; objectX < canvasWidth; objectX++) {
|
||||||
|
if(objectOffset >= ctx.count && ctx.count > 0) break;
|
||||||
|
unsigned objectIX = objectX * ctx.objectWidth();
|
||||||
|
unsigned objectIY = objectY * ctx.objectHeight();
|
||||||
|
objectOffset++;
|
||||||
|
|
||||||
|
unsigned mosaicOffset = 0;
|
||||||
|
for(unsigned mosaicY = 0; mosaicY < ctx.mosaicHeight; mosaicY++) {
|
||||||
|
for(unsigned mosaicX = 0; mosaicX < ctx.mosaicWidth; mosaicX++) {
|
||||||
|
unsigned mosaicData = ctx.mosaic(mosaicOffset, mosaicOffset);
|
||||||
|
unsigned mosaicIX = (mosaicData % ctx.mosaicWidth) * (ctx.tileWidth * ctx.blockWidth);
|
||||||
|
unsigned mosaicIY = (mosaicData / ctx.mosaicWidth) * (ctx.tileHeight * ctx.blockHeight);
|
||||||
|
mosaicOffset++;
|
||||||
|
|
||||||
|
unsigned tileOffset = 0;
|
||||||
|
for(unsigned tileY = 0; tileY < ctx.tileHeight; tileY++) {
|
||||||
|
for(unsigned tileX = 0; tileX < ctx.tileWidth; tileX++) {
|
||||||
|
unsigned tileData = ctx.tile(tileOffset, tileOffset);
|
||||||
|
unsigned tileIX = (tileData % ctx.tileWidth) * ctx.blockWidth;
|
||||||
|
unsigned tileIY = (tileData / ctx.tileWidth) * ctx.blockHeight;
|
||||||
|
tileOffset++;
|
||||||
|
|
||||||
|
unsigned blockOffset = 0;
|
||||||
|
for(unsigned blockY = 0; blockY < ctx.blockHeight; blockY++) {
|
||||||
|
for(unsigned blockX = 0; blockX < ctx.blockWidth; blockX++) {
|
||||||
|
if(load) {
|
||||||
|
unsigned palette = 0;
|
||||||
|
for(unsigned n = 0; n < ctx.depth; n++) {
|
||||||
|
unsigned index = blockOffset++;
|
||||||
|
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
|
||||||
|
palette |= stream.read(offset + ctx.block(index, index)) << n;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(
|
||||||
|
objectIX + mosaicIX + tileIX + blockX,
|
||||||
|
objectIY + mosaicIY + tileIY + blockY,
|
||||||
|
ctx.palette(palette, palette)
|
||||||
|
);
|
||||||
|
} else /* save */ {
|
||||||
|
uint32_t palette = read(
|
||||||
|
objectIX + mosaicIX + tileIX + blockX,
|
||||||
|
objectIY + mosaicIY + tileIY + blockY
|
||||||
|
);
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < ctx.depth; n++) {
|
||||||
|
unsigned index = blockOffset++;
|
||||||
|
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
|
||||||
|
stream.write(offset + ctx.block(index, index), palette & 1);
|
||||||
|
palette >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //blockX
|
||||||
|
} //blockY
|
||||||
|
|
||||||
|
offset += ctx.blockStride;
|
||||||
|
} //tileX
|
||||||
|
|
||||||
|
offset += ctx.blockOffset;
|
||||||
|
} //tileY
|
||||||
|
|
||||||
|
offset += ctx.tileStride;
|
||||||
|
} //mosaicX
|
||||||
|
|
||||||
|
offset += ctx.tileOffset;
|
||||||
|
} //mosaicY
|
||||||
|
|
||||||
|
offset += ctx.mosaicStride;
|
||||||
|
} //objectX
|
||||||
|
|
||||||
|
offset += ctx.mosaicOffset;
|
||||||
|
} //objectY
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef NALL_HPP
|
||||||
|
#define NALL_HPP
|
||||||
|
|
||||||
|
//include the most common nall headers with one statement
|
||||||
|
//does not include the most obscure components with high cost and low usage
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
|
||||||
|
#include <nall/algorithm.hpp>
|
||||||
|
#include <nall/any.hpp>
|
||||||
|
#include <nall/atoi.hpp>
|
||||||
|
#include <nall/base64.hpp>
|
||||||
|
#include <nall/bit.hpp>
|
||||||
|
#include <nall/bmp.hpp>
|
||||||
|
#include <nall/config.hpp>
|
||||||
|
#include <nall/crc16.hpp>
|
||||||
|
#include <nall/crc32.hpp>
|
||||||
|
#include <nall/directory.hpp>
|
||||||
|
#include <nall/dl.hpp>
|
||||||
|
#include <nall/endian.hpp>
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/function.hpp>
|
||||||
|
#include <nall/gzip.hpp>
|
||||||
|
#include <nall/http.hpp>
|
||||||
|
#include <nall/image.hpp>
|
||||||
|
#include <nall/inflate.hpp>
|
||||||
|
#include <nall/interpolation.hpp>
|
||||||
|
#include <nall/intrinsics.hpp>
|
||||||
|
#include <nall/invoke.hpp>
|
||||||
|
#include <nall/map.hpp>
|
||||||
|
#include <nall/png.hpp>
|
||||||
|
#include <nall/property.hpp>
|
||||||
|
#include <nall/random.hpp>
|
||||||
|
#include <nall/serializer.hpp>
|
||||||
|
#include <nall/set.hpp>
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/sort.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/stream.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/traits.hpp>
|
||||||
|
#include <nall/unzip.hpp>
|
||||||
|
#include <nall/utility.hpp>
|
||||||
|
#include <nall/varint.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
#include <nall/zip.hpp>
|
||||||
|
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
#include <nall/windows/registry.hpp>
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PLATFORM_X)
|
||||||
|
#include <nall/serial.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,86 @@
|
||||||
|
#ifndef NALL_PLATFORM_HPP
|
||||||
|
#define NALL_PLATFORM_HPP
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
//minimum version needed for _wstat64, etc
|
||||||
|
#undef __MSVCRT_VERSION__
|
||||||
|
#define __MSVCRT_VERSION__ 0x0601
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//=========================
|
||||||
|
//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>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <io.h>
|
||||||
|
#include <direct.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#undef interface
|
||||||
|
#define dllexport __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.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 putenv _putenv
|
||||||
|
#define vsnprintf _vsnprintf
|
||||||
|
inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 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
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,337 @@
|
||||||
|
#ifndef NALL_PNG_HPP
|
||||||
|
#define NALL_PNG_HPP
|
||||||
|
|
||||||
|
//PNG image decoder
|
||||||
|
//author: byuu
|
||||||
|
|
||||||
|
#include <nall/inflate.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct png {
|
||||||
|
//colorType:
|
||||||
|
//0 = L
|
||||||
|
//2 = R,G,B
|
||||||
|
//3 = P
|
||||||
|
//4 = L,A
|
||||||
|
//6 = R,G,B,A
|
||||||
|
struct Info {
|
||||||
|
unsigned width;
|
||||||
|
unsigned height;
|
||||||
|
unsigned bitDepth;
|
||||||
|
unsigned colorType;
|
||||||
|
unsigned compressionMethod;
|
||||||
|
unsigned filterType;
|
||||||
|
unsigned interlaceMethod;
|
||||||
|
|
||||||
|
unsigned bytesPerPixel;
|
||||||
|
unsigned pitch;
|
||||||
|
|
||||||
|
uint8_t palette[256][3];
|
||||||
|
} info;
|
||||||
|
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
|
||||||
|
inline bool decode(const string &filename);
|
||||||
|
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
|
||||||
|
inline unsigned readbits(const uint8_t *&data);
|
||||||
|
unsigned bitpos;
|
||||||
|
|
||||||
|
inline png();
|
||||||
|
inline ~png();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class FourCC : unsigned {
|
||||||
|
IHDR = 0x49484452,
|
||||||
|
PLTE = 0x504c5445,
|
||||||
|
IDAT = 0x49444154,
|
||||||
|
IEND = 0x49454e44,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline unsigned interlace(unsigned pass, unsigned index);
|
||||||
|
inline unsigned inflateSize();
|
||||||
|
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
|
||||||
|
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
|
||||||
|
inline unsigned read(const uint8_t *data, unsigned length);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool png::decode(const string &filename) {
|
||||||
|
if(auto memory = file::read(filename)) {
|
||||||
|
return decode(memory.data(), memory.size());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
||||||
|
if(sourceSize < 8) return false;
|
||||||
|
if(read(sourceData + 0, 4) != 0x89504e47) return false;
|
||||||
|
if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false;
|
||||||
|
|
||||||
|
uint8_t *compressedData = 0;
|
||||||
|
unsigned compressedSize = 0;
|
||||||
|
|
||||||
|
unsigned offset = 8;
|
||||||
|
while(offset < sourceSize) {
|
||||||
|
unsigned length = read(sourceData + offset + 0, 4);
|
||||||
|
unsigned fourCC = read(sourceData + offset + 4, 4);
|
||||||
|
unsigned checksum = read(sourceData + offset + 8 + length, 4);
|
||||||
|
|
||||||
|
if(fourCC == (unsigned)FourCC::IHDR) {
|
||||||
|
info.width = read(sourceData + offset + 8, 4);
|
||||||
|
info.height = read(sourceData + offset + 12, 4);
|
||||||
|
info.bitDepth = read(sourceData + offset + 16, 1);
|
||||||
|
info.colorType = read(sourceData + offset + 17, 1);
|
||||||
|
info.compressionMethod = read(sourceData + offset + 18, 1);
|
||||||
|
info.filterType = read(sourceData + offset + 19, 1);
|
||||||
|
info.interlaceMethod = read(sourceData + offset + 20, 1);
|
||||||
|
|
||||||
|
if(info.bitDepth == 0 || info.bitDepth > 16) return false;
|
||||||
|
if(info.bitDepth & (info.bitDepth - 1)) return false; //not a power of two
|
||||||
|
if(info.compressionMethod != 0) return false;
|
||||||
|
if(info.filterType != 0) return false;
|
||||||
|
if(info.interlaceMethod != 0 && info.interlaceMethod != 1) return false;
|
||||||
|
|
||||||
|
switch(info.colorType) {
|
||||||
|
case 0: info.bytesPerPixel = info.bitDepth * 1; break; //L
|
||||||
|
case 2: info.bytesPerPixel = info.bitDepth * 3; break; //R,G,B
|
||||||
|
case 3: info.bytesPerPixel = info.bitDepth * 1; break; //P
|
||||||
|
case 4: info.bytesPerPixel = info.bitDepth * 2; break; //L,A
|
||||||
|
case 6: info.bytesPerPixel = info.bitDepth * 4; break; //R,G,B,A
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.colorType == 2 || info.colorType == 4 || info.colorType == 6)
|
||||||
|
if(info.bitDepth != 8 && info.bitDepth != 16) return false;
|
||||||
|
if(info.colorType == 3 && info.bitDepth == 16) return false;
|
||||||
|
|
||||||
|
info.bytesPerPixel = (info.bytesPerPixel + 7) / 8;
|
||||||
|
info.pitch = (int)info.width * info.bytesPerPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fourCC == (unsigned)FourCC::PLTE) {
|
||||||
|
if(length % 3) return false;
|
||||||
|
for(unsigned n = 0, p = offset + 8; n < length / 3; n++) {
|
||||||
|
info.palette[n][0] = sourceData[p++];
|
||||||
|
info.palette[n][1] = sourceData[p++];
|
||||||
|
info.palette[n][2] = sourceData[p++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fourCC == (unsigned)FourCC::IDAT) {
|
||||||
|
compressedData = (uint8_t*)realloc(compressedData, compressedSize + length);
|
||||||
|
memcpy(compressedData + compressedSize, sourceData + offset + 8, length);
|
||||||
|
compressedSize += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fourCC == (unsigned)FourCC::IEND) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += 4 + 4 + length + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned interlacedSize = inflateSize();
|
||||||
|
uint8_t *interlacedData = new uint8_t[interlacedSize];
|
||||||
|
|
||||||
|
bool result = inflate(interlacedData, interlacedSize, compressedData + 2, compressedSize - 6);
|
||||||
|
delete[] compressedData;
|
||||||
|
|
||||||
|
if(result == false) {
|
||||||
|
delete[] interlacedData;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = info.width * info.height * info.bytesPerPixel;
|
||||||
|
data = new uint8_t[size];
|
||||||
|
|
||||||
|
if(info.interlaceMethod == 0) {
|
||||||
|
if(filter(data, interlacedData, info.width, info.height) == false) {
|
||||||
|
delete[] interlacedData;
|
||||||
|
delete[] data;
|
||||||
|
data = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const uint8_t *passData = interlacedData;
|
||||||
|
for(unsigned pass = 0; pass < 7; pass++) {
|
||||||
|
if(deinterlace(passData, pass) == false) {
|
||||||
|
delete[] interlacedData;
|
||||||
|
delete[] data;
|
||||||
|
data = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] interlacedData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned png::interlace(unsigned pass, unsigned index) {
|
||||||
|
static const unsigned data[7][4] = {
|
||||||
|
//x-distance, y-distance, x-origin, y-origin
|
||||||
|
{ 8, 8, 0, 0 },
|
||||||
|
{ 8, 8, 4, 0 },
|
||||||
|
{ 4, 8, 0, 4 },
|
||||||
|
{ 4, 4, 2, 0 },
|
||||||
|
{ 2, 4, 0, 2 },
|
||||||
|
{ 2, 2, 1, 0 },
|
||||||
|
{ 1, 2, 0, 1 },
|
||||||
|
};
|
||||||
|
return data[pass][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned png::inflateSize() {
|
||||||
|
if(info.interlaceMethod == 0) {
|
||||||
|
return info.width * info.height * info.bytesPerPixel + info.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned size = 0;
|
||||||
|
for(unsigned pass = 0; pass < 7; pass++) {
|
||||||
|
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
|
||||||
|
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
|
||||||
|
unsigned width = (info.width + (xd - xo - 1)) / xd;
|
||||||
|
unsigned height = (info.height + (yd - yo - 1)) / yd;
|
||||||
|
if(width == 0 || height == 0) continue;
|
||||||
|
size += width * height * info.bytesPerPixel + height;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
|
||||||
|
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
|
||||||
|
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
|
||||||
|
unsigned width = (info.width + (xd - xo - 1)) / xd;
|
||||||
|
unsigned height = (info.height + (yd - yo - 1)) / yd;
|
||||||
|
if(width == 0 || height == 0) return true;
|
||||||
|
|
||||||
|
unsigned outputSize = width * height * info.bytesPerPixel;
|
||||||
|
uint8_t *outputData = new uint8_t[outputSize];
|
||||||
|
bool result = filter(outputData, inputData, width, height);
|
||||||
|
|
||||||
|
const uint8_t *rd = outputData;
|
||||||
|
for(unsigned y = yo; y < info.height; y += yd) {
|
||||||
|
uint8_t *wr = data + y * info.pitch;
|
||||||
|
for(unsigned x = xo; x < info.width; x += xd) {
|
||||||
|
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
|
||||||
|
wr[x * info.bytesPerPixel + b] = *rd++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputData += outputSize + height;
|
||||||
|
delete[] outputData;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool png::filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height) {
|
||||||
|
uint8_t *wr = outputData;
|
||||||
|
const uint8_t *rd = inputData;
|
||||||
|
int bpp = info.bytesPerPixel, pitch = width * bpp;
|
||||||
|
for(int y = 0; y < height; y++) {
|
||||||
|
uint8_t filter = *rd++;
|
||||||
|
|
||||||
|
switch(filter) {
|
||||||
|
case 0x00: //None
|
||||||
|
for(int x = 0; x < pitch; x++) {
|
||||||
|
wr[x] = rd[x];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x01: //Subtract
|
||||||
|
for(int x = 0; x < pitch; x++) {
|
||||||
|
wr[x] = rd[x] + (x - bpp < 0 ? 0 : wr[x - bpp]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x02: //Above
|
||||||
|
for(int x = 0; x < pitch; x++) {
|
||||||
|
wr[x] = rd[x] + (y - 1 < 0 ? 0 : wr[x - pitch]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x03: //Average
|
||||||
|
for(int x = 0; x < pitch; x++) {
|
||||||
|
short a = x - bpp < 0 ? 0 : wr[x - bpp];
|
||||||
|
short b = y - 1 < 0 ? 0 : wr[x - pitch];
|
||||||
|
|
||||||
|
wr[x] = rd[x] + (uint8_t)((a + b) / 2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x04: //Paeth
|
||||||
|
for(int x = 0; x < pitch; x++) {
|
||||||
|
short a = x - bpp < 0 ? 0 : wr[x - bpp];
|
||||||
|
short b = y - 1 < 0 ? 0 : wr[x - pitch];
|
||||||
|
short c = x - bpp < 0 || y - 1 < 0 ? 0 : wr[x - pitch - bpp];
|
||||||
|
|
||||||
|
short p = a + b - c;
|
||||||
|
short pa = p > a ? p - a : a - p;
|
||||||
|
short pb = p > b ? p - b : b - p;
|
||||||
|
short pc = p > c ? p - c : c - p;
|
||||||
|
|
||||||
|
uint8_t paeth = (uint8_t)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
|
||||||
|
|
||||||
|
wr[x] = rd[x] + paeth;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: //Invalid
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rd += pitch;
|
||||||
|
wr += pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned png::read(const uint8_t *data, unsigned length) {
|
||||||
|
unsigned result = 0;
|
||||||
|
while(length--) result = (result << 8) | (*data++);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned png::readbits(const uint8_t *&data) {
|
||||||
|
unsigned result = 0;
|
||||||
|
switch(info.bitDepth) {
|
||||||
|
case 1:
|
||||||
|
result = (*data >> bitpos) & 1;
|
||||||
|
bitpos++;
|
||||||
|
if(bitpos == 8) { data++; bitpos = 0; }
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
result = (*data >> bitpos) & 3;
|
||||||
|
bitpos += 2;
|
||||||
|
if(bitpos == 8) { data++; bitpos = 0; }
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
result = (*data >> bitpos) & 15;
|
||||||
|
bitpos += 4;
|
||||||
|
if(bitpos == 8) { data++; bitpos = 0; }
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
result = *data++;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
result = (data[0] << 8) | (data[1] << 0);
|
||||||
|
data += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
png::png() : data(nullptr) {
|
||||||
|
bitpos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
png::~png() {
|
||||||
|
if(data) delete[] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,109 @@
|
||||||
|
#ifndef NALL_PRIORITY_QUEUE_HPP
|
||||||
|
#define NALL_PRIORITY_QUEUE_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) append (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 of 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,32 @@
|
||||||
|
#ifndef NALL_PUBLIC_CAST_HPP
|
||||||
|
#define NALL_PUBLIC_CAST_HPP
|
||||||
|
|
||||||
|
//this is a proof-of-concept-*only* C++ access-privilege elevation exploit.
|
||||||
|
//this code is 100% legal C++, per C++98 section 14.7.2 paragraph 8:
|
||||||
|
//"access checking rules do not apply to names in explicit instantiations."
|
||||||
|
//usage example:
|
||||||
|
|
||||||
|
//struct N { typedef void (Class::*)(); };
|
||||||
|
//template class public_cast<N, &Class::Reference>;
|
||||||
|
//(class.*public_cast<N>::value);
|
||||||
|
|
||||||
|
//Class::Reference may be public, protected or private
|
||||||
|
//Class::Reference may be a function, object or variable
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
template<typename T, typename T::type... P> struct public_cast;
|
||||||
|
|
||||||
|
template<typename T> struct public_cast<T> {
|
||||||
|
static typename T::type value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> typename T::type public_cast<T>::value;
|
||||||
|
|
||||||
|
template<typename T, typename T::type P> struct public_cast<T, P> {
|
||||||
|
static typename T::type value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename T::type P> typename T::type public_cast<T, P>::value = public_cast<T>::value = P;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
#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_lfsr {
|
||||||
|
inline void seed(unsigned seed__) {
|
||||||
|
seed_ = seed__;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned operator()() {
|
||||||
|
return seed_ = (seed_ >> 1) ^ (((seed_ & 1) - 1) & 0xedb88320);
|
||||||
|
}
|
||||||
|
|
||||||
|
random_lfsr() : seed_(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned seed_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,110 @@
|
||||||
|
#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 {
|
||||||
|
struct serial {
|
||||||
|
bool readable() {
|
||||||
|
if(port_open == false) return false;
|
||||||
|
fd_set fdset;
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(port, &fdset);
|
||||||
|
timeval timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
int result = select(FD_SETSIZE, &fdset, nullptr, nullptr, &timeout);
|
||||||
|
if(result < 1) return false;
|
||||||
|
return FD_ISSET(port, &fdset);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writable() {
|
||||||
|
if(port_open == false) return false;
|
||||||
|
fd_set fdset;
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(port, &fdset);
|
||||||
|
timeval timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
int result = select(FD_SETSIZE, nullptr, &fdset, nullptr, &timeout);
|
||||||
|
if(result < 1) return false;
|
||||||
|
return FD_ISSET(port, &fdset);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-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++] = (uintmax_t)value >> (n << 3);
|
||||||
|
} else if(imode == Load) {
|
||||||
|
value = 0;
|
||||||
|
for(unsigned n = 0; n < size; n++) value |= (uintmax_t)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,158 @@
|
||||||
|
#ifndef NALL_SET_HPP
|
||||||
|
#define NALL_SET_HPP
|
||||||
|
|
||||||
|
//set
|
||||||
|
//* unordered
|
||||||
|
//* intended for unique items
|
||||||
|
//* dynamic growth
|
||||||
|
//* reference-based variant
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <utility>
|
||||||
|
#include <nall/algorithm.hpp>
|
||||||
|
#include <nall/bit.hpp>
|
||||||
|
#include <nall/sort.hpp>
|
||||||
|
#include <nall/traits.hpp>
|
||||||
|
#include <nall/utility.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
template<typename T, typename Enable = void> struct set;
|
||||||
|
|
||||||
|
template<typename T> struct set<T, typename std::enable_if<!std::is_reference<T>::value>::type> {
|
||||||
|
struct exception_out_of_bounds{};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
T *pool;
|
||||||
|
unsigned poolsize, objectsize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
unsigned size() const { return objectsize; }
|
||||||
|
unsigned capacity() const { return poolsize; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//reference set
|
||||||
|
template<typename TR> struct set<TR, typename std::enable_if<std::is_reference<TR>::value>::type> {
|
||||||
|
struct exception_out_of_bounds{};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef typename std::remove_reference<TR>::type T;
|
||||||
|
T **pool;
|
||||||
|
unsigned poolsize, objectsize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
unsigned size() const { return objectsize; }
|
||||||
|
unsigned capacity() const { return poolsize; }
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
if(pool) free(pool);
|
||||||
|
pool = nullptr;
|
||||||
|
poolsize = 0;
|
||||||
|
objectsize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(unsigned size) {
|
||||||
|
if(size == poolsize) return;
|
||||||
|
pool = (T**)realloc(pool, sizeof(T*) * size);
|
||||||
|
poolsize = size;
|
||||||
|
objectsize = min(objectsize, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(unsigned size) {
|
||||||
|
if(size > poolsize) reserve(bit::round(size)); //amortize growth
|
||||||
|
objectsize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool append(T& data) {
|
||||||
|
if(find(data)) return false;
|
||||||
|
unsigned offset = objectsize++;
|
||||||
|
if(offset >= poolsize) resize(offset + 1);
|
||||||
|
pool[offset] = &data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
bool append(T& data, Args&&... args) {
|
||||||
|
bool result = append(data);
|
||||||
|
append(std::forward<Args>(args)...);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove(T& data) {
|
||||||
|
if(auto position = find(data)) {
|
||||||
|
for(signed i = position(); i < objectsize - 1; i++) pool[i] = pool[i + 1];
|
||||||
|
resize(objectsize - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<unsigned> find(const T& data) {
|
||||||
|
for(unsigned n = 0; n < objectsize; n++) if(pool[n] == &data) return {true, n};
|
||||||
|
return {false, 0u};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> set(Args&&... args) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||||
|
construct(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
~set() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
set& operator=(const set &source) {
|
||||||
|
if(&source == this) return *this;
|
||||||
|
if(pool) free(pool);
|
||||||
|
objectsize = source.objectsize;
|
||||||
|
poolsize = source.poolsize;
|
||||||
|
pool = (T**)malloc(sizeof(T*) * poolsize);
|
||||||
|
memcpy(pool, source.pool, sizeof(T*) * objectsize);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
set& operator=(const set &&source) {
|
||||||
|
if(&source == this) return *this;
|
||||||
|
if(pool) free(pool);
|
||||||
|
pool = source.pool;
|
||||||
|
poolsize = source.poolsize;
|
||||||
|
objectsize = source.objectsize;
|
||||||
|
source.pool = nullptr;
|
||||||
|
source.reset();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator[](unsigned position) const {
|
||||||
|
if(position >= objectsize) throw exception_out_of_bounds();
|
||||||
|
return *pool[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
bool operator!=(const iterator &source) const { return position != source.position; }
|
||||||
|
T& operator*() { return source.operator[](position); }
|
||||||
|
iterator& operator++() { position++; return *this; }
|
||||||
|
iterator(const set &source, unsigned position) : source(source), position(position) {}
|
||||||
|
private:
|
||||||
|
const set &source;
|
||||||
|
unsigned position;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() { return iterator(*this, 0); }
|
||||||
|
iterator end() { return iterator(*this, objectsize); }
|
||||||
|
const iterator begin() const { return iterator(*this, 0); }
|
||||||
|
const iterator end() const { return iterator(*this, objectsize); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void construct() {}
|
||||||
|
void construct(const set &source) { operator=(source); }
|
||||||
|
void construct(const set &&source) { operator=(std::move(source)); }
|
||||||
|
template<typename... Args> void construct(T& data, Args&&... args) {
|
||||||
|
append(data);
|
||||||
|
construct(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,145 @@
|
||||||
|
#ifndef NALL_SHA256_HPP
|
||||||
|
#define NALL_SHA256_HPP
|
||||||
|
|
||||||
|
//author: vladitx
|
||||||
|
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline 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,77 @@
|
||||||
|
#ifndef NALL_SORT_HPP
|
||||||
|
#define NALL_SORT_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#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
|
||||||
|
|
||||||
|
//note: merge sort was chosen over quick sort, because:
|
||||||
|
//* it is a stable sort
|
||||||
|
//* it lacks O(n^2) worst-case overhead
|
||||||
|
|
||||||
|
#define NALL_SORT_INSERTION
|
||||||
|
//#define NALL_SORT_SELECTION
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
template<typename T, typename Comparator>
|
||||||
|
void sort(T list[], unsigned size, const Comparator &lessthan) {
|
||||||
|
if(size <= 1) return; //nothing to sort
|
||||||
|
|
||||||
|
//use insertion sort to quickly sort smaller blocks
|
||||||
|
if(size < 64) {
|
||||||
|
#if defined(NALL_SORT_INSERTION)
|
||||||
|
for(signed i = 1, j; i < size; i++) {
|
||||||
|
T copy = std::move(list[i]);
|
||||||
|
for(j = i - 1; j >= 0; j--) {
|
||||||
|
if(lessthan(list[j], copy)) break;
|
||||||
|
list[j + 1] = std::move(list[j]);
|
||||||
|
}
|
||||||
|
list[j + 1] = std::move(copy);
|
||||||
|
}
|
||||||
|
#elif defined(NALL_SORT_SELECTION)
|
||||||
|
for(unsigned i = 0; i < size; i++) {
|
||||||
|
unsigned min = i;
|
||||||
|
for(unsigned j = i + 1; j < size; j++) {
|
||||||
|
if(lessthan(list[j], list[min])) min = j;
|
||||||
|
}
|
||||||
|
if(min != i) std::swap(list[i], list[min]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//split list in half and recursively sort both
|
||||||
|
unsigned middle = size / 2;
|
||||||
|
sort(list, middle, lessthan);
|
||||||
|
sort(list + middle, size - middle, lessthan);
|
||||||
|
|
||||||
|
//left and right are sorted here; perform merge sort
|
||||||
|
T *buffer = new T[size];
|
||||||
|
unsigned offset = 0, left = 0, right = middle;
|
||||||
|
while(left < middle && right < size) {
|
||||||
|
if(lessthan(list[left], list[right])) {
|
||||||
|
buffer[offset++] = std::move(list[left++]);
|
||||||
|
} else {
|
||||||
|
buffer[offset++] = std::move(list[right++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(left < middle) buffer[offset++] = std::move(list[left++]);
|
||||||
|
while(right < size) buffer[offset++] = std::move(list[right++]);
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < size; i++) list[i] = std::move(buffer[i]);
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void sort(T list[], unsigned size) {
|
||||||
|
return sort(list, size, [](const T &l, const T &r) { return l < r; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef NALL_STDINT_HPP
|
||||||
|
#define NALL_STDINT_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,26 @@
|
||||||
|
#ifndef NALL_STREAM_HPP
|
||||||
|
#define NALL_STREAM_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/gzip.hpp>
|
||||||
|
#include <nall/http.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/zip.hpp>
|
||||||
|
|
||||||
|
#define NALL_STREAM_INTERNAL_HPP
|
||||||
|
#include <nall/stream/stream.hpp>
|
||||||
|
#include <nall/stream/memory.hpp>
|
||||||
|
#include <nall/stream/mmap.hpp>
|
||||||
|
#include <nall/stream/file.hpp>
|
||||||
|
#include <nall/stream/http.hpp>
|
||||||
|
#include <nall/stream/gzip.hpp>
|
||||||
|
#include <nall/stream/zip.hpp>
|
||||||
|
#include <nall/stream/auto.hpp>
|
||||||
|
#undef NALL_STREAM_INTERNAL_HPP
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef NALL_STREAM_AUTO_HPP
|
||||||
|
#define NALL_STREAM_AUTO_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
#define autostream(...) (*makestream(__VA_ARGS__))
|
||||||
|
|
||||||
|
inline std::unique_ptr<stream> makestream(const string &path) {
|
||||||
|
if(path.ibeginswith("http://")) return std::unique_ptr<stream>(new httpstream(path, 80));
|
||||||
|
if(path.iendswith(".gz")) return std::unique_ptr<stream>(new gzipstream(filestream{path}));
|
||||||
|
if(path.iendswith(".zip")) return std::unique_ptr<stream>(new zipstream(filestream{path}));
|
||||||
|
return std::unique_ptr<stream>(new mmapstream(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::unique_ptr<stream> makestream(uint8_t *data, unsigned size) {
|
||||||
|
return std::unique_ptr<stream>(new memorystream(data, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::unique_ptr<stream> makestream(const uint8_t *data, unsigned size) {
|
||||||
|
return std::unique_ptr<stream>(new memorystream(data, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef NALL_STREAM_FILE_HPP
|
||||||
|
#define NALL_STREAM_FILE_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct filestream : stream {
|
||||||
|
using stream::read;
|
||||||
|
using stream::write;
|
||||||
|
|
||||||
|
bool seekable() const { return true; }
|
||||||
|
bool readable() const { return true; }
|
||||||
|
bool writable() const { return pwritable; }
|
||||||
|
bool randomaccess() const { return false; }
|
||||||
|
|
||||||
|
unsigned size() const { return pfile.size(); }
|
||||||
|
unsigned offset() const { return pfile.offset(); }
|
||||||
|
void seek(unsigned offset) const { pfile.seek(offset); }
|
||||||
|
|
||||||
|
uint8_t read() const { return pfile.read(); }
|
||||||
|
void write(uint8_t data) const { pfile.write(data); }
|
||||||
|
|
||||||
|
filestream(const string &filename) {
|
||||||
|
pfile.open(filename, file::mode::readwrite);
|
||||||
|
pwritable = pfile.open();
|
||||||
|
if(!pwritable) pfile.open(filename, file::mode::read);
|
||||||
|
}
|
||||||
|
|
||||||
|
filestream(const string &filename, file::mode mode) {
|
||||||
|
pfile.open(filename, mode);
|
||||||
|
pwritable = mode == file::mode::write || mode == file::mode::readwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable file pfile;
|
||||||
|
bool pwritable;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef NALL_STREAM_GZIP_HPP
|
||||||
|
#define NALL_STREAM_GZIP_HPP
|
||||||
|
|
||||||
|
#include <nall/gzip.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct gzipstream : memorystream {
|
||||||
|
using stream::read;
|
||||||
|
using stream::write;
|
||||||
|
|
||||||
|
gzipstream(const stream &stream) {
|
||||||
|
unsigned size = stream.size();
|
||||||
|
uint8_t *data = new uint8_t[size];
|
||||||
|
stream.read(data, size);
|
||||||
|
|
||||||
|
gzip archive;
|
||||||
|
bool result = archive.decompress(data, size);
|
||||||
|
delete[] data;
|
||||||
|
if(result == false) return;
|
||||||
|
|
||||||
|
psize = archive.size;
|
||||||
|
pdata = new uint8_t[psize];
|
||||||
|
memcpy(pdata, archive.data, psize);
|
||||||
|
}
|
||||||
|
|
||||||
|
~gzipstream() {
|
||||||
|
if(pdata) delete[] pdata;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef NALL_STREAM_HTTP_HPP
|
||||||
|
#define NALL_STREAM_HTTP_HPP
|
||||||
|
|
||||||
|
#include <nall/http.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct httpstream : stream {
|
||||||
|
using stream::read;
|
||||||
|
using stream::write;
|
||||||
|
|
||||||
|
bool seekable() const { return true; }
|
||||||
|
bool readable() const { return true; }
|
||||||
|
bool writable() const { return true; }
|
||||||
|
bool randomaccess() const { return true; }
|
||||||
|
|
||||||
|
unsigned size() const { return psize; }
|
||||||
|
unsigned offset() const { return poffset; }
|
||||||
|
void seek(unsigned offset) const { poffset = offset; }
|
||||||
|
|
||||||
|
uint8_t read() const { return pdata[poffset++]; }
|
||||||
|
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||||
|
|
||||||
|
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||||
|
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||||
|
|
||||||
|
httpstream(const string &url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
|
||||||
|
string uri = url;
|
||||||
|
uri.ltrim<1>("http://");
|
||||||
|
lstring part = uri.split<1>("/");
|
||||||
|
part[1] = { "/", part[1] };
|
||||||
|
|
||||||
|
http connection;
|
||||||
|
if(connection.connect(part[0], port) == false) return;
|
||||||
|
connection.download(part[1], pdata, psize);
|
||||||
|
}
|
||||||
|
|
||||||
|
~httpstream() {
|
||||||
|
if(pdata) delete[] pdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable uint8_t *pdata;
|
||||||
|
mutable unsigned psize, poffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef NALL_STREAM_MEMORY_HPP
|
||||||
|
#define NALL_STREAM_MEMORY_HPP
|
||||||
|
|
||||||
|
#include <nall/stream/stream.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct memorystream : stream {
|
||||||
|
using stream::read;
|
||||||
|
using stream::write;
|
||||||
|
|
||||||
|
bool seekable() const { return true; }
|
||||||
|
bool readable() const { return true; }
|
||||||
|
bool writable() const { return pwritable; }
|
||||||
|
bool randomaccess() const { return true; }
|
||||||
|
|
||||||
|
uint8_t *data() const { return pdata; }
|
||||||
|
unsigned size() const { return psize; }
|
||||||
|
unsigned offset() const { return poffset; }
|
||||||
|
void seek(unsigned offset) const { poffset = offset; }
|
||||||
|
|
||||||
|
uint8_t read() const { return pdata[poffset++]; }
|
||||||
|
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||||
|
|
||||||
|
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||||
|
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||||
|
|
||||||
|
memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {}
|
||||||
|
|
||||||
|
memorystream(uint8_t *data, unsigned size) {
|
||||||
|
pdata = data, psize = size, poffset = 0;
|
||||||
|
pwritable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memorystream(const uint8_t *data, unsigned size) {
|
||||||
|
pdata = (uint8_t*)data, psize = size, poffset = 0;
|
||||||
|
pwritable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable uint8_t *pdata;
|
||||||
|
mutable unsigned psize, poffset, pwritable;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef NALL_STREAM_MMAP_HPP
|
||||||
|
#define NALL_STREAM_MMAP_HPP
|
||||||
|
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct mmapstream : stream {
|
||||||
|
using stream::read;
|
||||||
|
using stream::write;
|
||||||
|
|
||||||
|
bool seekable() const { return true; }
|
||||||
|
bool readable() const { return true; }
|
||||||
|
bool writable() const { return pwritable; }
|
||||||
|
bool randomaccess() const { return true; }
|
||||||
|
|
||||||
|
unsigned size() const { return pmmap.size(); }
|
||||||
|
unsigned offset() const { return poffset; }
|
||||||
|
void seek(unsigned offset) const { poffset = offset; }
|
||||||
|
|
||||||
|
uint8_t read() const { return pdata[poffset++]; }
|
||||||
|
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||||
|
|
||||||
|
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||||
|
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||||
|
|
||||||
|
mmapstream(const string &filename) {
|
||||||
|
pmmap.open(filename, filemap::mode::readwrite);
|
||||||
|
pwritable = pmmap.open();
|
||||||
|
if(!pwritable) pmmap.open(filename, filemap::mode::read);
|
||||||
|
pdata = pmmap.data(), poffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable filemap pmmap;
|
||||||
|
mutable uint8_t *pdata;
|
||||||
|
mutable unsigned pwritable, poffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,92 @@
|
||||||
|
#ifndef NALL_STREAM_STREAM_HPP
|
||||||
|
#define NALL_STREAM_STREAM_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct stream {
|
||||||
|
virtual bool seekable() const = 0;
|
||||||
|
virtual bool readable() const = 0;
|
||||||
|
virtual bool writable() const = 0;
|
||||||
|
virtual bool randomaccess() const = 0;
|
||||||
|
|
||||||
|
virtual uint8_t* data() const { return nullptr; }
|
||||||
|
virtual unsigned size() const = 0;
|
||||||
|
virtual unsigned offset() const = 0;
|
||||||
|
virtual void seek(unsigned offset) const = 0;
|
||||||
|
|
||||||
|
virtual uint8_t read() const = 0;
|
||||||
|
virtual void write(uint8_t data) const = 0;
|
||||||
|
|
||||||
|
virtual uint8_t read(unsigned) const { return 0; }
|
||||||
|
virtual void write(unsigned, uint8_t) const {}
|
||||||
|
|
||||||
|
operator bool() const {
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool end() const {
|
||||||
|
return offset() >= size();
|
||||||
|
}
|
||||||
|
|
||||||
|
uintmax_t readl(unsigned length = 1) const {
|
||||||
|
uintmax_t data = 0, shift = 0;
|
||||||
|
while(length--) { data |= read() << shift; shift += 8; }
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintmax_t readm(unsigned length = 1) const {
|
||||||
|
uintmax_t data = 0;
|
||||||
|
while(length--) data = (data << 8) | read();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read(uint8_t *data, unsigned length) const {
|
||||||
|
while(length--) *data++ = read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void writel(uintmax_t data, unsigned length = 1) const {
|
||||||
|
while(length--) {
|
||||||
|
write(data);
|
||||||
|
data >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void writem(uintmax_t data, unsigned length = 1) const {
|
||||||
|
uintmax_t shift = 8 * length;
|
||||||
|
while(length--) {
|
||||||
|
shift -= 8;
|
||||||
|
write(data >> shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const uint8_t *data, unsigned length) const {
|
||||||
|
while(length--) write(*data++);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct byte {
|
||||||
|
operator uint8_t() const { return s.read(offset); }
|
||||||
|
byte& operator=(uint8_t data) { s.write(offset, data); return *this; }
|
||||||
|
byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const stream &s;
|
||||||
|
const unsigned offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
byte operator[](unsigned offset) const {
|
||||||
|
return byte(*this, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream() {}
|
||||||
|
virtual ~stream() {}
|
||||||
|
stream(const stream&) = delete;
|
||||||
|
stream& operator=(const stream&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef NALL_STREAM_VECTOR_HPP
|
||||||
|
#define NALL_STREAM_VECTOR_HPP
|
||||||
|
|
||||||
|
#include <nall/stream/stream.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct vectorstream : stream {
|
||||||
|
using stream::read;
|
||||||
|
using stream::write;
|
||||||
|
|
||||||
|
bool seekable() const { return true; }
|
||||||
|
bool readable() const { return true; }
|
||||||
|
bool writable() const { return pwritable; }
|
||||||
|
bool randomaccess() const { return true; }
|
||||||
|
|
||||||
|
uint8_t* data() const { return memory.data(); }
|
||||||
|
unsigned size() const { return memory.size(); }
|
||||||
|
unsigned offset() const { return poffset; }
|
||||||
|
void seek(unsigned offset) const { poffset = offset; }
|
||||||
|
|
||||||
|
uint8_t read() const { return memory[poffset++]; }
|
||||||
|
void write(uint8_t data) const { memory[poffset++] = data; }
|
||||||
|
|
||||||
|
uint8_t read(unsigned offset) const { return memory[offset]; }
|
||||||
|
void write(unsigned offset, uint8_t data) const { memory[offset] = data; }
|
||||||
|
|
||||||
|
vectorstream(vector<uint8_t> &memory) : memory(memory), poffset(0), pwritable(true) {}
|
||||||
|
vectorstream(const vector<uint8_t> &memory) : memory((vector<uint8_t>&)memory), poffset(0), pwritable(false) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
vector<uint8_t> &memory;
|
||||||
|
mutable unsigned poffset, pwritable;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef NALL_STREAM_ZIP_HPP
|
||||||
|
#define NALL_STREAM_ZIP_HPP
|
||||||
|
|
||||||
|
#include <nall/unzip.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct zipstream : memorystream {
|
||||||
|
using stream::read;
|
||||||
|
using stream::write;
|
||||||
|
|
||||||
|
zipstream(const stream &stream, const string &filter = "*") {
|
||||||
|
unsigned size = stream.size();
|
||||||
|
uint8_t *data = new uint8_t[size];
|
||||||
|
stream.read(data, size);
|
||||||
|
|
||||||
|
unzip archive;
|
||||||
|
if(archive.open(data, size) == false) return;
|
||||||
|
delete[] data;
|
||||||
|
|
||||||
|
for(auto &file : archive.file) {
|
||||||
|
if(file.name.wildcard(filter)) {
|
||||||
|
auto buffer = archive.extract(file);
|
||||||
|
psize = buffer.size();
|
||||||
|
pdata = buffer.move();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~zipstream() {
|
||||||
|
if(pdata) delete[] pdata;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef NALL_STRING_HPP
|
||||||
|
#define NALL_STRING_HPP
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#include <nall/atoi.hpp>
|
||||||
|
#include <nall/function.hpp>
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/sha256.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/utility.hpp>
|
||||||
|
#include <nall/varint.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
|
||||||
|
#define NALL_STRING_INTERNAL_HPP
|
||||||
|
#include <nall/string/base.hpp>
|
||||||
|
#include <nall/string/bsv.hpp>
|
||||||
|
#include <nall/string/cast.hpp>
|
||||||
|
#include <nall/string/compare.hpp>
|
||||||
|
#include <nall/string/convert.hpp>
|
||||||
|
#include <nall/string/core.hpp>
|
||||||
|
#include <nall/string/cstring.hpp>
|
||||||
|
#include <nall/string/datetime.hpp>
|
||||||
|
#include <nall/string/filename.hpp>
|
||||||
|
#include <nall/string/math-fixed-point.hpp>
|
||||||
|
#include <nall/string/math-floating-point.hpp>
|
||||||
|
#include <nall/string/platform.hpp>
|
||||||
|
#include <nall/string/strm.hpp>
|
||||||
|
#include <nall/string/strpos.hpp>
|
||||||
|
#include <nall/string/trim.hpp>
|
||||||
|
#include <nall/string/replace.hpp>
|
||||||
|
#include <nall/string/split.hpp>
|
||||||
|
#include <nall/string/static.hpp>
|
||||||
|
#include <nall/string/utf8.hpp>
|
||||||
|
#include <nall/string/utility.hpp>
|
||||||
|
#include <nall/string/variadic.hpp>
|
||||||
|
#include <nall/string/wildcard.hpp>
|
||||||
|
#include <nall/string/wrapper.hpp>
|
||||||
|
#include <nall/string/markup/node.hpp>
|
||||||
|
#include <nall/string/markup/bml.hpp>
|
||||||
|
#include <nall/string/markup/xml.hpp>
|
||||||
|
#include <nall/string/markup/document.hpp>
|
||||||
|
#undef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,219 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
struct cstring;
|
||||||
|
struct string;
|
||||||
|
struct lstring;
|
||||||
|
template<typename T> inline const char* to_string(T);
|
||||||
|
|
||||||
|
struct cstring {
|
||||||
|
inline operator const char*() const;
|
||||||
|
inline unsigned length() const;
|
||||||
|
inline bool operator==(const char*) const;
|
||||||
|
inline bool operator!=(const char*) const;
|
||||||
|
inline optional<unsigned> position(const char *key) const;
|
||||||
|
inline optional<unsigned> iposition(const char *key) const;
|
||||||
|
inline cstring& operator=(const char *data);
|
||||||
|
inline cstring(const char *data);
|
||||||
|
inline cstring();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const char *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string {
|
||||||
|
inline static string read(const string &filename);
|
||||||
|
inline static string date();
|
||||||
|
inline static string time();
|
||||||
|
inline static string datetime();
|
||||||
|
|
||||||
|
inline void reserve(unsigned);
|
||||||
|
inline bool empty() const;
|
||||||
|
|
||||||
|
template<typename... Args> inline string& assign(Args&&... args);
|
||||||
|
template<typename... Args> inline string& append(Args&&... args);
|
||||||
|
|
||||||
|
inline bool readfile(const string&);
|
||||||
|
|
||||||
|
template<unsigned Limit = 0> inline string& replace(const char*, const char*);
|
||||||
|
template<unsigned Limit = 0> inline string& ireplace(const char*, const char*);
|
||||||
|
template<unsigned Limit = 0> inline string& qreplace(const char*, const char*);
|
||||||
|
template<unsigned Limit = 0> inline string& iqreplace(const char*, const char*);
|
||||||
|
|
||||||
|
inline unsigned length() const;
|
||||||
|
inline unsigned capacity() const;
|
||||||
|
|
||||||
|
template<unsigned Limit = 0> inline lstring split(const char*) const;
|
||||||
|
template<unsigned Limit = 0> inline lstring isplit(const char*) const;
|
||||||
|
template<unsigned Limit = 0> inline lstring qsplit(const char*) const;
|
||||||
|
template<unsigned Limit = 0> inline lstring iqsplit(const char*) 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& qlower();
|
||||||
|
inline string& qupper();
|
||||||
|
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 = " ", const char *rkey = 0);
|
||||||
|
inline string& strip();
|
||||||
|
|
||||||
|
inline optional<unsigned> position(const char *key) const;
|
||||||
|
inline optional<unsigned> iposition(const char *key) const;
|
||||||
|
inline optional<unsigned> qposition(const char *key) const;
|
||||||
|
inline optional<unsigned> iqposition(const char *key) const;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
inline char* begin() { return &data[0]; }
|
||||||
|
inline char* end() { return &data[length()]; }
|
||||||
|
inline const char* begin() const { return &data[0]; }
|
||||||
|
inline const char* end() const { return &data[length()]; }
|
||||||
|
|
||||||
|
//internal functions
|
||||||
|
inline string& assign_(const char*);
|
||||||
|
inline string& append_(const char*);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
char *data;
|
||||||
|
unsigned size;
|
||||||
|
|
||||||
|
template<unsigned Limit, bool Insensitive, bool Quoted> inline string& ureplace(const char*, const char*);
|
||||||
|
|
||||||
|
#if defined(QSTRING_H)
|
||||||
|
public:
|
||||||
|
inline operator QString() const;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lstring : vector<string> {
|
||||||
|
inline optional<unsigned> find(const char*) const;
|
||||||
|
inline string concatenate(const char*) const;
|
||||||
|
inline void append() {}
|
||||||
|
template<typename... Args> inline void append(const string&, Args&&...);
|
||||||
|
|
||||||
|
template<unsigned Limit = 0> inline lstring& split(const char*, const char*);
|
||||||
|
template<unsigned Limit = 0> inline lstring& isplit(const char*, const char*);
|
||||||
|
template<unsigned Limit = 0> inline lstring& qsplit(const char*, const char*);
|
||||||
|
template<unsigned Limit = 0> inline lstring& iqsplit(const char*, const char*);
|
||||||
|
|
||||||
|
inline bool operator==(const lstring&) const;
|
||||||
|
inline bool operator!=(const lstring&) const;
|
||||||
|
|
||||||
|
inline lstring& operator=(const lstring&);
|
||||||
|
inline lstring& operator=(lstring&);
|
||||||
|
inline lstring& operator=(lstring&&);
|
||||||
|
|
||||||
|
template<typename... Args> inline lstring(Args&&... args);
|
||||||
|
inline lstring(const lstring&);
|
||||||
|
inline lstring(lstring&);
|
||||||
|
inline lstring(lstring&&);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<unsigned Limit, bool Insensitive, bool Quoted> inline lstring& usplit(const char*, const char*);
|
||||||
|
};
|
||||||
|
|
||||||
|
//compare.hpp
|
||||||
|
inline char chrlower(char c);
|
||||||
|
inline char chrupper(char c);
|
||||||
|
inline int istrcmp(const char *str1, const char *str2);
|
||||||
|
inline bool strbegin(const char *str, const char *key);
|
||||||
|
inline bool istrbegin(const char *str, const char *key);
|
||||||
|
inline bool strend(const char *str, const char *key);
|
||||||
|
inline bool istrend(const char *str, const char *key);
|
||||||
|
|
||||||
|
//convert.hpp
|
||||||
|
inline char* strlower(char *str);
|
||||||
|
inline char* strupper(char *str);
|
||||||
|
inline char* qstrlower(char *str);
|
||||||
|
inline char* qstrupper(char *str);
|
||||||
|
inline char* strtr(char *dest, const char *before, const char *after);
|
||||||
|
|
||||||
|
//math.hpp
|
||||||
|
inline bool strint(const char *str, int &result);
|
||||||
|
inline bool strmath(const char *str, int &result);
|
||||||
|
|
||||||
|
//platform.hpp
|
||||||
|
inline string activepath();
|
||||||
|
inline string realpath(const string &name);
|
||||||
|
inline string userpath();
|
||||||
|
inline string configpath();
|
||||||
|
inline string temppath();
|
||||||
|
|
||||||
|
//strm.hpp
|
||||||
|
inline unsigned strmcpy(char *target, const char *source, unsigned length);
|
||||||
|
inline unsigned strmcat(char *target, const char *source, unsigned length);
|
||||||
|
inline bool strccpy(char *target, const char *source, unsigned length);
|
||||||
|
inline bool strccat(char *target, const char *source, unsigned length);
|
||||||
|
inline void strpcpy(char *&target, const char *source, unsigned &length);
|
||||||
|
|
||||||
|
//strpos.hpp
|
||||||
|
inline optional<unsigned> strpos(const char *str, const char *key);
|
||||||
|
inline optional<unsigned> istrpos(const char *str, const char *key);
|
||||||
|
inline optional<unsigned> qstrpos(const char *str, const char *key);
|
||||||
|
inline optional<unsigned> iqstrpos(const char *str, const char *key);
|
||||||
|
template<bool Insensitive = false, bool Quoted = false> inline optional<unsigned> ustrpos(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 = " ", const char *rkey = 0);
|
||||||
|
inline char* strip(char *s);
|
||||||
|
|
||||||
|
//utility.hpp
|
||||||
|
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
|
||||||
|
template<bool Quoted, typename T> alwaysinline bool quoteskip(T *&p);
|
||||||
|
template<bool Quoted, typename T> alwaysinline bool quotecopy(char *&t, T *&p);
|
||||||
|
inline string substr(const char *src, unsigned start = 0, unsigned length = ~0u);
|
||||||
|
inline string sha256(const uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
inline char* integer(char *result, intmax_t value);
|
||||||
|
inline char* decimal(char *result, uintmax_t value);
|
||||||
|
|
||||||
|
template<unsigned length = 0, char padding = ' '> inline string integer(intmax_t value);
|
||||||
|
template<unsigned length = 0, char padding = ' '> inline string linteger(intmax_t value);
|
||||||
|
template<unsigned length = 0, char padding = ' '> inline string decimal(uintmax_t value);
|
||||||
|
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
|
||||||
|
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
|
||||||
|
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
|
||||||
|
inline unsigned fp(char *str, long double value);
|
||||||
|
inline string fp(long double value);
|
||||||
|
|
||||||
|
//variadic.hpp
|
||||||
|
template<typename... Args> inline void print(Args&&... args);
|
||||||
|
|
||||||
|
//wildcard.hpp
|
||||||
|
inline bool wildcard(const char *str, const char *pattern);
|
||||||
|
inline bool iwildcard(const char *str, const char *pattern);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,76 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
//BSV v1.0 parser
|
||||||
|
//revision 0.02
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct BSV {
|
||||||
|
static inline string 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline string 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,185 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
//convert any (supported) type to a const char* without constructing a new nall::string
|
||||||
|
//this is used inside istring(...) to build nall::string values
|
||||||
|
template<typename T> struct stringify;
|
||||||
|
|
||||||
|
// base types
|
||||||
|
|
||||||
|
template<> struct stringify<bool> {
|
||||||
|
bool value;
|
||||||
|
operator const char*() const { return value ? "true" : "false"; }
|
||||||
|
stringify(bool value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<char> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(char value) { integer(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// signed integers
|
||||||
|
|
||||||
|
template<> struct stringify<signed char> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(signed char value) { integer(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<signed short> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(signed short value) { integer(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<signed int> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(signed int value) { integer(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<signed long> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(signed long value) { integer(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<signed long long> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(signed long long value) { integer(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<unsigned bits> struct stringify<int_t<bits>> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(int_t<bits> value) { integer(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// unsigned integers
|
||||||
|
|
||||||
|
template<> struct stringify<unsigned char> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(unsigned char value) { decimal(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<unsigned short> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(unsigned short value) { decimal(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<unsigned int> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(unsigned int value) { decimal(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<unsigned long> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(unsigned long value) { decimal(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<unsigned long long> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(unsigned long long value) { decimal(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<unsigned bits> struct stringify<uint_t<bits>> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(uint_t<bits> value) { decimal(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// floating-point
|
||||||
|
|
||||||
|
template<> struct stringify<float> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(float value) { fp(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<double> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(double value) { fp(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<long double> {
|
||||||
|
char data[256];
|
||||||
|
operator const char*() const { return data; }
|
||||||
|
stringify(long double value) { fp(data, value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// strings
|
||||||
|
|
||||||
|
template<> struct stringify<char*> {
|
||||||
|
const char *value;
|
||||||
|
operator const char*() const { return value; }
|
||||||
|
stringify(char *value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<const char*> {
|
||||||
|
const char *value;
|
||||||
|
operator const char*() const { return value; }
|
||||||
|
stringify(const char *value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<string> {
|
||||||
|
const string &value;
|
||||||
|
operator const char*() const { return value; }
|
||||||
|
stringify(const string &value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<const string&> {
|
||||||
|
const string &value;
|
||||||
|
operator const char*() const { return value; }
|
||||||
|
stringify(const string &value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<cstring> {
|
||||||
|
const char *value;
|
||||||
|
operator const char*() const { return value; }
|
||||||
|
stringify(const cstring &value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<const cstring&> {
|
||||||
|
const char *value;
|
||||||
|
operator const char*() const { return value; }
|
||||||
|
stringify(const cstring &value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(QSTRING_H)
|
||||||
|
|
||||||
|
template<> struct stringify<QString> {
|
||||||
|
const QString &value;
|
||||||
|
operator const char*() const { return value.toUtf8().constData(); }
|
||||||
|
stringify(const QString &value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<const QString&> {
|
||||||
|
const QString &value;
|
||||||
|
operator const char*() const { return value.toUtf8().constData(); }
|
||||||
|
stringify(const QString &value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
string::operator QString() const {
|
||||||
|
return QString::fromUtf8(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename T> stringify<T> make_string(T value) {
|
||||||
|
return stringify<T>(std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,69 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_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 istrcmp(const char *str1, const char *str2) {
|
||||||
|
while(*str1) {
|
||||||
|
if(chrlower(*str1) != chrlower(*str2)) break;
|
||||||
|
str1++, str2++;
|
||||||
|
}
|
||||||
|
return (int)chrlower(*str1) - (int)chrlower(*str2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 istrbegin(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 istrend(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,64 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_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* qstrlower(char *s) {
|
||||||
|
if(!s) return 0;
|
||||||
|
bool quoted = false;
|
||||||
|
while(*s) {
|
||||||
|
if(*s == '\"' || *s == '\'') quoted ^= 1;
|
||||||
|
if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* qstrupper(char *s) {
|
||||||
|
if(!s) return 0;
|
||||||
|
bool quoted = false;
|
||||||
|
while(*s) {
|
||||||
|
if(*s == '\"' || *s == '\'') quoted ^= 1;
|
||||||
|
if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,200 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
static void istring(string &output) {
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
static void istring(string &output, const T &value, Args&&... args) {
|
||||||
|
output.append_(make_string(value));
|
||||||
|
istring(output, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void string::reserve(unsigned size_) {
|
||||||
|
if(size_ > size) {
|
||||||
|
size = size_;
|
||||||
|
data = (char*)realloc(data, size + 1);
|
||||||
|
data[size] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string::empty() const {
|
||||||
|
return !*data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> string& string::assign(Args&&... args) {
|
||||||
|
*data = 0;
|
||||||
|
istring(*this, std::forward<Args>(args)...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> string& string::append(Args&&... args) {
|
||||||
|
istring(*this, std::forward<Args>(args)...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::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) {
|
||||||
|
if(&value == this) return *this;
|
||||||
|
assign(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string& string::operator=(string &&source) {
|
||||||
|
if(&source == this) return *this;
|
||||||
|
if(data) free(data);
|
||||||
|
size = source.size;
|
||||||
|
data = source.data;
|
||||||
|
source.data = nullptr;
|
||||||
|
source.size = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if(&value == this) return;
|
||||||
|
size = strlen(value);
|
||||||
|
data = strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
string::string(string &&source) {
|
||||||
|
if(&source == this) return;
|
||||||
|
size = source.size;
|
||||||
|
data = source.data;
|
||||||
|
source.data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
string::~string() {
|
||||||
|
if(data) free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string::readfile(const string &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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
string lstring::concatenate(const char *separator) const {
|
||||||
|
string output;
|
||||||
|
for(unsigned i = 0; i < size(); i++) {
|
||||||
|
output.append(operator[](i), i < size() - 1 ? separator : "");
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> void lstring::append(const string &data, Args&&... args) {
|
||||||
|
vector::append(data);
|
||||||
|
append(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lstring::operator==(const lstring &source) const {
|
||||||
|
if(this == &source) return true;
|
||||||
|
if(size() != source.size()) return false;
|
||||||
|
for(unsigned n = 0; n < size(); n++) {
|
||||||
|
if(operator[](n) != source[n]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lstring::operator!=(const lstring &source) const {
|
||||||
|
return !operator==(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring& lstring::operator=(const lstring &source) {
|
||||||
|
vector::operator=(source);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring& lstring::operator=(lstring &source) {
|
||||||
|
vector::operator=(source);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring& lstring::operator=(lstring &&source) {
|
||||||
|
vector::operator=(std::move(source));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> lstring::lstring(Args&&... args) {
|
||||||
|
append(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring::lstring(const lstring &source) {
|
||||||
|
vector::operator=(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring::lstring(lstring &source) {
|
||||||
|
vector::operator=(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring::lstring(lstring &&source) {
|
||||||
|
vector::operator=(std::move(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
//const string:
|
||||||
|
//bind a const char* pointer to an object that has various testing functionality;
|
||||||
|
//yet lacks the memory allocation and modification functionality of the string class
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
cstring::operator const char*() const { return data; }
|
||||||
|
unsigned cstring::length() const { return strlen(data); }
|
||||||
|
bool cstring::operator==(const char *s) const { return !strcmp(data, s); }
|
||||||
|
bool cstring::operator!=(const char *s) const { return strcmp(data, s); }
|
||||||
|
optional<unsigned> cstring::position (const char *key) const { return strpos(data, key); }
|
||||||
|
optional<unsigned> cstring::iposition(const char *key) const { return istrpos(data, key); }
|
||||||
|
cstring& cstring::operator=(const char *data) { this->data = data; return *this; }
|
||||||
|
cstring::cstring(const char *data) : data(data) {}
|
||||||
|
cstring::cstring() : data("") {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
string string::date() {
|
||||||
|
time_t timestamp = ::time(0);
|
||||||
|
tm *info = localtime(×tamp);
|
||||||
|
return {
|
||||||
|
decimal<4, '0'>(1900 + info->tm_year), "-",
|
||||||
|
decimal<2, '0'>(1 + info->tm_mon), "-",
|
||||||
|
decimal<2, '0'>(info->tm_mday)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
string string::time() {
|
||||||
|
time_t timestamp = ::time(0);
|
||||||
|
tm *info = localtime(×tamp);
|
||||||
|
return {
|
||||||
|
decimal<2, '0'>(info->tm_hour), ":",
|
||||||
|
decimal<2, '0'>(info->tm_min), ":",
|
||||||
|
decimal<2, '0'>(info->tm_sec)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
string string::datetime() {
|
||||||
|
return {string::date(), " ", string::time()};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,74 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
// "/foo/bar.c" -> "/foo/"
|
||||||
|
// "/foo/" -> "/foo/"
|
||||||
|
// "bar.c" -> "./"
|
||||||
|
inline string dir(string name) {
|
||||||
|
for(signed i = name.length(); i >= 0; i--) {
|
||||||
|
if(name[i] == '/' || name[i] == '\\') {
|
||||||
|
name[i + 1] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(i == 0) name = "./";
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/foo/bar.c" -> "bar.c"
|
||||||
|
// "/foo/" -> ""
|
||||||
|
// "bar.c" -> "bar.c"
|
||||||
|
inline string notdir(string name) {
|
||||||
|
for(signed i = name.length(); i >= 0; i--) {
|
||||||
|
if(name[i] == '/' || name[i] == '\\') {
|
||||||
|
return (const char*)name + i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/foo/bar/baz" -> "/foo/bar/"
|
||||||
|
// "/foo/bar/" -> "/foo/"
|
||||||
|
// "/foo/bar" -> "/foo/"
|
||||||
|
inline string parentdir(string name) {
|
||||||
|
unsigned length = name.length(), paths = 0, prev, last;
|
||||||
|
for(unsigned i = 0; i < length; i++) {
|
||||||
|
if(name[i] == '/' || name[i] == '\\') {
|
||||||
|
paths++;
|
||||||
|
prev = last;
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(last + 1 == length) last = prev; //if name ends in slash; use previous slash
|
||||||
|
if(paths > 1) name[last + 1] = 0;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/foo/bar.c" -> "/foo/bar"
|
||||||
|
inline string basename(string name) {
|
||||||
|
for(signed i = name.length(); i >= 0; i--) {
|
||||||
|
if(name[i] == '/' || name[i] == '\\') break; //file has no extension
|
||||||
|
if(name[i] == '.') {
|
||||||
|
name[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/foo/bar.c" -> "c"
|
||||||
|
// "/foo/bar" -> ""
|
||||||
|
inline string extension(string name) {
|
||||||
|
for(signed i = name.length(); i >= 0; i--) {
|
||||||
|
if(name[i] == '/' || name[i] == '\\') return ""; //file has no extension
|
||||||
|
if(name[i] == '.') {
|
||||||
|
return (const char*)name + i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,147 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
//BML v1.0 parser
|
||||||
|
//revision 0.02
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace BML {
|
||||||
|
|
||||||
|
struct Node : Markup::Node {
|
||||||
|
protected:
|
||||||
|
//test to verify if a valid character for a node name
|
||||||
|
bool valid(char p) const { //A-Z, a-z, 0-9, -./
|
||||||
|
return p - 'A' < 26u || p - 'a' < 26u || p - '0' < 10u || p - '-' < 3u;
|
||||||
|
}
|
||||||
|
|
||||||
|
//determine indentation level, without incrementing pointer
|
||||||
|
unsigned readDepth(const char *p) {
|
||||||
|
unsigned depth = 0;
|
||||||
|
while(p[depth] == '\t' || p[depth] == ' ') depth++;
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
//determine indentation level
|
||||||
|
unsigned parseDepth(const char *&p) {
|
||||||
|
unsigned depth = readDepth(p);
|
||||||
|
p += depth;
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
//read name
|
||||||
|
void parseName(const char *&p) {
|
||||||
|
unsigned length = 0;
|
||||||
|
while(valid(p[length])) length++;
|
||||||
|
if(length == 0) throw "Invalid node name";
|
||||||
|
name = substr(p, 0, length);
|
||||||
|
p += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseData(const char *&p) {
|
||||||
|
if(*p == '=' && *(p + 1) == '\"') {
|
||||||
|
unsigned length = 2;
|
||||||
|
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
|
||||||
|
if(p[length] != '\"') throw "Unescaped value";
|
||||||
|
data = substr(p, 2, length - 2);
|
||||||
|
p += length + 1;
|
||||||
|
} else if(*p == '=') {
|
||||||
|
unsigned length = 1;
|
||||||
|
while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++;
|
||||||
|
if(p[length] == '\"') throw "Illegal character in value";
|
||||||
|
data = substr(p, 1, length - 1);
|
||||||
|
p += length;
|
||||||
|
} else if(*p == ':') {
|
||||||
|
unsigned length = 1;
|
||||||
|
while(p[length] && p[length] != '\n') length++;
|
||||||
|
data = {substr(p, 1, length - 1), "\n"};
|
||||||
|
p += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//read all attributes for a node
|
||||||
|
void parseAttributes(const char *&p) {
|
||||||
|
while(*p && *p != '\n') {
|
||||||
|
if(*p != ' ') throw "Invalid node name";
|
||||||
|
while(*p == ' ') p++; //skip excess spaces
|
||||||
|
|
||||||
|
Node node;
|
||||||
|
node.attribute = true;
|
||||||
|
unsigned length = 0;
|
||||||
|
while(valid(p[length])) length++;
|
||||||
|
if(length == 0) throw "Invalid attribute name";
|
||||||
|
node.name = substr(p, 0, length);
|
||||||
|
node.parseData(p += length);
|
||||||
|
children.append(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//read a node and all of its children nodes
|
||||||
|
void parseNode(const char *&p) {
|
||||||
|
level = parseDepth(p);
|
||||||
|
parseName(p);
|
||||||
|
parseData(p);
|
||||||
|
parseAttributes(p);
|
||||||
|
if(*p++ != '\n') throw "Missing line feed";
|
||||||
|
|
||||||
|
while(*p) {
|
||||||
|
if(*p == '\n') { p++; continue; }
|
||||||
|
|
||||||
|
unsigned depth = readDepth(p);
|
||||||
|
if(depth <= level) break;
|
||||||
|
|
||||||
|
if(p[depth] == ':') {
|
||||||
|
p += depth;
|
||||||
|
unsigned length = 0;
|
||||||
|
while(p[length] && p[length] != '\n') length++;
|
||||||
|
data.append(substr(p, 1, length - 1), "\n");
|
||||||
|
p += length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node node;
|
||||||
|
node.parseNode(p);
|
||||||
|
children.append(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.rtrim<1>("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//read top-level nodes
|
||||||
|
void parse(const char *p) {
|
||||||
|
while(*p) {
|
||||||
|
Node node;
|
||||||
|
node.parseNode(p);
|
||||||
|
if(node.level > 0) throw "Root nodes cannot be indented";
|
||||||
|
children.append(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class Document;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Document : Node {
|
||||||
|
string error;
|
||||||
|
|
||||||
|
bool load(string document) {
|
||||||
|
name = "{root}", data = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
document.replace("\r", "");
|
||||||
|
while(document.position("\n\n")) document.replace("\n\n", "\n");
|
||||||
|
parse(document);
|
||||||
|
} catch(const char *perror) {
|
||||||
|
error = perror;
|
||||||
|
children.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Document(const string &document = "") {
|
||||||
|
load(document);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace Markup {
|
||||||
|
|
||||||
|
inline Node Document(const string &markup) {
|
||||||
|
if(markup.beginswith("<")) return XML::Document(markup);
|
||||||
|
return BML::Document(markup);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,146 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
//note: specific markups inherit from Markup::Node
|
||||||
|
//vector<Node> will slice any data; so derived nodes must not contain data nor virtual functions
|
||||||
|
//vector<Node*> would incur a large performance penalty and greatly increased complexity
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace Markup {
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
string name;
|
||||||
|
string data;
|
||||||
|
bool attribute;
|
||||||
|
|
||||||
|
bool exists() const {
|
||||||
|
return !name.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
string text() const {
|
||||||
|
return string{data}.strip();
|
||||||
|
}
|
||||||
|
|
||||||
|
intmax_t integer() const {
|
||||||
|
return numeral(text());
|
||||||
|
}
|
||||||
|
|
||||||
|
uintmax_t decimal() const {
|
||||||
|
return numeral(text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
children.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool evaluate(const string &query) const {
|
||||||
|
if(query.empty()) return true;
|
||||||
|
lstring rules = string{query}.replace(" ", "").split(",");
|
||||||
|
|
||||||
|
for(auto &rule : rules) {
|
||||||
|
enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE };
|
||||||
|
auto comparator = Comparator::ID;
|
||||||
|
if(rule.wildcard("*!=*")) comparator = Comparator::NE;
|
||||||
|
else if(rule.wildcard("*<=*")) comparator = Comparator::LE;
|
||||||
|
else if(rule.wildcard("*>=*")) comparator = Comparator::GE;
|
||||||
|
else if(rule.wildcard ("*=*")) comparator = Comparator::EQ;
|
||||||
|
else if(rule.wildcard ("*<*")) comparator = Comparator::LT;
|
||||||
|
else if(rule.wildcard ("*>*")) comparator = Comparator::GT;
|
||||||
|
|
||||||
|
if(comparator == Comparator::ID) {
|
||||||
|
if(find(rule).size()) continue;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lstring side;
|
||||||
|
switch(comparator) {
|
||||||
|
case Comparator::EQ: side = rule.split<1> ("="); break;
|
||||||
|
case Comparator::NE: side = rule.split<1>("!="); break;
|
||||||
|
case Comparator::LT: side = rule.split<1> ("<"); break;
|
||||||
|
case Comparator::LE: side = rule.split<1>("<="); break;
|
||||||
|
case Comparator::GT: side = rule.split<1> (">"); break;
|
||||||
|
case Comparator::GE: side = rule.split<1>(">="); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string data = text();
|
||||||
|
if(side(0).empty() == false) {
|
||||||
|
auto result = find(side(0));
|
||||||
|
if(result.size() == 0) return false;
|
||||||
|
data = result(0).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(comparator) {
|
||||||
|
case Comparator::EQ: if(data.wildcard(side(1)) == true) continue; break;
|
||||||
|
case Comparator::NE: if(data.wildcard(side(1)) == false) continue; break;
|
||||||
|
case Comparator::LT: if(numeral(data) < numeral(side(1))) continue; break;
|
||||||
|
case Comparator::LE: if(numeral(data) <= numeral(side(1))) continue; break;
|
||||||
|
case Comparator::GT: if(numeral(data) > numeral(side(1))) continue; break;
|
||||||
|
case Comparator::GE: if(numeral(data) >= numeral(side(1))) continue; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<Node> find(const string &query) const {
|
||||||
|
vector<Node> result;
|
||||||
|
|
||||||
|
lstring path = query.split("/");
|
||||||
|
string name = path.take(0), rule;
|
||||||
|
unsigned lo = 0u, hi = ~0u;
|
||||||
|
|
||||||
|
if(name.wildcard("*[*]")) {
|
||||||
|
lstring side = name.split<1>("[");
|
||||||
|
name = side(0);
|
||||||
|
side = side(1).rtrim<1>("]").split<1>("-");
|
||||||
|
lo = side(0).empty() ? 0u : numeral(side(0));
|
||||||
|
hi = side(1).empty() ? ~0u : numeral(side(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(name.wildcard("*(*)")) {
|
||||||
|
lstring side = name.split<1>("(");
|
||||||
|
name = side(0);
|
||||||
|
rule = side(1).rtrim<1>(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned position = 0;
|
||||||
|
for(auto &node : children) {
|
||||||
|
if(node.name.wildcard(name) == false) continue;
|
||||||
|
if(node.evaluate(rule) == false) continue;
|
||||||
|
|
||||||
|
bool inrange = position >= lo && position <= hi;
|
||||||
|
position++;
|
||||||
|
if(inrange == false) continue;
|
||||||
|
|
||||||
|
if(path.size() == 0) result.append(node);
|
||||||
|
else {
|
||||||
|
auto list = node.find(path.concatenate("/"));
|
||||||
|
for(auto &item : list) result.append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node operator[](const string &query) const {
|
||||||
|
auto result = find(query);
|
||||||
|
return result(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* begin() { return children.begin(); }
|
||||||
|
Node* end() { return children.end(); }
|
||||||
|
const Node* begin() const { return children.begin(); }
|
||||||
|
const Node* end() const { return children.end(); }
|
||||||
|
|
||||||
|
Node() : attribute(false), level(0) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned level;
|
||||||
|
vector<Node> children;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,218 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
//XML v1.0 subset parser
|
||||||
|
//revision 0.03
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace XML {
|
||||||
|
|
||||||
|
struct Node : Markup::Node {
|
||||||
|
protected:
|
||||||
|
inline string escape() const {
|
||||||
|
string result = data;
|
||||||
|
result.replace("&", "&");
|
||||||
|
result.replace("<", "<");
|
||||||
|
result.replace(">", ">");
|
||||||
|
if(attribute == false) return result;
|
||||||
|
result.replace("\'", "'");
|
||||||
|
result.replace("\"", """);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isName(char c) const {
|
||||||
|
if(c >= 'A' && c <= 'Z') return true;
|
||||||
|
if(c >= 'a' && c <= 'z') return true;
|
||||||
|
if(c >= '0' && c <= '9') return true;
|
||||||
|
if(c == '.' || c == '_') return true;
|
||||||
|
if(c == '?') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isWhitespace(char c) const {
|
||||||
|
if(c == ' ' || c == '\t') return true;
|
||||||
|
if(c == '\r' || c == '\n') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy part of string from source document into target string; decode markup while copying
|
||||||
|
inline void copy(string &target, const char *source, unsigned length) {
|
||||||
|
target.reserve(length + 1);
|
||||||
|
|
||||||
|
#if defined(NALL_XML_LITERAL)
|
||||||
|
memcpy(target(), source, length);
|
||||||
|
target[length] = 0;
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *output = target();
|
||||||
|
while(length) {
|
||||||
|
if(*source == '&') {
|
||||||
|
if(!memcmp(source, "<", 4)) { *output++ = '<'; source += 4; length -= 4; continue; }
|
||||||
|
if(!memcmp(source, ">", 4)) { *output++ = '>'; source += 4; length -= 4; continue; }
|
||||||
|
if(!memcmp(source, "&", 5)) { *output++ = '&'; source += 5; length -= 5; continue; }
|
||||||
|
if(!memcmp(source, "'", 6)) { *output++ = '\''; source += 6; length -= 6; continue; }
|
||||||
|
if(!memcmp(source, """, 6)) { *output++ = '\"'; source += 6; length -= 6; continue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attribute == false && source[0] == '<' && source[1] == '!') {
|
||||||
|
//comment
|
||||||
|
if(!memcmp(source, "<!--", 4)) {
|
||||||
|
source += 4, length -= 4;
|
||||||
|
while(memcmp(source, "-->", 3)) source++, length--;
|
||||||
|
source += 3, length -= 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//CDATA
|
||||||
|
if(!memcmp(source, "<![CDATA[", 9)) {
|
||||||
|
source += 9, length -= 9;
|
||||||
|
while(memcmp(source, "]]>", 3)) *output++ = *source++, length--;
|
||||||
|
source += 3, length -= 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*output++ = *source++, length--;
|
||||||
|
}
|
||||||
|
*output = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool parseExpression(const char *&p) {
|
||||||
|
if(*(p + 1) != '!') return false;
|
||||||
|
|
||||||
|
//comment
|
||||||
|
if(!memcmp(p, "<!--", 4)) {
|
||||||
|
while(*p && memcmp(p, "-->", 3)) p++;
|
||||||
|
if(!*p) throw "unclosed comment";
|
||||||
|
p += 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//CDATA
|
||||||
|
if(!memcmp(p, "<![CDATA[", 9)) {
|
||||||
|
while(*p && memcmp(p, "]]>", 3)) p++;
|
||||||
|
if(!*p) throw "unclosed CDATA";
|
||||||
|
p += 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//DOCTYPE
|
||||||
|
if(!memcmp(p, "<!DOCTYPE", 9)) {
|
||||||
|
unsigned counter = 0;
|
||||||
|
do {
|
||||||
|
char n = *p++;
|
||||||
|
if(!n) throw "unclosed DOCTYPE";
|
||||||
|
if(n == '<') counter++;
|
||||||
|
if(n == '>') counter--;
|
||||||
|
} while(counter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns true if tag closes itself (<tag/>); false if not (<tag>)
|
||||||
|
inline bool parseHead(const char *&p) {
|
||||||
|
//parse name
|
||||||
|
const char *nameStart = ++p; //skip '<'
|
||||||
|
while(isName(*p)) p++;
|
||||||
|
const char *nameEnd = p;
|
||||||
|
copy(name, nameStart, nameEnd - nameStart);
|
||||||
|
if(name.empty()) throw "missing element name";
|
||||||
|
|
||||||
|
//parse attributes
|
||||||
|
while(*p) {
|
||||||
|
while(isWhitespace(*p)) p++;
|
||||||
|
if(!*p) throw "unclosed attribute";
|
||||||
|
if(*p == '?' || *p == '/' || *p == '>') break;
|
||||||
|
|
||||||
|
//parse attribute name
|
||||||
|
Node attribute;
|
||||||
|
attribute.attribute = true;
|
||||||
|
|
||||||
|
const char *nameStart = p;
|
||||||
|
while(isName(*p)) p++;
|
||||||
|
const char *nameEnd = p;
|
||||||
|
copy(attribute.name, nameStart, nameEnd - nameStart);
|
||||||
|
if(attribute.name.empty()) throw "missing attribute name";
|
||||||
|
|
||||||
|
//parse attribute data
|
||||||
|
if(*p++ != '=') throw "missing attribute value";
|
||||||
|
char terminal = *p++;
|
||||||
|
if(terminal != '\'' && terminal != '\"') throw "attribute value not quoted";
|
||||||
|
const char *dataStart = p;
|
||||||
|
while(*p && *p != terminal) p++;
|
||||||
|
if(!*p) throw "missing attribute data terminal";
|
||||||
|
const char *dataEnd = p++; //skip closing terminal
|
||||||
|
|
||||||
|
copy(attribute.data, dataStart, dataEnd - dataStart);
|
||||||
|
children.append(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
//parse closure
|
||||||
|
if(*p == '?' && *(p + 1) == '>') { p += 2; return true; }
|
||||||
|
if(*p == '/' && *(p + 1) == '>') { p += 2; return true; }
|
||||||
|
if(*p == '>') { p += 1; return false; }
|
||||||
|
throw "invalid element tag";
|
||||||
|
}
|
||||||
|
|
||||||
|
//parse element and all of its child elements
|
||||||
|
inline void parseElement(const char *&p) {
|
||||||
|
Node node;
|
||||||
|
if(node.parseHead(p) == false) node.parse(p);
|
||||||
|
children.append(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
//return true if </tag> matches this node's name
|
||||||
|
inline bool parseClosureElement(const char *&p) {
|
||||||
|
if(p[0] != '<' || p[1] != '/') return false;
|
||||||
|
p += 2;
|
||||||
|
const char *nameStart = p;
|
||||||
|
while(*p && *p != '>') p++;
|
||||||
|
if(*p != '>') throw "unclosed closure element";
|
||||||
|
const char *nameEnd = p++;
|
||||||
|
if(memcmp(name, nameStart, nameEnd - nameStart)) throw "closure element name mismatch";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//parse contents of an element
|
||||||
|
inline void parse(const char *&p) {
|
||||||
|
const char *dataStart = p, *dataEnd = p;
|
||||||
|
|
||||||
|
while(*p) {
|
||||||
|
while(*p && *p != '<') p++;
|
||||||
|
if(!*p) break;
|
||||||
|
dataEnd = p;
|
||||||
|
if(parseClosureElement(p) == true) break;
|
||||||
|
if(parseExpression(p) == true) continue;
|
||||||
|
parseElement(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(data, dataStart, dataEnd - dataStart);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Document : Node {
|
||||||
|
string error;
|
||||||
|
|
||||||
|
inline bool load(const char *document) {
|
||||||
|
if(document == nullptr) return false;
|
||||||
|
reset();
|
||||||
|
try {
|
||||||
|
parse(document);
|
||||||
|
} catch(const char *error) {
|
||||||
|
reset();
|
||||||
|
this->error = error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Document() {}
|
||||||
|
inline Document(const char *document) { load(document); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,166 @@
|
||||||
|
#ifdef NALL_STRING_INTERNAL_HPP
|
||||||
|
|
||||||
|
namespace fixedpoint {
|
||||||
|
|
||||||
|
static nall::function<intmax_t (const char *&)> eval_fallback;
|
||||||
|
|
||||||
|
static intmax_t eval_integer(const char *& s) {
|
||||||
|
if(!*s) throw "unrecognized integer";
|
||||||
|
intmax_t 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 intmax_t eval(const char *&s, int depth = 0) {
|
||||||
|
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||||
|
if(!*s) throw "unrecognized token";
|
||||||
|
intmax_t 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 if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
|
||||||
|
|
||||||
|
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 == '/') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value /= result; continue; }
|
||||||
|
if(x == '%') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value %= result; 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 == '?') {
|
||||||
|
intmax_t lhs = eval(++s, 2);
|
||||||
|
if(*s != ':') throw "mismatched ternary";
|
||||||
|
intmax_t rhs = eval(++s, 2);
|
||||||
|
value = value ? lhs : rhs;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(depth >= 2) break;
|
||||||
|
|
||||||
|
if(depth > 0 && x == ')') break;
|
||||||
|
|
||||||
|
throw "unrecognized token";
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool eval(const char *s, intmax_t &result) {
|
||||||
|
try {
|
||||||
|
result = eval(s);
|
||||||
|
return true;
|
||||||
|
} catch(const char*) {
|
||||||
|
result = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static intmax_t parse(const char *s) {
|
||||||
|
try {
|
||||||
|
intmax_t result = eval(s);
|
||||||
|
return result;
|
||||||
|
} catch(const char *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue