mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-27 02:50:32 +00:00
Update to v092r06 release.
byuu says: Changelog: - added support for ruby shader folders (place in "Video Shaders/") - higan now also looks in your shared folder for configuration files and system media folders - added CFBundleExecutable key to OS X Info.plist Shared folder locations: - Windows XP: C:\Documents and Settings\All Users\Application Data\higan - Windows 7: C:\ProgramData\higan - OS X: /Library/Application Support/higan - Linux: /etc/higan Evaluation order: - look for item in binary folder: if found, use this folder - look for item in user folder: if found, use this folder - look for item in shared folder: if found, use this folder - create item in user folder For people repackaging higan for other distros: you should chmod 777 /etc/higan. Failure to do so could result in higan breaking. No, I will not copy the files from the shared path to the user path.
This commit is contained in:
parent
5b4bbf5045
commit
0d75524791
@ -98,11 +98,10 @@ ifeq ($(shell id -un),byuu)
|
||||
cp -r ../ruby ./ruby
|
||||
cp -r ../phoenix ./phoenix
|
||||
rm -r libco/doc
|
||||
rm -r libco/test
|
||||
rm -r nall/test
|
||||
rm -r ruby/_test
|
||||
rm -r phoenix/nall
|
||||
rm -r phoenix/test
|
||||
rm -r libco/.test
|
||||
rm -r nall/.test
|
||||
rm -r ruby/.test
|
||||
rm -r phoenix/.test
|
||||
endif
|
||||
|
||||
help:;
|
||||
|
@ -4,6 +4,8 @@
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>higan.icns</string>
|
||||
</dict>
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "higan";
|
||||
static const char Version[] = "092.05";
|
||||
static const char Version[] = "092.06";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <nall/file.hpp>
|
||||
|
||||
//BMP reader / writer
|
||||
//author: byuu
|
||||
//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
|
||||
|
||||
namespace nall {
|
||||
|
@ -227,11 +227,13 @@ namespace nall {
|
||||
static bool exists(const string &filename) {
|
||||
#if !defined(_WIN32)
|
||||
struct stat64 data;
|
||||
return stat64(filename, &data) == 0;
|
||||
if(stat64(filename, &data) != 0) return false;
|
||||
#else
|
||||
struct __stat64 data;
|
||||
return _wstat64(utf16_t(filename), &data) == 0;
|
||||
if(_wstat64(utf16_t(filename), &data) != 0) return false;
|
||||
#endif
|
||||
//return true if this is a file, and false if this is a directory
|
||||
return !(data.st_mode & S_IFDIR);
|
||||
}
|
||||
|
||||
static uintmax_t size(const string &filename) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
#define NALL_INTRINSICS_HPP
|
||||
|
||||
struct Intrinsics {
|
||||
enum class Compiler : unsigned { GCC, VisualC, Unknown };
|
||||
enum class Compiler : unsigned { Clang, GCC, VisualC, Unknown };
|
||||
enum class Platform : unsigned { X, OSX, Windows, Unknown };
|
||||
enum class Endian : unsigned { LSB, MSB, Unknown };
|
||||
|
||||
@ -13,7 +13,10 @@ struct Intrinsics {
|
||||
|
||||
/* Compiler detection */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__clang__)
|
||||
#define COMPILER_CLANG
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Clang; }
|
||||
#elif defined(__GNUC__)
|
||||
#define COMPILER_GCC
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::GCC; }
|
||||
#elif defined(_MSC_VER)
|
||||
|
@ -9,14 +9,16 @@
|
||||
//path environment variable is always consulted
|
||||
//execution is asynchronous (non-blocking); use system() for synchronous execution
|
||||
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#ifdef _WIN32
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
||||
template<typename... Args>
|
||||
inline void invoke(const string &name, Args&&... args) {
|
||||
@ -26,7 +28,7 @@ inline void invoke(const string &name, Args&&... args) {
|
||||
ShellExecuteW(NULL, NULL, utf16_t(name), utf16_t(arguments), NULL, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
#else
|
||||
#elif defined(PLATFORM_X)
|
||||
|
||||
template<typename... Args>
|
||||
inline void invoke(const string &name, Args&&... args) {
|
||||
@ -45,6 +47,12 @@ inline void invoke(const string &name, Args&&... args) {
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename... Args>
|
||||
inline void invoke(const string &name, Args&&... args) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
35
higan/nall/matrix.hpp
Normal file
35
higan/nall/matrix.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef NALL_MATRIX_HPP
|
||||
#define NALL_MATRIX_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
namespace Matrix {
|
||||
|
||||
template<typename T>
|
||||
inline void Multiply(T *output, const T *xdata, unsigned xrows, unsigned xcols, const T *ydata, unsigned yrows, unsigned ycols) {
|
||||
if(xcols != yrows) return;
|
||||
|
||||
for(unsigned y = 0; y < xrows; y++) {
|
||||
for(unsigned x = 0; x < ycols; x++) {
|
||||
T sum = 0;
|
||||
for(unsigned z = 0; z < xcols; z++) {
|
||||
sum += xdata[y * xcols + z] * ydata[z * ycols + x];
|
||||
}
|
||||
*output++ = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline vector<T> Multiply(const T *xdata, unsigned xrows, unsigned xcols, const T *ydata, unsigned yrows, unsigned ycols) {
|
||||
vector<T> output;
|
||||
output.resize(xrows * ycols);
|
||||
Multiply(output.data(), xdata, xrows, xcols, ydata, yrows, ycols);
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -29,6 +29,7 @@
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/invoke.hpp>
|
||||
#include <nall/map.hpp>
|
||||
#include <nall/matrix.hpp>
|
||||
#include <nall/png.hpp>
|
||||
#include <nall/property.hpp>
|
||||
#include <nall/random.hpp>
|
||||
|
@ -54,8 +54,8 @@
|
||||
//================
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define PATH_MAX _MAX_PATH
|
||||
#define va_copy(dest, src) ((dest) = (src))
|
||||
#define PATH_MAX _MAX_PATH
|
||||
#define va_copy(dest, src) ((dest) = (src))
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
@ -69,7 +69,7 @@
|
||||
//inline expansion
|
||||
//================
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define noinline __attribute__((noinline))
|
||||
#define inline inline
|
||||
#define alwaysinline inline __attribute__((always_inline))
|
||||
@ -83,4 +83,14 @@
|
||||
#define alwaysinline inline
|
||||
#endif
|
||||
|
||||
//===========
|
||||
//unreachable
|
||||
//===========
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define unreachable __builtin_unreachable()
|
||||
#else
|
||||
#define unreachable throw
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -9,9 +9,10 @@
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/atoi.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
@ -180,6 +180,7 @@ namespace nall {
|
||||
inline string realpath(const string &name);
|
||||
inline string userpath();
|
||||
inline string configpath();
|
||||
inline string sharedpath();
|
||||
inline string temppath();
|
||||
|
||||
//strm.hpp
|
||||
|
@ -4,7 +4,7 @@ namespace nall {
|
||||
|
||||
string activepath() {
|
||||
string result;
|
||||
#ifdef _WIN32
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
wchar_t path[PATH_MAX] = L"";
|
||||
auto unused = _wgetcwd(path, PATH_MAX);
|
||||
result = (const char*)utf8_t(path);
|
||||
@ -21,7 +21,7 @@ string activepath() {
|
||||
|
||||
string realpath(const string &name) {
|
||||
string result;
|
||||
#ifdef _WIN32
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
wchar_t path[PATH_MAX] = L"";
|
||||
if(_wfullpath(path, utf16_t(name), PATH_MAX)) result = (const char*)utf8_t(path);
|
||||
result.transform("\\", "/");
|
||||
@ -37,16 +37,14 @@ string realpath(const string &name) {
|
||||
// c:/users/username/
|
||||
string userpath() {
|
||||
string result;
|
||||
#ifdef _WIN32
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
wchar_t path[PATH_MAX] = L"";
|
||||
SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path);
|
||||
result = (const char*)utf8_t(path);
|
||||
result.transform("\\", "/");
|
||||
#else
|
||||
char path[PATH_MAX] = "";
|
||||
struct passwd *userinfo = getpwuid(getuid());
|
||||
if(userinfo) strcpy(path, userinfo->pw_dir);
|
||||
result = path;
|
||||
result = userinfo->pw_dir;
|
||||
#endif
|
||||
if(result.empty()) result = ".";
|
||||
if(result.endswith("/") == false) result.append("/");
|
||||
@ -57,11 +55,13 @@ string userpath() {
|
||||
// c:/users/username/appdata/roaming/
|
||||
string configpath() {
|
||||
string result;
|
||||
#ifdef _WIN32
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
wchar_t path[PATH_MAX] = L"";
|
||||
SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
|
||||
result = (const char*)utf8_t(path);
|
||||
result.transform("\\", "/");
|
||||
#elif defined(PLATFORM_OSX)
|
||||
result = {userpath(), "Library/Application Support/"};
|
||||
#else
|
||||
result = {userpath(), ".config/"};
|
||||
#endif
|
||||
@ -70,8 +70,25 @@ string configpath() {
|
||||
return result;
|
||||
}
|
||||
|
||||
string sharedpath() {
|
||||
string result;
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
wchar_t path[PATH_MAX] = L"";
|
||||
SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
|
||||
result = (const char*)utf8_t(path);
|
||||
result.transform("\\", "/");
|
||||
#elif defined(PLATFORM_OSX)
|
||||
result = "/Library/Application Support/";
|
||||
#else
|
||||
result = "/etc/";
|
||||
#endif
|
||||
if(result.empty()) result = ".";
|
||||
if(result.endswith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
|
||||
string temppath() {
|
||||
#ifdef _WIN32
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
wchar_t path[PATH_MAX] = L"";
|
||||
GetTempPathW(PATH_MAX, path);
|
||||
string result = (const char*)utf8_t(path);
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <pthread.h>
|
||||
|
||||
namespace nall {
|
||||
void* thread_entry_point(void*);
|
||||
inline void* thread_entry_point(void*);
|
||||
|
||||
struct thread {
|
||||
thread(function<void ()> entryPoint) : entryPoint(entryPoint), completed(false), dead(false) {
|
||||
|
@ -18,6 +18,16 @@
|
||||
if(Application::main) Application::main();
|
||||
}
|
||||
|
||||
-(void) updateInDock:(NSTimer*)timer {
|
||||
NSArray *windows = [NSApp windows];
|
||||
for(unsigned n = 0; n < [windows count]; n++) {
|
||||
NSWindow *window = [windows objectAtIndex:n];
|
||||
if([window isMiniaturized]) {
|
||||
[window updateInDock];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
CocoaDelegate *cocoaDelegate = nullptr;
|
||||
@ -25,12 +35,15 @@ CocoaDelegate *cocoaDelegate = nullptr;
|
||||
namespace phoenix {
|
||||
|
||||
void pApplication::run() {
|
||||
//NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1667 target:cocoaDelegate selector:@selector(updateInDock:) userInfo:nil repeats:YES];
|
||||
|
||||
if(Application::main) {
|
||||
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:cocoaDelegate selector:@selector(run:) userInfo:nil repeats:YES];
|
||||
|
||||
//below line is needed to run application during window resize; however it has a large performance penalty on the resize smoothness
|
||||
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
[NSApp run];
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
-(NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender;
|
||||
-(BOOL) applicationShouldHandleReopen:(NSApplication*)application hasVisibleWindows:(BOOL)flag;
|
||||
-(void) run:(NSTimer*)timer;
|
||||
-(void) updateInDock:(NSTimer*)timer;
|
||||
@end
|
||||
|
||||
namespace phoenix {
|
||||
|
@ -32,7 +32,14 @@ Size pButton::minimumSize() {
|
||||
size.height += button.state.image.height;
|
||||
}
|
||||
|
||||
return {size.width + 24, size.height + 8};
|
||||
return {size.width + 20, size.height + 4};
|
||||
}
|
||||
|
||||
void pButton::setGeometry(const Geometry &geometry) {
|
||||
pWidget::setGeometry({
|
||||
geometry.x - 2, geometry.y - 2,
|
||||
geometry.width + 4, geometry.height + 4
|
||||
});
|
||||
}
|
||||
|
||||
void pButton::setImage(const image &image, Orientation orientation) {
|
||||
|
@ -13,6 +13,7 @@ struct pButton : public pWidget {
|
||||
CocoaButton *cocoaButton = nullptr;
|
||||
|
||||
Size minimumSize();
|
||||
void setGeometry(const Geometry &geometry);
|
||||
void setImage(const image &image, Orientation orientation);
|
||||
void setText(const string &text);
|
||||
|
||||
|
@ -28,7 +28,7 @@ bool pCheckButton::checked() {
|
||||
|
||||
Size pCheckButton::minimumSize() {
|
||||
Size size = Font::size(checkButton.font(), checkButton.state.text);
|
||||
return {size.width + 24, size.height + 8};
|
||||
return {size.width + 20, size.height};
|
||||
}
|
||||
|
||||
void pCheckButton::setChecked(bool checked) {
|
||||
@ -37,6 +37,13 @@ void pCheckButton::setChecked(bool checked) {
|
||||
}
|
||||
}
|
||||
|
||||
void pCheckButton::setGeometry(const Geometry &geometry) {
|
||||
pWidget::setGeometry({
|
||||
geometry.x - 2, geometry.y,
|
||||
geometry.width + 4, geometry.height
|
||||
});
|
||||
}
|
||||
|
||||
void pCheckButton::setText(const string &text) {
|
||||
@autoreleasepool {
|
||||
[cocoaView setTitle:[NSString stringWithUTF8String:text]];
|
||||
|
@ -15,6 +15,7 @@ struct pCheckButton : public pWidget {
|
||||
bool checked();
|
||||
Size minimumSize();
|
||||
void setChecked(bool checked);
|
||||
void setGeometry(const Geometry &geometry);
|
||||
void setText(const string &text);
|
||||
|
||||
pCheckButton(CheckButton &checkButton) : pWidget(checkButton), checkButton(checkButton) {}
|
||||
|
@ -28,7 +28,7 @@ Size pComboButton::minimumSize() {
|
||||
unsigned maximumWidth = 0;
|
||||
for(auto &text : comboButton.state.text) maximumWidth = max(maximumWidth, Font::size(comboButton.font(), text).width);
|
||||
Size size = Font::size(comboButton.font(), " ");
|
||||
return {maximumWidth + 40, size.height + 8};
|
||||
return {maximumWidth + 36, size.height + 6};
|
||||
}
|
||||
|
||||
void pComboButton::modify(unsigned row, const string &text) {
|
||||
@ -55,6 +55,13 @@ unsigned pComboButton::selection() {
|
||||
}
|
||||
}
|
||||
|
||||
void pComboButton::setGeometry(const Geometry &geometry) {
|
||||
pWidget::setGeometry({
|
||||
geometry.x - 2, geometry.y,
|
||||
geometry.width + 4, geometry.height
|
||||
});
|
||||
}
|
||||
|
||||
void pComboButton::setSelection(unsigned row) {
|
||||
@autoreleasepool {
|
||||
[cocoaView selectItemAtIndex:row];
|
||||
|
@ -18,6 +18,7 @@ struct pComboButton : public pWidget {
|
||||
void remove(unsigned row);
|
||||
void reset();
|
||||
unsigned selection();
|
||||
void setGeometry(const Geometry &geometry);
|
||||
void setSelection(unsigned row);
|
||||
|
||||
pComboButton(ComboButton &comboButton) : pWidget(comboButton), comboButton(comboButton) {}
|
||||
|
@ -21,7 +21,7 @@
|
||||
namespace phoenix {
|
||||
|
||||
Size pHorizontalSlider::minimumSize() {
|
||||
return {64, 24};
|
||||
return {48, 20};
|
||||
}
|
||||
|
||||
unsigned pHorizontalSlider::position() {
|
||||
@ -30,6 +30,13 @@ unsigned pHorizontalSlider::position() {
|
||||
}
|
||||
}
|
||||
|
||||
void pHorizontalSlider::setGeometry(const Geometry &geometry) {
|
||||
pWidget::setGeometry({
|
||||
geometry.x - 2, geometry.y,
|
||||
geometry.width + 4, geometry.height
|
||||
});
|
||||
}
|
||||
|
||||
void pHorizontalSlider::setLength(unsigned length) {
|
||||
@autoreleasepool {
|
||||
[cocoaView setMaxValue:length];
|
||||
|
@ -14,6 +14,7 @@ struct pHorizontalSlider : public pWidget {
|
||||
|
||||
Size minimumSize();
|
||||
unsigned position();
|
||||
void setGeometry(const Geometry &geometry);
|
||||
void setLength(unsigned length);
|
||||
void setPosition(unsigned position);
|
||||
|
||||
|
@ -18,7 +18,26 @@ namespace phoenix {
|
||||
|
||||
Size pLabel::minimumSize() {
|
||||
Size size = Font::size(label.font(), label.state.text);
|
||||
return {size.width + 6, size.height};
|
||||
return {size.width, size.height};
|
||||
}
|
||||
|
||||
void pLabel::setGeometry(const Geometry &geometry) {
|
||||
//NSTextField does not support vertical text centering:
|
||||
//simulate this by adjusting the geometry placement (reduce height, move view down)
|
||||
unsigned height = Font::size(label.font(), " ").height;
|
||||
unsigned widgetHeight = geometry.height + 4; //+4 compensates for margin adjust
|
||||
auto offset = geometry;
|
||||
|
||||
if(widgetHeight > height) {
|
||||
unsigned diff = widgetHeight - height;
|
||||
offset.y += diff >> 1;
|
||||
offset.height -= diff >> 1;
|
||||
}
|
||||
|
||||
pWidget::setGeometry({
|
||||
offset.x - 3, offset.y - 3,
|
||||
offset.width + 6, offset.height + 6
|
||||
});
|
||||
}
|
||||
|
||||
void pLabel::setText(const string &text) {
|
||||
|
@ -12,6 +12,7 @@ struct pLabel : public pWidget {
|
||||
CocoaLabel *cocoaLabel = nullptr;
|
||||
|
||||
Size minimumSize();
|
||||
void setGeometry(const Geometry &geometry);
|
||||
void setText(const string &text);
|
||||
|
||||
pLabel(Label &label) : pWidget(label), label(label) {}
|
||||
|
@ -28,7 +28,7 @@ bool pRadioButton::checked() {
|
||||
|
||||
Size pRadioButton::minimumSize() {
|
||||
Size size = Font::size(radioButton.font(), radioButton.state.text);
|
||||
return {size.width + 24, size.height + 8};
|
||||
return {size.width + 22, size.height};
|
||||
}
|
||||
|
||||
void pRadioButton::setChecked() {
|
||||
@ -40,6 +40,13 @@ void pRadioButton::setChecked() {
|
||||
}
|
||||
}
|
||||
|
||||
void pRadioButton::setGeometry(const Geometry &geometry) {
|
||||
pWidget::setGeometry({
|
||||
geometry.x - 1, geometry.y,
|
||||
geometry.width + 2, geometry.height
|
||||
});
|
||||
}
|
||||
|
||||
void pRadioButton::setGroup(const set<RadioButton&> &group) {
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ struct pRadioButton : public pWidget {
|
||||
bool checked();
|
||||
Size minimumSize();
|
||||
void setChecked();
|
||||
void setGeometry(const Geometry &geometry);
|
||||
void setGroup(const set<RadioButton&> &group);
|
||||
void setText(const string &text);
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
namespace phoenix {
|
||||
|
||||
Size pVerticalSlider::minimumSize() {
|
||||
return {24, 64};
|
||||
return {20, 48};
|
||||
}
|
||||
|
||||
unsigned pVerticalSlider::position() {
|
||||
@ -30,6 +30,13 @@ unsigned pVerticalSlider::position() {
|
||||
}
|
||||
}
|
||||
|
||||
void pVerticalSlider::setGeometry(const Geometry &geometry) {
|
||||
pWidget::setGeometry({
|
||||
geometry.x, geometry.y - 2,
|
||||
geometry.width, geometry.height + 4
|
||||
});
|
||||
}
|
||||
|
||||
void pVerticalSlider::setLength(unsigned length) {
|
||||
@autoreleasepool {
|
||||
[cocoaView setMaxValue:length];
|
||||
|
@ -14,6 +14,7 @@ struct pVerticalSlider : public pWidget {
|
||||
|
||||
Size minimumSize();
|
||||
unsigned position();
|
||||
void setGeometry(const Geometry &geometry);
|
||||
void setLength(unsigned length);
|
||||
void setPosition(unsigned position);
|
||||
|
||||
|
@ -10,7 +10,7 @@ struct pWidget : public pSizable {
|
||||
void setEnabled(bool enabled);
|
||||
void setFocused();
|
||||
virtual void setFont(const string &font);
|
||||
void setGeometry(const Geometry &geometry);
|
||||
virtual void setGeometry(const Geometry &geometry);
|
||||
void setVisible(bool visible);
|
||||
|
||||
pWidget(Widget &widget) : pSizable(widget), widget(widget) {}
|
||||
|
@ -12,6 +12,11 @@
|
||||
[self setAcceptsMouseMovedEvents:YES];
|
||||
[self setTitle:@""];
|
||||
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
NSDictionary *dictionary = [bundle infoDictionary];
|
||||
NSString *applicationName = [dictionary objectForKey:@"CFBundleDisplayName"];
|
||||
if(applicationName == nil) applicationName = [NSString stringWithUTF8String:phoenix::applicationState.name];
|
||||
|
||||
menuBar = [[NSMenu alloc] init];
|
||||
|
||||
NSMenuItem *item;
|
||||
@ -22,17 +27,39 @@
|
||||
[item setSubmenu:rootMenu];
|
||||
[menuBar addItem:item];
|
||||
|
||||
text = {"About ", phoenix::applicationState.name, " ..."};
|
||||
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:text] action:@selector(menuAbout) keyEquivalent:@""] autorelease];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"About %@ ...", applicationName] action:@selector(menuAbout) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:self];
|
||||
[rootMenu addItem:item];
|
||||
[rootMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Preferences" action:@selector(menuPreferences) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:self];
|
||||
[rootMenu addItem:item];
|
||||
[rootMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
text = {"Quit ", phoenix::applicationState.name};
|
||||
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:text] action:@selector(menuQuit) keyEquivalent:@""] autorelease];
|
||||
NSMenu *servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:nil keyEquivalent:@""] autorelease];
|
||||
[item setTarget:self];
|
||||
[item setSubmenu:servicesMenu];
|
||||
[rootMenu addItem:item];
|
||||
[rootMenu addItem:[NSMenuItem separatorItem]];
|
||||
[NSApp setServicesMenu:servicesMenu];
|
||||
|
||||
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Hide %@", applicationName] action:@selector(hide:) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:NSApp];
|
||||
[rootMenu addItem:item];
|
||||
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:NSApp];
|
||||
[rootMenu addItem:item];
|
||||
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:NSApp];
|
||||
[rootMenu addItem:item];
|
||||
[rootMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Quit %@", applicationName] action:@selector(menuQuit) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:self];
|
||||
[rootMenu addItem:item];
|
||||
|
||||
statusBar = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)];
|
||||
|
@ -1,5 +1,5 @@
|
||||
void FixedLayout::append(Sizable &sizable, const Geometry &geometry) {
|
||||
children.append({ &sizable, geometry });
|
||||
children.append({&sizable, geometry});
|
||||
synchronizeLayout();
|
||||
if(window()) window()->synchronizeLayout();
|
||||
}
|
||||
|
131
higan/phoenix/nall/Makefile
Normal file
131
higan/phoenix/nall/Makefile
Normal file
@ -0,0 +1,131 @@
|
||||
# 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 := g++
|
||||
flags :=
|
||||
link :=
|
||||
else ifeq ($(platform),osx)
|
||||
compiler := clang
|
||||
flags := -w -stdlib=libc++
|
||||
link := -lc++ -lobjc
|
||||
else
|
||||
compiler := g++-4.7
|
||||
flags :=
|
||||
link :=
|
||||
endif
|
||||
|
||||
cflags := -x c -std=gnu99
|
||||
objcflags := -x objective-c -std=gnu99
|
||||
cppflags := -x c++ -std=gnu++11
|
||||
objcppflags := -x objective-c++ -std=gnu++11
|
||||
endif
|
||||
|
||||
ifeq ($(arch),x86)
|
||||
flags := -m32 $(flags)
|
||||
link := -m32 $(link)
|
||||
endif
|
||||
|
||||
ifeq ($(prefix),)
|
||||
prefix := /usr/local
|
||||
endif
|
||||
|
||||
#####
|
||||
# function rwildcard(directory, pattern)
|
||||
#####
|
||||
rwildcard = \
|
||||
$(strip \
|
||||
$(filter $(if $2,$2,%), \
|
||||
$(foreach f, \
|
||||
$(wildcard $1*), \
|
||||
$(eval t = $(call rwildcard,$f/)) \
|
||||
$(if $t,$t,$f) \
|
||||
) \
|
||||
) \
|
||||
)
|
||||
|
||||
#####
|
||||
# function strtr(source, from, to)
|
||||
#####
|
||||
strtr = \
|
||||
$(eval __temp := $1) \
|
||||
$(strip \
|
||||
$(foreach c, \
|
||||
$(join $(addsuffix :,$2),$3), \
|
||||
$(eval __temp := \
|
||||
$(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \
|
||||
) \
|
||||
) \
|
||||
$(__temp) \
|
||||
)
|
||||
|
||||
#####
|
||||
# function strupper(source)
|
||||
#####
|
||||
strupper = $(call strtr,$1,$([a-z]),$([A-Z]))
|
||||
|
||||
#####
|
||||
# function strlower(source)
|
||||
#####
|
||||
strlower = $(call strtr,$1,$([A-Z]),$([a-z]))
|
||||
|
||||
#####
|
||||
# function strlen(source)
|
||||
#####
|
||||
strlen = \
|
||||
$(eval __temp := $(subst $([space]),_,$1)) \
|
||||
$(words \
|
||||
$(strip \
|
||||
$(foreach c, \
|
||||
$([all]), \
|
||||
$(eval __temp := \
|
||||
$(subst $c,$c ,$(__temp)) \
|
||||
) \
|
||||
) \
|
||||
$(__temp) \
|
||||
) \
|
||||
)
|
||||
|
||||
#####
|
||||
# function streq(source)
|
||||
#####
|
||||
streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1)
|
||||
|
||||
#####
|
||||
# function strne(source)
|
||||
#####
|
||||
strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,)
|
17
higan/phoenix/nall/algorithm.hpp
Normal file
17
higan/phoenix/nall/algorithm.hpp
Normal file
@ -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
|
73
higan/phoenix/nall/any.hpp
Normal file
73
higan/phoenix/nall/any.hpp
Normal file
@ -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
|
103
higan/phoenix/nall/atoi.hpp
Normal file
103
higan/phoenix/nall/atoi.hpp
Normal file
@ -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
|
118
higan/phoenix/nall/base64.hpp
Normal file
118
higan/phoenix/nall/base64.hpp
Normal file
@ -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
|
84
higan/phoenix/nall/beat/archive.hpp
Normal file
84
higan/phoenix/nall/beat/archive.hpp
Normal file
@ -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
|
92
higan/phoenix/nall/beat/base.hpp
Normal file
92
higan/phoenix/nall/beat/base.hpp
Normal file
@ -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
|
214
higan/phoenix/nall/beat/delta.hpp
Normal file
214
higan/phoenix/nall/beat/delta.hpp
Normal file
@ -0,0 +1,214 @@
|
||||
#ifndef NALL_BEAT_DELTA_HPP
|
||||
#define NALL_BEAT_DELTA_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpsdelta {
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void target(const uint8_t *data, unsigned size);
|
||||
|
||||
inline bool source(const string &filename);
|
||||
inline bool target(const string &filename);
|
||||
inline bool create(const string &filename, const string &metadata = "");
|
||||
|
||||
protected:
|
||||
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
enum : unsigned { Granularity = 1 };
|
||||
|
||||
struct Node {
|
||||
unsigned offset;
|
||||
Node *next;
|
||||
inline Node() : offset(0), next(nullptr) {}
|
||||
inline ~Node() { if(next) delete next; }
|
||||
};
|
||||
|
||||
filemap sourceFile;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
|
||||
filemap targetFile;
|
||||
const uint8_t *targetData;
|
||||
unsigned targetSize;
|
||||
};
|
||||
|
||||
void bpsdelta::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data;
|
||||
sourceSize = size;
|
||||
}
|
||||
|
||||
void bpsdelta::target(const uint8_t *data, unsigned size) {
|
||||
targetData = data;
|
||||
targetSize = size;
|
||||
}
|
||||
|
||||
bool bpsdelta::source(const string &filename) {
|
||||
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||
source(sourceFile.data(), sourceFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpsdelta::target(const string &filename) {
|
||||
if(targetFile.open(filename, filemap::mode::read) == false) return false;
|
||||
target(targetFile.data(), targetFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpsdelta::create(const string &filename, const string &metadata) {
|
||||
file modifyFile;
|
||||
if(modifyFile.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
|
||||
unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
modifyFile.write(data);
|
||||
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||
};
|
||||
|
||||
auto encode = [&](uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
};
|
||||
|
||||
write('B');
|
||||
write('P');
|
||||
write('S');
|
||||
write('1');
|
||||
|
||||
encode(sourceSize);
|
||||
encode(targetSize);
|
||||
|
||||
unsigned markupSize = metadata.length();
|
||||
encode(markupSize);
|
||||
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
|
||||
|
||||
Node *sourceTree[65536], *targetTree[65536];
|
||||
for(unsigned n = 0; n < 65536; n++) sourceTree[n] = nullptr, targetTree[n] = nullptr;
|
||||
|
||||
//source tree creation
|
||||
for(unsigned offset = 0; offset < sourceSize; offset++) {
|
||||
uint16_t symbol = sourceData[offset + 0];
|
||||
sourceChecksum = crc32_adjust(sourceChecksum, symbol);
|
||||
if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
|
||||
Node *node = new Node;
|
||||
node->offset = offset;
|
||||
node->next = sourceTree[symbol];
|
||||
sourceTree[symbol] = node;
|
||||
}
|
||||
|
||||
unsigned targetReadLength = 0;
|
||||
|
||||
auto targetReadFlush = [&]() {
|
||||
if(targetReadLength) {
|
||||
encode(TargetRead | ((targetReadLength - 1) << 2));
|
||||
unsigned offset = outputOffset - targetReadLength;
|
||||
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
|
||||
}
|
||||
};
|
||||
|
||||
while(outputOffset < targetSize) {
|
||||
unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
|
||||
|
||||
uint16_t symbol = targetData[outputOffset + 0];
|
||||
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
|
||||
|
||||
{ //source read
|
||||
unsigned length = 0, offset = outputOffset;
|
||||
while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
|
||||
length++;
|
||||
offset++;
|
||||
}
|
||||
if(length > maxLength) maxLength = length, mode = SourceRead;
|
||||
}
|
||||
|
||||
{ //source copy
|
||||
Node *node = sourceTree[symbol];
|
||||
while(node) {
|
||||
unsigned length = 0, x = node->offset, y = outputOffset;
|
||||
while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
|
||||
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
{ //target copy
|
||||
Node *node = targetTree[symbol];
|
||||
while(node) {
|
||||
unsigned length = 0, x = node->offset, y = outputOffset;
|
||||
while(y < targetSize && targetData[x++] == targetData[y++]) length++;
|
||||
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
//target tree append
|
||||
node = new Node;
|
||||
node->offset = outputOffset;
|
||||
node->next = targetTree[symbol];
|
||||
targetTree[symbol] = node;
|
||||
}
|
||||
|
||||
{ //target read
|
||||
if(maxLength < 4) {
|
||||
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
|
||||
mode = TargetRead;
|
||||
}
|
||||
}
|
||||
|
||||
if(mode != TargetRead) targetReadFlush();
|
||||
|
||||
switch(mode) {
|
||||
case SourceRead:
|
||||
encode(SourceRead | ((maxLength - 1) << 2));
|
||||
break;
|
||||
case TargetRead:
|
||||
//delay write to group sequential TargetRead commands into one
|
||||
targetReadLength += maxLength;
|
||||
break;
|
||||
case SourceCopy:
|
||||
case TargetCopy:
|
||||
encode(mode | ((maxLength - 1) << 2));
|
||||
signed relativeOffset;
|
||||
if(mode == SourceCopy) {
|
||||
relativeOffset = maxOffset - sourceRelativeOffset;
|
||||
sourceRelativeOffset = maxOffset + maxLength;
|
||||
} else {
|
||||
relativeOffset = maxOffset - targetRelativeOffset;
|
||||
targetRelativeOffset = maxOffset + maxLength;
|
||||
}
|
||||
encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
|
||||
break;
|
||||
}
|
||||
|
||||
outputOffset += maxLength;
|
||||
}
|
||||
|
||||
targetReadFlush();
|
||||
|
||||
sourceChecksum = ~sourceChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
|
||||
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
|
||||
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
|
||||
uint32_t outputChecksum = ~modifyChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||
|
||||
modifyFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
152
higan/phoenix/nall/beat/linear.hpp
Normal file
152
higan/phoenix/nall/beat/linear.hpp
Normal file
@ -0,0 +1,152 @@
|
||||
#ifndef NALL_BEAT_LINEAR_HPP
|
||||
#define NALL_BEAT_LINEAR_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpslinear {
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void target(const uint8_t *data, unsigned size);
|
||||
|
||||
inline bool source(const string &filename);
|
||||
inline bool target(const string &filename);
|
||||
inline bool create(const string &filename, const string &metadata = "");
|
||||
|
||||
protected:
|
||||
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
enum : unsigned { Granularity = 1 };
|
||||
|
||||
filemap sourceFile;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
|
||||
filemap targetFile;
|
||||
const uint8_t *targetData;
|
||||
unsigned targetSize;
|
||||
};
|
||||
|
||||
void bpslinear::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data;
|
||||
sourceSize = size;
|
||||
}
|
||||
|
||||
void bpslinear::target(const uint8_t *data, unsigned size) {
|
||||
targetData = data;
|
||||
targetSize = size;
|
||||
}
|
||||
|
||||
bool bpslinear::source(const string &filename) {
|
||||
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||
source(sourceFile.data(), sourceFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpslinear::target(const string &filename) {
|
||||
if(targetFile.open(filename, filemap::mode::read) == false) return false;
|
||||
target(targetFile.data(), targetFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpslinear::create(const string &filename, const string &metadata) {
|
||||
file modifyFile;
|
||||
if(modifyFile.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
uint32_t modifyChecksum = ~0;
|
||||
unsigned targetRelativeOffset = 0, outputOffset = 0;
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
modifyFile.write(data);
|
||||
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||
};
|
||||
|
||||
auto encode = [&](uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
};
|
||||
|
||||
unsigned targetReadLength = 0;
|
||||
|
||||
auto targetReadFlush = [&]() {
|
||||
if(targetReadLength) {
|
||||
encode(TargetRead | ((targetReadLength - 1) << 2));
|
||||
unsigned offset = outputOffset - targetReadLength;
|
||||
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
|
||||
}
|
||||
};
|
||||
|
||||
write('B');
|
||||
write('P');
|
||||
write('S');
|
||||
write('1');
|
||||
|
||||
encode(sourceSize);
|
||||
encode(targetSize);
|
||||
|
||||
unsigned markupSize = metadata.length();
|
||||
encode(markupSize);
|
||||
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
|
||||
|
||||
while(outputOffset < targetSize) {
|
||||
unsigned sourceLength = 0;
|
||||
for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
|
||||
if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
|
||||
sourceLength++;
|
||||
}
|
||||
|
||||
unsigned rleLength = 0;
|
||||
for(unsigned n = 1; outputOffset + n < targetSize; n++) {
|
||||
if(targetData[outputOffset] != targetData[outputOffset + n]) break;
|
||||
rleLength++;
|
||||
}
|
||||
|
||||
if(rleLength >= 4) {
|
||||
//write byte to repeat
|
||||
targetReadLength++;
|
||||
outputOffset++;
|
||||
targetReadFlush();
|
||||
|
||||
//copy starting from repetition byte
|
||||
encode(TargetCopy | ((rleLength - 1) << 2));
|
||||
unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset;
|
||||
encode(relativeOffset << 1);
|
||||
outputOffset += rleLength;
|
||||
targetRelativeOffset = outputOffset - 1;
|
||||
} else if(sourceLength >= 4) {
|
||||
targetReadFlush();
|
||||
encode(SourceRead | ((sourceLength - 1) << 2));
|
||||
outputOffset += sourceLength;
|
||||
} else {
|
||||
targetReadLength += Granularity;
|
||||
outputOffset += Granularity;
|
||||
}
|
||||
}
|
||||
|
||||
targetReadFlush();
|
||||
|
||||
uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
|
||||
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
|
||||
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
|
||||
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
|
||||
uint32_t outputChecksum = ~modifyChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||
|
||||
modifyFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
121
higan/phoenix/nall/beat/metadata.hpp
Normal file
121
higan/phoenix/nall/beat/metadata.hpp
Normal file
@ -0,0 +1,121 @@
|
||||
#ifndef NALL_BEAT_METADATA_HPP
|
||||
#define NALL_BEAT_METADATA_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpsmetadata {
|
||||
inline bool load(const string &filename);
|
||||
inline bool save(const string &filename, const string &metadata);
|
||||
inline string metadata() const;
|
||||
|
||||
protected:
|
||||
file sourceFile;
|
||||
string metadataString;
|
||||
};
|
||||
|
||||
bool bpsmetadata::load(const string &filename) {
|
||||
if(sourceFile.open(filename, file::mode::read) == false) return false;
|
||||
|
||||
auto read = [&]() -> uint8_t {
|
||||
return sourceFile.read();
|
||||
};
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
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;
|
||||
};
|
||||
|
||||
if(read() != 'B') return false;
|
||||
if(read() != 'P') return false;
|
||||
if(read() != 'S') return false;
|
||||
if(read() != '1') return false;
|
||||
decode();
|
||||
decode();
|
||||
unsigned metadataSize = decode();
|
||||
char data[metadataSize + 1];
|
||||
for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
|
||||
data[metadataSize] = 0;
|
||||
metadataString = (const char*)data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpsmetadata::save(const string &filename, const string &metadata) {
|
||||
file targetFile;
|
||||
if(targetFile.open(filename, file::mode::write) == false) return false;
|
||||
if(sourceFile.open() == false) return false;
|
||||
sourceFile.seek(0);
|
||||
|
||||
auto read = [&]() -> uint8_t {
|
||||
return sourceFile.read();
|
||||
};
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
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;
|
||||
};
|
||||
|
||||
uint32_t checksum = ~0;
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
targetFile.write(data);
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
};
|
||||
|
||||
auto encode = [&](uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
};
|
||||
|
||||
for(unsigned n = 0; n < 4; n++) write(read());
|
||||
encode(decode());
|
||||
encode(decode());
|
||||
unsigned sourceLength = decode();
|
||||
unsigned targetLength = metadata.length();
|
||||
encode(targetLength);
|
||||
sourceFile.seek(sourceLength, file::index::relative);
|
||||
for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
|
||||
unsigned length = sourceFile.size() - sourceFile.offset() - 4;
|
||||
for(unsigned n = 0; n < length; n++) write(read());
|
||||
uint32_t outputChecksum = ~checksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||
|
||||
targetFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
string bpsmetadata::metadata() const {
|
||||
return metadataString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
242
higan/phoenix/nall/beat/multi.hpp
Normal file
242
higan/phoenix/nall/beat/multi.hpp
Normal file
@ -0,0 +1,242 @@
|
||||
#ifndef NALL_BEAT_MULTI_HPP
|
||||
#define NALL_BEAT_MULTI_HPP
|
||||
|
||||
#include <nall/beat/patch.hpp>
|
||||
#include <nall/beat/linear.hpp>
|
||||
#include <nall/beat/delta.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpsmulti {
|
||||
enum : unsigned {
|
||||
CreatePath = 0,
|
||||
CreateFile = 1,
|
||||
ModifyFile = 2,
|
||||
MirrorFile = 3,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
OriginSource = 0,
|
||||
OriginTarget = 1,
|
||||
};
|
||||
|
||||
bool create(const string &patchName, const string &sourcePath, const string &targetPath, bool delta = false, const string &metadata = "") {
|
||||
if(fp.open()) fp.close();
|
||||
fp.open(patchName, file::mode::write);
|
||||
checksum = ~0;
|
||||
|
||||
writeString("BPM1"); //signature
|
||||
writeNumber(metadata.length());
|
||||
writeString(metadata);
|
||||
|
||||
lstring sourceList, targetList;
|
||||
ls(sourceList, sourcePath, sourcePath);
|
||||
ls(targetList, targetPath, targetPath);
|
||||
|
||||
for(auto &targetName : targetList) {
|
||||
if(targetName.endswith("/")) {
|
||||
targetName.rtrim<1>("/");
|
||||
writeNumber(CreatePath | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
} else if(auto position = sourceList.find(targetName)) { //if sourceName == targetName
|
||||
file sp, dp;
|
||||
sp.open({sourcePath, targetName}, file::mode::read);
|
||||
dp.open({targetPath, targetName}, file::mode::read);
|
||||
|
||||
bool identical = sp.size() == dp.size();
|
||||
uint32_t cksum = ~0;
|
||||
|
||||
for(unsigned n = 0; n < sp.size(); n++) {
|
||||
uint8_t byte = sp.read();
|
||||
if(identical && byte != dp.read()) identical = false;
|
||||
cksum = crc32_adjust(cksum, byte);
|
||||
}
|
||||
|
||||
if(identical) {
|
||||
writeNumber(MirrorFile | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
writeNumber(OriginSource);
|
||||
writeChecksum(~cksum);
|
||||
} else {
|
||||
writeNumber(ModifyFile | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
writeNumber(OriginSource);
|
||||
|
||||
if(delta == false) {
|
||||
bpslinear patch;
|
||||
patch.source({sourcePath, targetName});
|
||||
patch.target({targetPath, targetName});
|
||||
patch.create({temppath(), "temp.bps"});
|
||||
} else {
|
||||
bpsdelta patch;
|
||||
patch.source({sourcePath, targetName});
|
||||
patch.target({targetPath, targetName});
|
||||
patch.create({temppath(), "temp.bps"});
|
||||
}
|
||||
|
||||
auto buffer = file::read({temppath(), "temp.bps"});
|
||||
writeNumber(buffer.size());
|
||||
for(auto &byte : buffer) write(byte);
|
||||
}
|
||||
} else {
|
||||
writeNumber(CreateFile | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
auto buffer = file::read({targetPath, targetName});
|
||||
writeNumber(buffer.size());
|
||||
for(auto &byte : buffer) write(byte);
|
||||
writeChecksum(crc32_calculate(buffer.data(), buffer.size()));
|
||||
}
|
||||
}
|
||||
|
||||
//checksum
|
||||
writeChecksum(~checksum);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool apply(const string &patchName, const string &sourcePath, const string &targetPath) {
|
||||
directory::remove(targetPath); //start with a clean directory
|
||||
directory::create(targetPath);
|
||||
|
||||
if(fp.open()) fp.close();
|
||||
fp.open(patchName, file::mode::read);
|
||||
checksum = ~0;
|
||||
|
||||
if(readString(4) != "BPM1") return false;
|
||||
auto metadataLength = readNumber();
|
||||
while(metadataLength--) read();
|
||||
|
||||
while(fp.offset() < fp.size() - 4) {
|
||||
auto encoding = readNumber();
|
||||
unsigned action = encoding & 3;
|
||||
unsigned targetLength = (encoding >> 2) + 1;
|
||||
string targetName = readString(targetLength);
|
||||
|
||||
if(action == CreatePath) {
|
||||
directory::create({targetPath, targetName, "/"});
|
||||
} else if(action == CreateFile) {
|
||||
file fp;
|
||||
fp.open({targetPath, targetName}, file::mode::write);
|
||||
auto fileSize = readNumber();
|
||||
while(fileSize--) fp.write(read());
|
||||
uint32_t cksum = readChecksum();
|
||||
} else if(action == ModifyFile) {
|
||||
auto encoding = readNumber();
|
||||
string originPath = encoding & 1 ? targetPath : sourcePath;
|
||||
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
|
||||
auto patchSize = readNumber();
|
||||
vector<uint8_t> buffer;
|
||||
buffer.resize(patchSize);
|
||||
for(unsigned n = 0; n < patchSize; n++) buffer[n] = read();
|
||||
bpspatch patch;
|
||||
patch.modify(buffer.data(), buffer.size());
|
||||
patch.source({originPath, sourceName});
|
||||
patch.target({targetPath, targetName});
|
||||
if(patch.apply() != bpspatch::result::success) return false;
|
||||
} else if(action == MirrorFile) {
|
||||
auto encoding = readNumber();
|
||||
string originPath = encoding & 1 ? targetPath : sourcePath;
|
||||
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
|
||||
file::copy({originPath, sourceName}, {targetPath, targetName});
|
||||
uint32_t cksum = readChecksum();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cksum = ~checksum;
|
||||
if(read() != (uint8_t)(cksum >> 0)) return false;
|
||||
if(read() != (uint8_t)(cksum >> 8)) return false;
|
||||
if(read() != (uint8_t)(cksum >> 16)) return false;
|
||||
if(read() != (uint8_t)(cksum >> 24)) return false;
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
file fp;
|
||||
uint32_t checksum;
|
||||
|
||||
//create() functions
|
||||
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) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
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 cksum) {
|
||||
write(cksum >> 0);
|
||||
write(cksum >> 8);
|
||||
write(cksum >> 16);
|
||||
write(cksum >> 24);
|
||||
}
|
||||
|
||||
//apply() functions
|
||||
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] = read();
|
||||
text[length] = 0;
|
||||
return text;
|
||||
}
|
||||
|
||||
uint32_t readChecksum() {
|
||||
uint32_t checksum = 0;
|
||||
checksum |= read() << 0;
|
||||
checksum |= read() << 8;
|
||||
checksum |= read() << 16;
|
||||
checksum |= read() << 24;
|
||||
return checksum;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
219
higan/phoenix/nall/beat/patch.hpp
Normal file
219
higan/phoenix/nall/beat/patch.hpp
Normal file
@ -0,0 +1,219 @@
|
||||
#ifndef NALL_BEAT_PATCH_HPP
|
||||
#define NALL_BEAT_PATCH_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpspatch {
|
||||
inline bool modify(const uint8_t *data, unsigned size);
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void target(uint8_t *data, unsigned size);
|
||||
|
||||
inline bool modify(const string &filename);
|
||||
inline bool source(const string &filename);
|
||||
inline bool target(const string &filename);
|
||||
|
||||
inline string metadata() const;
|
||||
inline unsigned size() const;
|
||||
|
||||
enum result : unsigned {
|
||||
unknown,
|
||||
success,
|
||||
patch_too_small,
|
||||
patch_invalid_header,
|
||||
source_too_small,
|
||||
target_too_small,
|
||||
source_checksum_invalid,
|
||||
target_checksum_invalid,
|
||||
patch_checksum_invalid,
|
||||
};
|
||||
|
||||
inline result apply();
|
||||
|
||||
protected:
|
||||
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
|
||||
filemap modifyFile;
|
||||
const uint8_t *modifyData;
|
||||
unsigned modifySize;
|
||||
|
||||
filemap sourceFile;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
|
||||
filemap targetFile;
|
||||
uint8_t *targetData;
|
||||
unsigned targetSize;
|
||||
|
||||
unsigned modifySourceSize;
|
||||
unsigned modifyTargetSize;
|
||||
unsigned modifyMarkupSize;
|
||||
string metadataString;
|
||||
};
|
||||
|
||||
bool bpspatch::modify(const uint8_t *data, unsigned size) {
|
||||
if(size < 19) return false;
|
||||
modifyData = data;
|
||||
modifySize = size;
|
||||
|
||||
unsigned offset = 4;
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t data = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = modifyData[offset++];
|
||||
data += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
modifySourceSize = decode();
|
||||
modifyTargetSize = decode();
|
||||
modifyMarkupSize = decode();
|
||||
|
||||
char buffer[modifyMarkupSize + 1];
|
||||
for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
|
||||
buffer[modifyMarkupSize] = 0;
|
||||
metadataString = (const char*)buffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bpspatch::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data;
|
||||
sourceSize = size;
|
||||
}
|
||||
|
||||
void bpspatch::target(uint8_t *data, unsigned size) {
|
||||
targetData = data;
|
||||
targetSize = size;
|
||||
}
|
||||
|
||||
bool bpspatch::modify(const string &filename) {
|
||||
if(modifyFile.open(filename, filemap::mode::read) == false) return false;
|
||||
return modify(modifyFile.data(), modifyFile.size());
|
||||
}
|
||||
|
||||
bool bpspatch::source(const string &filename) {
|
||||
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||
source(sourceFile.data(), sourceFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpspatch::target(const string &filename) {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
fp.truncate(modifyTargetSize);
|
||||
fp.close();
|
||||
|
||||
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
|
||||
target(targetFile.data(), targetFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
string bpspatch::metadata() const {
|
||||
return metadataString;
|
||||
}
|
||||
|
||||
unsigned bpspatch::size() const {
|
||||
return modifyTargetSize;
|
||||
}
|
||||
|
||||
bpspatch::result bpspatch::apply() {
|
||||
if(modifySize < 19) return result::patch_too_small;
|
||||
|
||||
uint32_t modifyChecksum = ~0, targetChecksum = ~0;
|
||||
unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
|
||||
|
||||
auto read = [&]() -> uint8_t {
|
||||
uint8_t data = modifyData[modifyOffset++];
|
||||
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||
return data;
|
||||
};
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
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;
|
||||
};
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
targetData[outputOffset++] = data;
|
||||
targetChecksum = crc32_adjust(targetChecksum, data);
|
||||
};
|
||||
|
||||
if(read() != 'B') return result::patch_invalid_header;
|
||||
if(read() != 'P') return result::patch_invalid_header;
|
||||
if(read() != 'S') return result::patch_invalid_header;
|
||||
if(read() != '1') return result::patch_invalid_header;
|
||||
|
||||
modifySourceSize = decode();
|
||||
modifyTargetSize = decode();
|
||||
modifyMarkupSize = decode();
|
||||
for(unsigned n = 0; n < modifyMarkupSize; n++) read();
|
||||
|
||||
if(modifySourceSize > sourceSize) return result::source_too_small;
|
||||
if(modifyTargetSize > targetSize) return result::target_too_small;
|
||||
|
||||
while(modifyOffset < modifySize - 12) {
|
||||
unsigned length = decode();
|
||||
unsigned mode = length & 3;
|
||||
length = (length >> 2) + 1;
|
||||
|
||||
switch(mode) {
|
||||
case SourceRead:
|
||||
while(length--) write(sourceData[outputOffset]);
|
||||
break;
|
||||
case TargetRead:
|
||||
while(length--) write(read());
|
||||
break;
|
||||
case SourceCopy:
|
||||
case TargetCopy:
|
||||
signed offset = decode();
|
||||
bool negative = offset & 1;
|
||||
offset >>= 1;
|
||||
if(negative) offset = -offset;
|
||||
|
||||
if(mode == SourceCopy) {
|
||||
sourceRelativeOffset += offset;
|
||||
while(length--) write(sourceData[sourceRelativeOffset++]);
|
||||
} else {
|
||||
targetRelativeOffset += offset;
|
||||
while(length--) write(targetData[targetRelativeOffset++]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
|
||||
for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
|
||||
for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
|
||||
uint32_t checksum = ~modifyChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
|
||||
|
||||
uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
|
||||
targetChecksum = ~targetChecksum;
|
||||
|
||||
if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
|
||||
if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
|
||||
if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
|
||||
|
||||
return result::success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
82
higan/phoenix/nall/bit.hpp
Normal file
82
higan/phoenix/nall/bit.hpp
Normal file
@ -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
|
100
higan/phoenix/nall/bmp.hpp
Normal file
100
higan/phoenix/nall/bmp.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
#ifndef NALL_BMP_HPP
|
||||
#define NALL_BMP_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
|
||||
//BMP reader / writer
|
||||
//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
|
152
higan/phoenix/nall/compositor.hpp
Normal file
152
higan/phoenix/nall/compositor.hpp
Normal file
@ -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) return false;
|
||||
|
||||
char buffer[512];
|
||||
if(!fgets(buffer, sizeof buffer, fp)) 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) 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) return false;
|
||||
|
||||
char buffer[512];
|
||||
if(!fgets(buffer, sizeof buffer, fp)) 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) 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 == nullptr) module = LoadLibraryW(L"dwmapi");
|
||||
if(module == nullptr) return false;
|
||||
|
||||
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
|
||||
if(pDwmIsCompositionEnabled == nullptr) 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 == nullptr) module = LoadLibraryW(L"dwmapi");
|
||||
if(module == nullptr) return false;
|
||||
|
||||
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
|
||||
if(pDwmEnableComposition == nullptr) 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
|
126
higan/phoenix/nall/config.hpp
Normal file
126
higan/phoenix/nall/config.hpp
Normal file
@ -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
|
25
higan/phoenix/nall/crc16.hpp
Normal file
25
higan/phoenix/nall/crc16.hpp
Normal file
@ -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
|
66
higan/phoenix/nall/crc32.hpp
Normal file
66
higan/phoenix/nall/crc32.hpp
Normal file
@ -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
|
224
higan/phoenix/nall/directory.hpp
Normal file
224
higan/phoenix/nall/directory.hpp
Normal file
@ -0,0 +1,224 @@
|
||||
#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 = "*") {
|
||||
lstring folders = directory::ufolders(pathname, pattern);
|
||||
folders.sort();
|
||||
return folders;
|
||||
}
|
||||
|
||||
static lstring files(const string &pathname, const string &pattern = "*") {
|
||||
lstring files = directory::ufiles(pathname, pattern);
|
||||
files.sort();
|
||||
return files;
|
||||
}
|
||||
|
||||
static lstring contents(const string &pathname, const string &pattern = "*") {
|
||||
lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files
|
||||
lstring files = directory::ufiles(pathname, pattern);
|
||||
folders.sort();
|
||||
files.sort();
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
|
||||
static lstring ifolders(const string &pathname, const string &pattern = "*") {
|
||||
lstring folders = ufolders(pathname, pattern);
|
||||
folders.isort();
|
||||
return folders;
|
||||
}
|
||||
|
||||
static lstring ifiles(const string &pathname, const string &pattern = "*") {
|
||||
lstring files = ufiles(pathname, pattern);
|
||||
files.isort();
|
||||
return files;
|
||||
}
|
||||
|
||||
static lstring icontents(const string &pathname, const string &pattern = "*") {
|
||||
lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files
|
||||
lstring files = directory::ufiles(pathname, pattern);
|
||||
folders.isort();
|
||||
files.isort();
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
|
||||
private:
|
||||
//internal functions; these return unsorted lists
|
||||
static lstring ufolders(const string &pathname, const string &pattern = "*");
|
||||
static lstring ufiles(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::ufolders(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);
|
||||
}
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::ufiles(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);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
#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::ufolders(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);
|
||||
}
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::ufiles(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);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
117
higan/phoenix/nall/dl.hpp
Normal file
117
higan/phoenix/nall/dl.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
#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 {
|
||||
explicit operator bool() const { return open(); }
|
||||
bool open() 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 nullptr;
|
||||
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 nullptr;
|
||||
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 nullptr;
|
||||
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 bool library::open_absolute(const char*) { return false; }
|
||||
inline void* library::sym(const char*) { return nullptr; }
|
||||
inline void library::close() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
13
higan/phoenix/nall/dsp.hpp
Normal file
13
higan/phoenix/nall/dsp.hpp
Normal file
@ -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
|
52
higan/phoenix/nall/dsp/buffer.hpp
Normal file
52
higan/phoenix/nall/dsp/buffer.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct Buffer {
|
||||
double **sample = nullptr;
|
||||
uint16_t rdoffset = 0;
|
||||
uint16_t wroffset = 0;
|
||||
unsigned channels = 0;
|
||||
|
||||
void setChannels(unsigned channels) {
|
||||
if(sample) {
|
||||
for(unsigned c = 0; c < this->channels; c++) {
|
||||
if(sample[c]) delete[] sample[c];
|
||||
}
|
||||
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() {
|
||||
}
|
||||
|
||||
~Buffer() {
|
||||
setChannels(0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
168
higan/phoenix/nall/dsp/core.hpp
Normal file
168
higan/phoenix/nall/dsp/core.hpp
Normal file
@ -0,0 +1,168 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
#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 = nullptr;
|
||||
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
|
72
higan/phoenix/nall/dsp/resample/average.hpp
Normal file
72
higan/phoenix/nall/dsp/resample/average.hpp
Normal file
@ -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
|
44
higan/phoenix/nall/dsp/resample/cosine.hpp
Normal file
44
higan/phoenix/nall/dsp/resample/cosine.hpp
Normal file
@ -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
|
50
higan/phoenix/nall/dsp/resample/cubic.hpp
Normal file
50
higan/phoenix/nall/dsp/resample/cubic.hpp
Normal file
@ -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
|
62
higan/phoenix/nall/dsp/resample/hermite.hpp
Normal file
62
higan/phoenix/nall/dsp/resample/hermite.hpp
Normal file
@ -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
|
600
higan/phoenix/nall/dsp/resample/lib/sinc.hpp
Normal file
600
higan/phoenix/nall/dsp/resample/lib/sinc.hpp
Normal file
@ -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 *)nullptr;
|
||||
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;
|
||||
}
|
43
higan/phoenix/nall/dsp/resample/linear.hpp
Normal file
43
higan/phoenix/nall/dsp/resample/linear.hpp
Normal file
@ -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
|
43
higan/phoenix/nall/dsp/resample/nearest.hpp
Normal file
43
higan/phoenix/nall/dsp/resample/nearest.hpp
Normal file
@ -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
|
54
higan/phoenix/nall/dsp/resample/sinc.hpp
Normal file
54
higan/phoenix/nall/dsp/resample/sinc.hpp
Normal file
@ -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] = nullptr;
|
||||
}
|
||||
|
||||
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
|
50
higan/phoenix/nall/dsp/settings.hpp
Normal file
50
higan/phoenix/nall/dsp/settings.hpp
Normal file
@ -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
|
103
higan/phoenix/nall/emulation/super-famicom-usart.hpp
Normal file
103
higan/phoenix/nall/emulation/super-famicom-usart.hpp
Normal file
@ -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
|
42
higan/phoenix/nall/endian.hpp
Normal file
42
higan/phoenix/nall/endian.hpp
Normal file
@ -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
|
352
higan/phoenix/nall/file.hpp
Normal file
352
higan/phoenix/nall/file.hpp
Normal file
@ -0,0 +1,352 @@
|
||||
#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 string &text) {
|
||||
file fp;
|
||||
if(fp.open(filename, mode::write) == false) return false;
|
||||
fp.print(text);
|
||||
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 bool create(const string &filename) {
|
||||
//create an empty file (will replace existing files)
|
||||
file fp;
|
||||
if(fp.open(filename, mode::write) == false) return false;
|
||||
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;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return open();
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
}
|
||||
|
||||
file() {
|
||||
}
|
||||
|
||||
file(const string &filename, mode mode_) {
|
||||
open(filename, mode_);
|
||||
}
|
||||
|
||||
~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] = {0};
|
||||
int buffer_offset = -1; //invalidate buffer
|
||||
bool buffer_dirty = false;
|
||||
FILE *fp = nullptr;
|
||||
unsigned file_offset = 0;
|
||||
unsigned file_size = 0;
|
||||
mode file_mode = mode::read;
|
||||
|
||||
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
|
214
higan/phoenix/nall/filemap.hpp
Normal file
214
higan/phoenix/nall/filemap.hpp
Normal file
@ -0,0 +1,214 @@
|
||||
#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 };
|
||||
|
||||
explicit operator bool() const { return open(); }
|
||||
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(nullptr) { p_ctor(); }
|
||||
filemap(const char *filename, mode mode_) : p_size(0), p_handle(nullptr) { 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 = nullptr;
|
||||
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, nullptr,
|
||||
creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if(p_filehandle == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
p_size = GetFileSize(p_filehandle, nullptr);
|
||||
|
||||
p_maphandle = CreateFileMapping(p_filehandle, nullptr, flprotect, 0, p_size, nullptr);
|
||||
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 = nullptr;
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
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(nullptr, p_size, mmap_flags, MAP_SHARED, p_fd, 0);
|
||||
if(p_handle == MAP_FAILED) {
|
||||
p_handle = nullptr;
|
||||
::close(p_fd);
|
||||
p_fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return p_handle;
|
||||
}
|
||||
|
||||
void p_close() {
|
||||
if(p_handle) {
|
||||
munmap(p_handle, p_size);
|
||||
p_handle = nullptr;
|
||||
}
|
||||
|
||||
if(p_fd >= 0) {
|
||||
::close(p_fd);
|
||||
p_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void p_ctor() {
|
||||
p_fd = -1;
|
||||
}
|
||||
|
||||
void p_dtor() {
|
||||
p_close();
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
60
higan/phoenix/nall/function.hpp
Normal file
60
higan/phoenix/nall/function.hpp
Normal file
@ -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:
|
||||
explicit 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
|
85
higan/phoenix/nall/gzip.hpp
Normal file
85
higan/phoenix/nall/gzip.hpp
Normal file
@ -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
|
176
higan/phoenix/nall/http.hpp
Normal file
176
higan/phoenix/nall/http.hpp
Normal file
@ -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 = nullptr;
|
||||
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 = nullptr;
|
||||
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
|
539
higan/phoenix/nall/image.hpp
Normal file
539
higan/phoenix/nall/image.hpp
Normal file
@ -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
|
358
higan/phoenix/nall/inflate.hpp
Normal file
358
higan/phoenix/nall/inflate.hpp
Normal file
@ -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 != nullptr) {
|
||||
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 != nullptr) {
|
||||
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 != nullptr) {
|
||||
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
|
386
higan/phoenix/nall/input.hpp
Normal file
386
higan/phoenix/nall/input.hpp
Normal file
@ -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
|
59
higan/phoenix/nall/interpolation.hpp
Normal file
59
higan/phoenix/nall/interpolation.hpp
Normal file
@ -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
|
66
higan/phoenix/nall/intrinsics.hpp
Normal file
66
higan/phoenix/nall/intrinsics.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef NALL_INTRINSICS_HPP
|
||||
#define NALL_INTRINSICS_HPP
|
||||
|
||||
struct Intrinsics {
|
||||
enum class Compiler : unsigned { Clang, 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(__clang__)
|
||||
#define COMPILER_CLANG
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Clang; }
|
||||
#elif 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
|
60
higan/phoenix/nall/invoke.hpp
Normal file
60
higan/phoenix/nall/invoke.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
#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/intrinsics.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_X)
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename... Args>
|
||||
inline void invoke(const string &name, Args&&... args) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
100
higan/phoenix/nall/ips.hpp
Normal file
100
higan/phoenix/nall/ips.hpp
Normal file
@ -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
|
165
higan/phoenix/nall/lzss.hpp
Normal file
165
higan/phoenix/nall/lzss.hpp
Normal file
@ -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] = nullptr;
|
||||
|
||||
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 = nullptr; }
|
||||
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
|
117
higan/phoenix/nall/map.hpp
Normal file
117
higan/phoenix/nall/map.hpp
Normal file
@ -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
|
10
higan/phoenix/nall/mosaic.hpp
Normal file
10
higan/phoenix/nall/mosaic.hpp
Normal file
@ -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
|
55
higan/phoenix/nall/mosaic/bitstream.hpp
Normal file
55
higan/phoenix/nall/mosaic/bitstream.hpp
Normal file
@ -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
|
224
higan/phoenix/nall/mosaic/context.hpp
Normal file
224
higan/phoenix/nall/mosaic/context.hpp
Normal file
@ -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
|
126
higan/phoenix/nall/mosaic/parser.hpp
Normal file
126
higan/phoenix/nall/mosaic/parser.hpp
Normal file
@ -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
|
59
higan/phoenix/nall/nall.hpp
Normal file
59
higan/phoenix/nall/nall.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
#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/thread.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
|
96
higan/phoenix/nall/platform.hpp
Normal file
96
higan/phoenix/nall/platform.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
#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(__clang__) || 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
|
||||
|
||||
//===========
|
||||
//unreachable
|
||||
//===========
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define unreachable __builtin_unreachable()
|
||||
#else
|
||||
#define unreachable throw
|
||||
#endif
|
||||
|
||||
#endif
|
337
higan/phoenix/nall/png.hpp
Normal file
337
higan/phoenix/nall/png.hpp
Normal file
@ -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 = nullptr;
|
||||
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 = nullptr;
|
||||
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 = nullptr;
|
||||
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
|
109
higan/phoenix/nall/priority-queue.hpp
Normal file
109
higan/phoenix/nall/priority-queue.hpp
Normal file
@ -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
|
83
higan/phoenix/nall/property.hpp
Normal file
83
higan/phoenix/nall/property.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
#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.
|
||||
//
|
||||
//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 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 C;
|
||||
};
|
||||
|
||||
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 C;
|
||||
};
|
||||
|
||||
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
|
32
higan/phoenix/nall/public-cast.hpp
Normal file
32
higan/phoenix/nall/public-cast.hpp
Normal file
@ -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
|
28
higan/phoenix/nall/random.hpp
Normal file
28
higan/phoenix/nall/random.hpp
Normal file
@ -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
|
110
higan/phoenix/nall/serial.hpp
Normal file
110
higan/phoenix/nall/serial.hpp
Normal file
@ -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
|
146
higan/phoenix/nall/serializer.hpp
Normal file
146
higan/phoenix/nall/serializer.hpp
Normal file
@ -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(nullptr) {
|
||||
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 = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
serializer(serializer &&s) {
|
||||
operator=(std::move(s));
|
||||
}
|
||||
|
||||
//construction
|
||||
serializer() {
|
||||
imode = Size;
|
||||
idata = nullptr;
|
||||
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
|
158
higan/phoenix/nall/set.hpp
Normal file
158
higan/phoenix/nall/set.hpp
Normal file
@ -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
|
145
higan/phoenix/nall/sha256.hpp
Normal file
145
higan/phoenix/nall/sha256.hpp
Normal file
@ -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
|
77
higan/phoenix/nall/sort.hpp
Normal file
77
higan/phoenix/nall/sort.hpp
Normal file
@ -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(copy, list[j])) 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[right], list[left])) {
|
||||
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
|
42
higan/phoenix/nall/stdint.hpp
Normal file
42
higan/phoenix/nall/stdint.hpp
Normal file
@ -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
|
26
higan/phoenix/nall/stream.hpp
Normal file
26
higan/phoenix/nall/stream.hpp
Normal file
@ -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
|
25
higan/phoenix/nall/stream/auto.hpp
Normal file
25
higan/phoenix/nall/stream/auto.hpp
Normal file
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user