mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r65 release.
byuu says: This synchronizes bsnes/higan with many recent internal nall changes. This will be the last WIP until I am situated in Japan. Apologies for the bugfixes that didn't get applied yet, I ran out of time.
This commit is contained in:
parent
336d20123f
commit
03b06257d3
|
@ -55,3 +55,5 @@ else ifneq ($(filter $(platform),linux bsd),)
|
||||||
rm -f $(prefix)/share/applications/$(name).desktop
|
rm -f $(prefix)/share/applications/$(name).desktop
|
||||||
rm -f $(prefix)/share/icons/$(name).png
|
rm -f $(prefix)/share/icons/$(name).png
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
-include obj/*.d
|
||||||
|
|
|
@ -148,7 +148,7 @@ auto ListWindow::loadDatabase(string location) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ListWindow::saveDatabase(string location) -> void {
|
auto ListWindow::saveDatabase(string location) -> void {
|
||||||
file fp{location, file::mode::write};
|
auto fp = file::open(location, file::mode::write);
|
||||||
if(!fp) return MessageDialog().setParent(*this).setText({
|
if(!fp) return MessageDialog().setParent(*this).setText({
|
||||||
"Error: failed to write file.\n\n",
|
"Error: failed to write file.\n\n",
|
||||||
"Name: ", location
|
"Name: ", location
|
||||||
|
@ -618,24 +618,12 @@ auto hiro::initialize() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <nall/main.hpp>
|
#include <nall/main.hpp>
|
||||||
auto nall::main(vector<string> arguments) -> void {
|
auto nall::main(Arguments) -> void {
|
||||||
new ListWindow;
|
new ListWindow;
|
||||||
new GameWindow;
|
new GameWindow;
|
||||||
new MemoryWindow;
|
new MemoryWindow;
|
||||||
new OscillatorWindow;
|
new OscillatorWindow;
|
||||||
|
|
||||||
//internal command used to synchronize all genius databases from an old format to a new format
|
|
||||||
//if enabled, use with extreme caution and make backups first
|
|
||||||
/*if(arguments.size() == 3 && arguments[1] == "--sync") {
|
|
||||||
for(auto& filename : directory::contents(arguments[2], "*.bml")) {
|
|
||||||
if(filename.beginsWith("Boards")) continue;
|
|
||||||
print(filename, "\n");
|
|
||||||
listWindow->loadDatabase({arguments[2], filename});
|
|
||||||
listWindow->saveDatabase({arguments[2], filename});
|
|
||||||
}
|
|
||||||
return print("[Done]\n");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
listWindow->setVisible();
|
listWindow->setVisible();
|
||||||
Application::run();
|
Application::run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "106.64";
|
static const string Version = "106.65";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
|
|
@ -67,7 +67,7 @@ auto APU::power() -> void {
|
||||||
phase = 0;
|
phase = 0;
|
||||||
cycle = 0;
|
cycle = 0;
|
||||||
|
|
||||||
PRNG prng;
|
PRNG::PCG prng;
|
||||||
for(auto& n : wave.pattern) n = prng.random();
|
for(auto& n : wave.pattern) n = prng.random();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ auto BSMemory::load() -> bool {
|
||||||
for(auto& byte : page.buffer[1]) byte = random();
|
for(auto& byte : page.buffer[1]) byte = random();
|
||||||
|
|
||||||
for(auto& block : blocks) {
|
for(auto& block : blocks) {
|
||||||
block.erased = 0;
|
block.erased = 1;
|
||||||
block.locked = 1;
|
block.locked = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,9 @@ auto hiro::initialize() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <nall/main.hpp>
|
#include <nall/main.hpp>
|
||||||
auto nall::main(vector<string> arguments) -> void {
|
auto nall::main(Arguments arguments) -> void {
|
||||||
settings.location = locate("settings.bml");
|
settings.location = locate("settings.bml");
|
||||||
|
|
||||||
arguments.takeLeft(); //ignore program location in argument parsing
|
|
||||||
for(auto argument : arguments) {
|
for(auto argument : arguments) {
|
||||||
if(argument == "--fullscreen") {
|
if(argument == "--fullscreen") {
|
||||||
presentation.startFullScreen = true;
|
presentation.startFullScreen = true;
|
||||||
|
|
|
@ -123,7 +123,7 @@ auto Program::openRomSuperFamicom(string name, vfs::file::mode mode) -> vfs::sha
|
||||||
return vfs::fs::file::open({Location::notsuffix(superFamicom.location), ".msu"}, mode);
|
return vfs::fs::file::open({Location::notsuffix(superFamicom.location), ".msu"}, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(name.match("msu1/track-*.pcm")) {
|
if(name.match("msu1/track*.pcm")) {
|
||||||
name.trimLeft("msu1/track", 1L);
|
name.trimLeft("msu1/track", 1L);
|
||||||
return vfs::fs::file::open({Location::notsuffix(superFamicom.location), name}, mode);
|
return vfs::fs::file::open({Location::notsuffix(superFamicom.location), name}, mode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ auto Program::saveState(string filename) -> bool {
|
||||||
if(gamePath().endsWith("/")) {
|
if(gamePath().endsWith("/")) {
|
||||||
string location = {statePath(), filename, ".bst"};
|
string location = {statePath(), filename, ".bst"};
|
||||||
directory::create(Location::path(location));
|
directory::create(Location::path(location));
|
||||||
if(!file::write(location, saveState.data(), saveState.size())) {
|
if(!file::write(location, saveState)) {
|
||||||
return showMessage({"Unable to write [", prefix, "] to disk"}), false;
|
return showMessage({"Unable to write [", prefix, "] to disk"}), false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,7 +18,7 @@ auto hiro::initialize() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <nall/main.hpp>
|
#include <nall/main.hpp>
|
||||||
auto nall::main(vector<string> arguments) -> void {
|
auto nall::main(Arguments arguments) -> void {
|
||||||
new Program(arguments);
|
new Program(arguments);
|
||||||
Application::run();
|
Application::run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
#include <nall/nall.hpp>
|
||||||
|
using namespace nall;
|
||||||
|
|
||||||
#include <ruby/ruby.hpp>
|
#include <ruby/ruby.hpp>
|
||||||
using namespace ruby;
|
using namespace ruby;
|
||||||
extern unique_pointer<Video> video;
|
extern unique_pointer<Video> video;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "utility.cpp"
|
#include "utility.cpp"
|
||||||
unique_pointer<Program> program;
|
unique_pointer<Program> program;
|
||||||
|
|
||||||
Program::Program(vector<string> arguments) {
|
Program::Program(Arguments arguments) {
|
||||||
program = this;
|
program = this;
|
||||||
|
|
||||||
Emulator::platform = this;
|
Emulator::platform = this;
|
||||||
|
@ -89,8 +89,7 @@ Program::Program(vector<string> arguments) {
|
||||||
updateAudioDriver();
|
updateAudioDriver();
|
||||||
updateAudioEffects();
|
updateAudioEffects();
|
||||||
|
|
||||||
arguments.takeFirst(); //ignore program location in argument parsing
|
for(auto argument : arguments) {
|
||||||
for(auto& argument : arguments) {
|
|
||||||
if(argument == "--fullscreen") {
|
if(argument == "--fullscreen") {
|
||||||
presentation->toggleFullScreen();
|
presentation->toggleFullScreen();
|
||||||
} else if(directory::exists(argument.split("|", 1L).right())) {
|
} else if(directory::exists(argument.split("|", 1L).right())) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
struct Program : Emulator::Platform {
|
struct Program : Emulator::Platform {
|
||||||
//program.cpp
|
//program.cpp
|
||||||
Program(vector<string> arguments);
|
Program(Arguments arguments);
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto quit() -> void;
|
auto quit() -> void;
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ auto Program::loadState(uint slot, bool managed) -> bool {
|
||||||
string type = managed ? "managed" : "quick";
|
string type = managed ? "managed" : "quick";
|
||||||
auto location = stateName(slot, managed);
|
auto location = stateName(slot, managed);
|
||||||
auto memory = file::read(location);
|
auto memory = file::read(location);
|
||||||
if(memory.size() == 0) return showMessage({"Slot ", slot, " ", type, " state does not exist"}), false;
|
if(!memory) return showMessage({"Slot ", slot, " ", type, " state does not exist"}), false;
|
||||||
serializer s(memory.data(), memory.size());
|
serializer s(memory.data(), memory.size());
|
||||||
if(emulator->unserialize(s) == false) return showMessage({"Slot ", slot, " ", type, " state incompatible"}), false;
|
if(!emulator->unserialize(s)) return showMessage({"Slot ", slot, " ", type, " state incompatible"}), false;
|
||||||
return showMessage({"Loaded ", type, " state from slot ", slot}), true;
|
return showMessage({"Loaded ", type, " state from slot ", slot}), true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +22,8 @@ auto Program::saveState(uint slot, bool managed) -> bool {
|
||||||
string type = managed ? "managed" : "quick";
|
string type = managed ? "managed" : "quick";
|
||||||
auto location = stateName(slot, managed);
|
auto location = stateName(slot, managed);
|
||||||
serializer s = emulator->serialize();
|
serializer s = emulator->serialize();
|
||||||
if(s.size() == 0) return showMessage({"Failed to save ", type, " state to slot ", slot}), false;
|
if(!s) return showMessage({"Failed to save ", type, " state to slot ", slot}), false;
|
||||||
directory::create(Location::path(location));
|
directory::create(Location::path(location));
|
||||||
if(file::write(location, s.data(), s.size()) == false) {
|
if(!file::write(location, {s.data(), s.size()})) return showMessage({"Unable to write ", type, " state to slot ", slot}), false;
|
||||||
return showMessage({"Unable to write ", type, " state to slot ", slot}), false;
|
|
||||||
}
|
|
||||||
return showMessage({"Saved ", type, " state to slot ", slot}), true;
|
return showMessage({"Saved ", type, " state to slot ", slot}), true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
database
|
database
|
||||||
revision: 2018-05-17
|
revision: 2018-09-20
|
||||||
|
|
||||||
//BS Memory (JPN)
|
//BS Memory (JPN)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
database
|
database
|
||||||
revision: 2018-05-17
|
revision: 2018-09-20
|
||||||
|
|
||||||
//Sufami Turbo (JPN)
|
//Sufami Turbo (JPN)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
database
|
database
|
||||||
revision: 2018-05-17
|
revision: 2018-09-20
|
||||||
|
|
||||||
//Prototypes (JPN)
|
//Prototypes (JPN)
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ game
|
||||||
//Super Famicom (JPN)
|
//Super Famicom (JPN)
|
||||||
|
|
||||||
database
|
database
|
||||||
revision: 2018-05-17
|
revision: 2018-09-20
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: 5c4e283efc338958b8dd45ebd6daf133a9eb280420a98e2e1df358ae0242c366
|
sha256: 5c4e283efc338958b8dd45ebd6daf133a9eb280420a98e2e1df358ae0242c366
|
||||||
|
@ -1002,6 +1002,18 @@ game
|
||||||
content: Program
|
content: Program
|
||||||
note: Custom wiring on PCB
|
note: Custom wiring on PCB
|
||||||
|
|
||||||
|
game
|
||||||
|
sha256: 76f80cdf704a0e1daf1af5bbf564e427b425a5ee42329417de6f29219fe63e5f
|
||||||
|
label: ロックマンエックス
|
||||||
|
name: Rockman X
|
||||||
|
region: SHVC-RX
|
||||||
|
revision: SHVC-RX-1
|
||||||
|
board: SHVC-2A0N-11
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x180000
|
||||||
|
content: Program
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: 6dfc016c571a16e5d42045060b1a88b6f3da5831e05b33c22035e1d990deccf3
|
sha256: 6dfc016c571a16e5d42045060b1a88b6f3da5831e05b33c22035e1d990deccf3
|
||||||
label: ロマンシング サ・ガ3 体験版サンプルROM
|
label: ロマンシング サ・ガ3 体験版サンプルROM
|
||||||
|
@ -1321,6 +1333,22 @@ game
|
||||||
size: 0x2000
|
size: 0x2000
|
||||||
content: Save
|
content: Save
|
||||||
|
|
||||||
|
game
|
||||||
|
sha256: b3204162def67b0dc40097344074e9b660ed296e5b5e22e778f373f0b985645b
|
||||||
|
label: スーパー麻雀大会
|
||||||
|
name: Super Mahjong Taikai
|
||||||
|
region: SHVC-IQ
|
||||||
|
revision: SHVC-IQ-2
|
||||||
|
board: SHVC-1A3B-13
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x80000
|
||||||
|
content: Program
|
||||||
|
memory
|
||||||
|
type: RAM
|
||||||
|
size: 0x2000
|
||||||
|
content: Save
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: 15d1187d17fa10c77152f691197d33674e64c33a1f8ceb37e8570588be507b89
|
sha256: 15d1187d17fa10c77152f691197d33674e64c33a1f8ceb37e8570588be507b89
|
||||||
label: スーパー麻雀大会
|
label: スーパー麻雀大会
|
||||||
|
@ -5733,7 +5761,7 @@ game
|
||||||
//Super Nintendo (USA)
|
//Super Nintendo (USA)
|
||||||
|
|
||||||
database
|
database
|
||||||
revision: 2018-05-06
|
revision: 2018-09-20
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: 2ffe8828480f943056fb1ab5c3c84d48a0bf8cbe3ed7c9960b349b59adb07f3b
|
sha256: 2ffe8828480f943056fb1ab5c3c84d48a0bf8cbe3ed7c9960b349b59adb07f3b
|
||||||
|
@ -7538,6 +7566,22 @@ game
|
||||||
oscillator
|
oscillator
|
||||||
frequency: 21440000
|
frequency: 21440000
|
||||||
|
|
||||||
|
game
|
||||||
|
sha256: fa8cacf5bbfc39ee6bbaa557adf89133d60d42f6cf9e1db30d5a36a469f74d15
|
||||||
|
label: Donkey Kong Country
|
||||||
|
name: Donkey Kong Country
|
||||||
|
region: SNS-8X-USA
|
||||||
|
revision: SNS-8X-0
|
||||||
|
board: SHVC-1J1M-11
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x400000
|
||||||
|
content: Program
|
||||||
|
memory
|
||||||
|
type: RAM
|
||||||
|
size: 0x800
|
||||||
|
content: Save
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: df2644d435330192a13768cc1f79c5aa3084a64217a5250c6dd4ffdbe2175be4
|
sha256: df2644d435330192a13768cc1f79c5aa3084a64217a5250c6dd4ffdbe2175be4
|
||||||
label: Donkey Kong Country
|
label: Donkey Kong Country
|
||||||
|
@ -10270,6 +10314,18 @@ game
|
||||||
size: 0x180000
|
size: 0x180000
|
||||||
content: Program
|
content: Program
|
||||||
|
|
||||||
|
game
|
||||||
|
sha256: b8f70a6e7fb93819f79693578887e2c11e196bdf1ac6ddc7cb924b1ad0be2d32
|
||||||
|
label: Mega Man X
|
||||||
|
name: Mega Man X
|
||||||
|
region: SNS-RX-USA
|
||||||
|
revision: SNS-RX-1
|
||||||
|
board: MAXI-1A0N-30
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x180000
|
||||||
|
content: Program
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: da484f2a636b8d692840f40e5468e992c5f2af26d365c69fbc12ef2923588d23
|
sha256: da484f2a636b8d692840f40e5468e992c5f2af26d365c69fbc12ef2923588d23
|
||||||
label: Mega Man X2
|
label: Mega Man X2
|
||||||
|
@ -12980,6 +13036,23 @@ game
|
||||||
content: Save
|
content: Save
|
||||||
volatile
|
volatile
|
||||||
|
|
||||||
|
game
|
||||||
|
sha256: 82e39dfbb3e4fe5c28044e80878392070c618b298dd5a267e5ea53c8f72cc548
|
||||||
|
label: Star Fox
|
||||||
|
name: Star Fox
|
||||||
|
region: SNS-FO-USA
|
||||||
|
revision: SNS-FO-2
|
||||||
|
board: SHVC-1C0N
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x100000
|
||||||
|
content: Program
|
||||||
|
memory
|
||||||
|
type: RAM
|
||||||
|
size: 0x8000
|
||||||
|
content: Save
|
||||||
|
volatile
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: 2c0bac12a7866fad1cb306da768a201c12f2520332df1ef51cba1576db21ff06
|
sha256: 2c0bac12a7866fad1cb306da768a201c12f2520332df1ef51cba1576db21ff06
|
||||||
label: Star Fox: Super Weekend
|
label: Star Fox: Super Weekend
|
||||||
|
@ -13690,6 +13763,46 @@ game
|
||||||
oscillator
|
oscillator
|
||||||
frequency: 7600000
|
frequency: 7600000
|
||||||
|
|
||||||
|
game
|
||||||
|
sha256: 76d293e5a772eb2f326e173eac62ca323873b1f329f9b935a97ba86974e1fcd5
|
||||||
|
label: Super Mario Kart
|
||||||
|
name: Super Mario Kart
|
||||||
|
region: SNS-MK-USA
|
||||||
|
revision: SNS-MK-0
|
||||||
|
board: SHVC-1K1X-10
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x80000
|
||||||
|
content: Program
|
||||||
|
memory
|
||||||
|
type: RAM
|
||||||
|
size: 0x800
|
||||||
|
content: Save
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x1800
|
||||||
|
content: Program
|
||||||
|
manufacturer: NEC
|
||||||
|
architecture: uPD7725
|
||||||
|
identifier: DSP1B
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x800
|
||||||
|
content: Data
|
||||||
|
manufacturer: NEC
|
||||||
|
architecture: uPD7725
|
||||||
|
identifier: DSP1B
|
||||||
|
memory
|
||||||
|
type: RAM
|
||||||
|
size: 0x200
|
||||||
|
content: Data
|
||||||
|
manufacturer: NEC
|
||||||
|
architecture: uPD7725
|
||||||
|
identifier: DSP1B
|
||||||
|
volatile
|
||||||
|
oscillator
|
||||||
|
frequency: 7600000
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: 740646f3535bfb365ca44e70d46ab433467b142bd84010393070bd0b141af853
|
sha256: 740646f3535bfb365ca44e70d46ab433467b142bd84010393070bd0b141af853
|
||||||
label: Super Mario RPG: Legend of the Seven Stars
|
label: Super Mario RPG: Legend of the Seven Stars
|
||||||
|
@ -13727,6 +13840,24 @@ game
|
||||||
size: 0x800
|
size: 0x800
|
||||||
content: Save
|
content: Save
|
||||||
|
|
||||||
|
game
|
||||||
|
sha256: 9b4957466798bbdb5b43a450bbb60b2591ae81d95b891430f62d53ca62e8bc7b
|
||||||
|
label: Super Mario World 2: Yoshi's Island
|
||||||
|
name: Super Mario World 2 - Yoshi's Island
|
||||||
|
region: SNS-YI-USA
|
||||||
|
revision: SNS-YI-0
|
||||||
|
board: SHVC-1CB5B-01
|
||||||
|
memory
|
||||||
|
type: ROM
|
||||||
|
size: 0x200000
|
||||||
|
content: Program
|
||||||
|
memory
|
||||||
|
type: RAM
|
||||||
|
size: 0x8000
|
||||||
|
content: Save
|
||||||
|
oscillator
|
||||||
|
frequency: 21440000
|
||||||
|
|
||||||
game
|
game
|
||||||
sha256: bd763c1a56365c244be92e6cffefd318780a2a19eda7d5baf1c6d5bd6c1b3e06
|
sha256: bd763c1a56365c244be92e6cffefd318780a2a19eda7d5baf1c6d5bd6c1b3e06
|
||||||
label: Super Mario World 2: Yoshi's Island
|
label: Super Mario World 2: Yoshi's Island
|
|
@ -42,13 +42,13 @@ else ifneq ($(filter $(platform),linux bsd),)
|
||||||
mkdir -p $(prefix)/share/applications/
|
mkdir -p $(prefix)/share/applications/
|
||||||
mkdir -p $(prefix)/share/icons/
|
mkdir -p $(prefix)/share/icons/
|
||||||
mkdir -p $(prefix)/share/$(name)/
|
mkdir -p $(prefix)/share/$(name)/
|
||||||
mkdir -p $(prefix)/share/$(name)/database/
|
mkdir -p $(prefix)/share/$(name)/Database/
|
||||||
mkdir -p $(prefix)/share/$(name)/firmware/
|
mkdir -p $(prefix)/share/$(name)/Firmware/
|
||||||
cp out/$(name) $(prefix)/bin/$(name)
|
cp out/$(name) $(prefix)/bin/$(name)
|
||||||
cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
||||||
cp data/$(name).png $(prefix)/share/icons/$(name).png
|
cp data/$(name).png $(prefix)/share/icons/$(name).png
|
||||||
cp -R database/* $(prefix)/share/$(name)/database/
|
cp -R Database/* $(prefix)/share/$(name)/Database/
|
||||||
cp -R firmware/* $(prefix)/share/$(name)/firmware/
|
cp -R Firmware/* $(prefix)/share/$(name)/Firmware/
|
||||||
endif
|
endif
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
Icarus::Icarus() {
|
Icarus::Icarus() {
|
||||||
Database::Famicom = BML::unserialize(string::read(locate("database/Famicom.bml")));
|
Database::Famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
|
||||||
Database::SuperFamicom = BML::unserialize(string::read(locate("database/Super Famicom.bml")));
|
Database::SuperFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.bml")));
|
||||||
Database::MasterSystem = BML::unserialize(string::read(locate("database/Master System.bml")));
|
Database::MasterSystem = BML::unserialize(string::read(locate("Database/Master System.bml")));
|
||||||
Database::MegaDrive = BML::unserialize(string::read(locate("database/Mega Drive.bml")));
|
Database::MegaDrive = BML::unserialize(string::read(locate("Database/Mega Drive.bml")));
|
||||||
Database::PCEngine = BML::unserialize(string::read(locate("database/PC Engine.bml")));
|
Database::PCEngine = BML::unserialize(string::read(locate("Database/PC Engine.bml")));
|
||||||
Database::SuperGrafx = BML::unserialize(string::read(locate("database/SuperGrafx.bml")));
|
Database::SuperGrafx = BML::unserialize(string::read(locate("Database/SuperGrafx.bml")));
|
||||||
Database::GameBoy = BML::unserialize(string::read(locate("database/Game Boy.bml")));
|
Database::GameBoy = BML::unserialize(string::read(locate("Database/Game Boy.bml")));
|
||||||
Database::GameBoyColor = BML::unserialize(string::read(locate("database/Game Boy Color.bml")));
|
Database::GameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.bml")));
|
||||||
Database::GameBoyAdvance = BML::unserialize(string::read(locate("database/Game Boy Advance.bml")));
|
Database::GameBoyAdvance = BML::unserialize(string::read(locate("Database/Game Boy Advance.bml")));
|
||||||
Database::GameGear = BML::unserialize(string::read(locate("database/Game Gear.bml")));
|
Database::GameGear = BML::unserialize(string::read(locate("Database/Game Gear.bml")));
|
||||||
Database::WonderSwan = BML::unserialize(string::read(locate("database/WonderSwan.bml")));
|
Database::WonderSwan = BML::unserialize(string::read(locate("Database/WonderSwan.bml")));
|
||||||
Database::WonderSwanColor = BML::unserialize(string::read(locate("database/WonderSwan Color.bml")));
|
Database::WonderSwanColor = BML::unserialize(string::read(locate("Database/WonderSwan Color.bml")));
|
||||||
Database::PocketChallengeV2 = BML::unserialize(string::read(locate("database/Pocket Challenge V2.bml")));
|
Database::PocketChallengeV2 = BML::unserialize(string::read(locate("Database/Pocket Challenge V2.bml")));
|
||||||
Database::BSMemory = BML::unserialize(string::read(locate("database/BS Memory.bml")));
|
Database::BSMemory = BML::unserialize(string::read(locate("Database/BS Memory.bml")));
|
||||||
Database::SufamiTurbo = BML::unserialize(string::read(locate("database/Sufami Turbo.bml")));
|
Database::SufamiTurbo = BML::unserialize(string::read(locate("Database/Sufami Turbo.bml")));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Icarus::error() const -> string {
|
auto Icarus::error() const -> string {
|
||||||
|
|
|
@ -12,7 +12,7 @@ struct Icarus {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual auto write(const string& filename, const uint8_t* data, uint size) -> bool {
|
virtual auto write(const string& filename, const uint8_t* data, uint size) -> bool {
|
||||||
return file::write(filename, data, size);
|
return file::write(filename, {data, size});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto write(const string& filename, const vector<uint8_t>& buffer) -> bool {
|
auto write(const string& filename, const vector<uint8_t>& buffer) -> bool {
|
||||||
|
|
|
@ -46,7 +46,7 @@ auto Icarus::superFamicomImport(vector<uint8_t>& buffer, string location) -> str
|
||||||
auto size = rom["size"].natural();
|
auto size = rom["size"].natural();
|
||||||
if(size > buffer.size() - offset) {
|
if(size > buffer.size() - offset) {
|
||||||
auto firmware = string{rom["identifier"].text(), ".", rom["content"].text(), ".rom"}.trimLeft(".", 1L).downcase();
|
auto firmware = string{rom["identifier"].text(), ".", rom["content"].text(), ".rom"}.trimLeft(".", 1L).downcase();
|
||||||
auto location = locate({"firmware/", firmware});
|
auto location = locate({"Firmware/", firmware});
|
||||||
if(location && file::size(location) == size) {
|
if(location && file::size(location) == size) {
|
||||||
write({target, name}, file::read(location));
|
write({target, name}, file::read(location));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -62,23 +62,23 @@ auto hiro::initialize() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <nall/main.hpp>
|
#include <nall/main.hpp>
|
||||||
auto nall::main(vector<string> arguments) -> void {
|
auto nall::main(Arguments arguments) -> void {
|
||||||
if(arguments.size() == 2 && arguments[1] == "--name") {
|
if(arguments.size() == 1 && arguments[0] == "--name") {
|
||||||
return print("icarus");
|
return print("icarus");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(arguments.size() == 3 && arguments[1] == "--manifest" && directory::exists(arguments[2])) {
|
if(arguments.size() == 2 && arguments[0] == "--manifest" && directory::exists(arguments[1])) {
|
||||||
return print(icarus.manifest(arguments[2]));
|
return print(icarus.manifest(arguments[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(arguments.size() == 3 && arguments[1] == "--import" && file::exists(arguments[2])) {
|
if(arguments.size() == 2 && arguments[0] == "--import" && file::exists(arguments[1])) {
|
||||||
if(string target = icarus.import(arguments[2])) {
|
if(string target = icarus.import(arguments[1])) {
|
||||||
return print(target, "\n");
|
return print(target, "\n");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(arguments.size() == 2 && arguments[1] == "--import") {
|
if(arguments.size() == 1 && arguments[0] == "--import") {
|
||||||
if(string source = BrowserDialog()
|
if(string source = BrowserDialog()
|
||||||
.setTitle("Load ROM File")
|
.setTitle("Load ROM File")
|
||||||
.setPath(settings["icarus/Path"].text())
|
.setPath(settings["icarus/Path"].text())
|
||||||
|
|
|
@ -39,10 +39,13 @@ ifeq ($(platform),)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
compiler.c = $(compiler) -x c -std=c11
|
||||||
|
compiler.cpp = $(compiler) -x c++ -std=c++14
|
||||||
|
compiler.objc = $(compiler) -x objective-c -std=c11
|
||||||
|
compiler.objcpp = $(compiler) -x objective-c++ -std=c++14
|
||||||
|
|
||||||
flags.c = -x c -std=c11
|
flags.c = -x c -std=c11
|
||||||
flags.h = -x c-header -std=c11
|
|
||||||
flags.cpp = -x c++ -std=c++14
|
flags.cpp = -x c++ -std=c++14
|
||||||
flags.hpp = -x c++-header -std=c++14
|
|
||||||
flags.objc = -x objective-c -std=c11
|
flags.objc = -x objective-c -std=c11
|
||||||
flags.objcpp = -x objective-c++ -std=c++14
|
flags.objcpp = -x objective-c++ -std=c++14
|
||||||
flags.deps = -MMD -MP -MF $(@:.o=.d)
|
flags.deps = -MMD -MP -MF $(@:.o=.d)
|
||||||
|
@ -51,8 +54,8 @@ flags.deps = -MMD -MP -MF $(@:.o=.d)
|
||||||
ifeq ($(compiler),)
|
ifeq ($(compiler),)
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
compiler := g++
|
compiler := g++
|
||||||
flags.cpp := -x c++ -std=gnu++14
|
compiler.cpp = $(compiler) -x c++ -std=gnu++14
|
||||||
flags.hpp := -x c++-header -std=gnu++14
|
flags.cpp = -x c++ -std=gnu++14
|
||||||
else ifeq ($(platform),macos)
|
else ifeq ($(platform),macos)
|
||||||
compiler := clang++
|
compiler := clang++
|
||||||
else ifeq ($(platform),linux)
|
else ifeq ($(platform),linux)
|
||||||
|
@ -158,14 +161,10 @@ nall.verbose:
|
||||||
compile = \
|
compile = \
|
||||||
$(strip \
|
$(strip \
|
||||||
$(if $(filter %.c,$<), \
|
$(if $(filter %.c,$<), \
|
||||||
$(compiler) $(flags.c) $(flags.deps) $(flags) $1 -c $< -o $@ \
|
$(compiler.c) $(flags.deps) $(flags) $1 -c $< -o $@ \
|
||||||
,$(if $(filter %.cpp,$<), \
|
,$(if $(filter %.cpp,$<), \
|
||||||
$(compiler) $(flags.cpp) $(flags.deps) $(flags) $1 -c $< -o $@ \
|
$(compiler.cpp) $(flags.deps) $(flags) $1 -c $< -o $@ \
|
||||||
,$(if $(filter %.h,$<), \
|
)) \
|
||||||
$(compiler) $(flags.h) $(flags) $1 -c $< -o $@ \
|
|
||||||
,$(if $(filter %.hpp,$<), \
|
|
||||||
$(compiler) $(flags.hpp) $(flags) $1 -c $< -o $@ \
|
|
||||||
)))) \
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# function rwildcard(directory, pattern)
|
# function rwildcard(directory, pattern)
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/location.hpp>
|
||||||
|
#include <nall/path.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
Arguments(int argc, char** argv);
|
||||||
|
Arguments(vector<string> arguments);
|
||||||
|
|
||||||
|
explicit operator bool() const { return (bool)arguments; }
|
||||||
|
auto size() const -> uint { return arguments.size(); }
|
||||||
|
|
||||||
|
auto operator[](uint index) -> string& { return arguments[index]; }
|
||||||
|
auto operator[](uint index) const -> const string& { return arguments[index]; }
|
||||||
|
|
||||||
|
auto programPath() const -> string;
|
||||||
|
auto programName() const -> string;
|
||||||
|
auto programLocation() const -> string;
|
||||||
|
|
||||||
|
auto find(string_view name) const -> bool;
|
||||||
|
auto find(string_view name, bool& argument) const -> bool;
|
||||||
|
auto find(string_view name, string& argument) const -> bool;
|
||||||
|
|
||||||
|
auto begin() const { return arguments.begin(); }
|
||||||
|
auto end() const { return arguments.end(); }
|
||||||
|
|
||||||
|
auto rbegin() const { return arguments.rbegin(); }
|
||||||
|
auto rend() const { return arguments.rend(); }
|
||||||
|
|
||||||
|
auto take() -> string;
|
||||||
|
auto take(string_view name) -> bool;
|
||||||
|
auto take(string_view name, bool& argument) -> bool;
|
||||||
|
auto take(string_view name, string& argument) -> bool;
|
||||||
|
|
||||||
|
auto begin() { return arguments.begin(); }
|
||||||
|
auto end() { return arguments.end(); }
|
||||||
|
|
||||||
|
auto rbegin() { return arguments.rbegin(); }
|
||||||
|
auto rend() { return arguments.rend(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto construct() -> void;
|
||||||
|
|
||||||
|
string programArgument;
|
||||||
|
vector<string> arguments;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto Arguments::construct() -> void {
|
||||||
|
if(!arguments) return;
|
||||||
|
|
||||||
|
//extract and pre-process program argument
|
||||||
|
programArgument = arguments.takeFirst();
|
||||||
|
programArgument = {Path::real(programArgument), Location::file(programArgument)};
|
||||||
|
|
||||||
|
//normalize path and file arguments
|
||||||
|
for(auto& argument : arguments) {
|
||||||
|
if(directory::exists(argument)) argument.transform("\\", "/").trimRight("/").append("/");
|
||||||
|
else if(file::exists(argument)) argument.transform("\\", "/").trimRight("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Arguments::Arguments(int argc, char** argv) {
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
utf8_arguments(argc, argv);
|
||||||
|
#endif
|
||||||
|
for(uint index : range(argc)) arguments.append(argv[index]);
|
||||||
|
construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Arguments::Arguments(vector<string> arguments) {
|
||||||
|
this->arguments = arguments;
|
||||||
|
construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::programPath() const -> string {
|
||||||
|
return Location::path(programArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::programName() const -> string {
|
||||||
|
return Location::file(programArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::programLocation() const -> string {
|
||||||
|
return programArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::find(string_view name) const -> bool {
|
||||||
|
for(uint index : range(arguments.size())) {
|
||||||
|
if(arguments[index].match(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::find(string_view name, bool& argument) const -> bool {
|
||||||
|
for(uint index : range(arguments.size())) {
|
||||||
|
if(arguments[index].match(name) && arguments.size() >= index
|
||||||
|
&& (arguments[index + 1] == "true" || arguments[index + 1] == "false")) {
|
||||||
|
argument = arguments[index + 1] == "true";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::find(string_view name, string& argument) const -> bool {
|
||||||
|
for(uint index : range(arguments.size())) {
|
||||||
|
if(arguments[index].match(name) && arguments.size() >= index) {
|
||||||
|
argument = arguments[index + 1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
inline auto Arguments::take() -> string {
|
||||||
|
if(!arguments) return {};
|
||||||
|
return arguments.takeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::take(string_view name) -> bool {
|
||||||
|
for(uint index : range(arguments.size())) {
|
||||||
|
if(arguments[index].match(name)) {
|
||||||
|
arguments.remove(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::take(string_view name, bool& argument) -> bool {
|
||||||
|
for(uint index : range(arguments.size())) {
|
||||||
|
if(arguments[index].match(name) && arguments.size() >= index
|
||||||
|
&& (arguments[index + 1] == "true" || arguments[index + 1] == "false")) {
|
||||||
|
arguments.remove(index);
|
||||||
|
argument = arguments.take(index) == "true";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Arguments::take(string_view name, string& argument) -> bool {
|
||||||
|
for(uint index : range(arguments.size())) {
|
||||||
|
if(arguments[index].match(name) && arguments.size() >= index) {
|
||||||
|
arguments.remove(index);
|
||||||
|
argument = arguments.take(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,7 +10,18 @@
|
||||||
|
|
||||||
#include <nall/arithmetic/unsigned.hpp>
|
#include <nall/arithmetic/unsigned.hpp>
|
||||||
|
|
||||||
#if !defined(__SIZEOF_INT128__)
|
namespace nall {
|
||||||
|
template<uint Bits> struct ArithmeticNatural;
|
||||||
|
template<> struct ArithmeticNatural< 8> { using type = uint8_t; };
|
||||||
|
template<> struct ArithmeticNatural< 16> { using type = uint16_t; };
|
||||||
|
template<> struct ArithmeticNatural< 32> { using type = uint32_t; };
|
||||||
|
template<> struct ArithmeticNatural< 64> { using type = uint64_t; };
|
||||||
|
#if INTMAX_BITS >= 128
|
||||||
|
template<> struct ArithmeticNatural<128> { using type = uint128_t; };
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if INTMAX_BITS < 128
|
||||||
#define PairBits 128
|
#define PairBits 128
|
||||||
#define TypeBits 64
|
#define TypeBits 64
|
||||||
#define HalfBits 32
|
#define HalfBits 32
|
||||||
|
|
|
@ -2,20 +2,27 @@
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
struct BarrettReduction {
|
template<uint Bits> struct BarrettReduction {
|
||||||
BarrettReduction(uint256_t modulo) : modulo(modulo), factor((1_u1024 << 512) / modulo) {}
|
using type = typename ArithmeticNatural<1 * Bits>::type;
|
||||||
|
using pair = typename ArithmeticNatural<2 * Bits>::type;
|
||||||
|
|
||||||
//return = value % modulo
|
explicit BarrettReduction(type modulo) : modulo(modulo), factor(pair(1) + -pair(modulo) / modulo) {}
|
||||||
inline auto operator()(uint512_t value) const -> uint256_t {
|
|
||||||
uint512_t hi, lo;
|
//return => value % modulo
|
||||||
nall::mul(value, factor, hi, lo);
|
inline auto operator()(pair value) const -> type {
|
||||||
uint512_t remainder = value - hi * modulo;
|
pair hi, lo;
|
||||||
|
mul(value, factor, hi, lo);
|
||||||
|
pair remainder = value - hi * modulo;
|
||||||
return remainder < modulo ? remainder : remainder - modulo;
|
return remainder < modulo ? remainder : remainder - modulo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint512_t modulo;
|
const pair modulo;
|
||||||
const uint512_t factor;
|
const pair factor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T, uint Bits> auto operator%(T value, const BarrettReduction<Bits>& modulo) {
|
||||||
|
return modulo(value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ struct Pair {
|
||||||
explicit operator bool() const { return hi | lo; }
|
explicit operator bool() const { return hi | lo; }
|
||||||
template<typename T> operator T() const { T value; _get(*this, value); return value; }
|
template<typename T> operator T() const { T value; _get(*this, value); return value; }
|
||||||
|
|
||||||
|
auto operator+() const -> Pair { return *this; }
|
||||||
|
auto operator-() const -> Pair { return Pair(0) - *this; }
|
||||||
auto operator~() const -> Pair { return {~hi, ~lo}; }
|
auto operator~() const -> Pair { return {~hi, ~lo}; }
|
||||||
auto operator!() const -> bool { return !(hi || lo); }
|
auto operator!() const -> bool { return !(hi || lo); }
|
||||||
|
|
||||||
|
@ -75,13 +77,6 @@ struct Pair {
|
||||||
template<typename T> auto operator> (const T& rhs) const -> bool { return Cast(*this) > Cast(rhs); }
|
template<typename T> auto operator> (const T& rhs) const -> bool { return Cast(*this) > Cast(rhs); }
|
||||||
template<typename T> auto operator< (const T& rhs) const -> bool { return Cast(*this) < Cast(rhs); }
|
template<typename T> auto operator< (const T& rhs) const -> bool { return Cast(*this) < Cast(rhs); }
|
||||||
|
|
||||||
explicit Pair(const vector<uint8_t>& value) : hi(0), lo(0) {
|
|
||||||
for(auto n : reverse(value)) {
|
|
||||||
operator<<=(8);
|
|
||||||
operator|=(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type lo;
|
Type lo;
|
||||||
Type hi;
|
Type hi;
|
||||||
|
@ -98,6 +93,10 @@ private:
|
||||||
template<typename T> friend auto shr(const Pair&, const T&) -> Pair;
|
template<typename T> friend auto shr(const Pair&, const T&) -> Pair;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<> struct ArithmeticNatural<PairBits> {
|
||||||
|
using type = Pair;
|
||||||
|
};
|
||||||
|
|
||||||
#define ConcatenateUDL(Size) _u##Size
|
#define ConcatenateUDL(Size) _u##Size
|
||||||
#define DeclareUDL(Size) ConcatenateUDL(Size)
|
#define DeclareUDL(Size) ConcatenateUDL(Size)
|
||||||
|
|
||||||
|
@ -333,29 +332,6 @@ template<> struct stringify<Pair> {
|
||||||
uint _size;
|
uint _size;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline auto to_vector(Pair value) -> vector<uint8_t> {
|
|
||||||
vector<uint8_t> result;
|
|
||||||
result.resize(PairBits / 8);
|
|
||||||
for(auto& byte : result) {
|
|
||||||
byte = value;
|
|
||||||
value >>= 8;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
inline auto hex(const Pair& value, long precision = 0, char padchar = '0') -> string {
|
|
||||||
string text;
|
|
||||||
if(!upper(value)) {
|
|
||||||
text.append(hex(lower(value)));
|
|
||||||
} else {
|
|
||||||
text.append(hex(upper(value)));
|
|
||||||
text.append(hex(lower(value), TypeBits / 4, '0'));
|
|
||||||
}
|
|
||||||
return pad(text, precision, padchar);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef ConcatenateType
|
#undef ConcatenateType
|
||||||
|
|
|
@ -2,25 +2,35 @@
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
template<typename T, enable_if_t<is_unsigned<T>::value>> alwaysinline auto upper(T value) -> T {
|
template<typename T, enable_if_t<is_unsigned<T>::value>>
|
||||||
|
inline auto upper(T value) -> T {
|
||||||
return value >> sizeof(T) * 4;
|
return value >> sizeof(T) * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, enable_if_t<is_unsigned<T>::value>> alwaysinline auto lower(T value) -> T {
|
template<typename T, enable_if_t<is_unsigned<T>::value>>
|
||||||
static const T Mask = T(0) - 1 >> sizeof(T) * 4;
|
inline auto lower(T value) -> T {
|
||||||
|
static const T Mask = ~T(0) >> sizeof(T) * 4;
|
||||||
return value & Mask;
|
return value & Mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
alwaysinline auto square(uintmax value) -> uintmax {
|
template<typename T, typename U, enable_if_t<is_unsigned<T>::value>, enable_if_t<is_unsigned<U>::value>>
|
||||||
|
inline auto mul(T lhs, U rhs) -> uintmax {
|
||||||
|
return lhs * rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, enable_if_t<is_unsigned<T>::value>>
|
||||||
|
inline auto square(T value) -> uintmax {
|
||||||
return value * value;
|
return value * value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename U> alwaysinline auto rol(const T& lhs, const U& rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
|
template<typename T, typename U>
|
||||||
return lhs << rhs | lhs >> (sizeof(T) * 8 - rhs);
|
inline auto rol(T lhs, U rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
|
||||||
|
return lhs << rhs | lhs >> sizeof(T) * 8 - rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename U> alwaysinline auto ror(const T& lhs, const U& rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
|
template<typename T, typename U>
|
||||||
return lhs >> rhs | lhs << (sizeof(T) * 8 - rhs);
|
inline auto ror(T lhs, U rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
|
||||||
|
return lhs >> rhs | lhs << sizeof(T) * 8 - rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INTMAX_BITS >= 128
|
#if INTMAX_BITS >= 128
|
||||||
|
|
|
@ -41,6 +41,14 @@ template<typename T> struct array_span : array_view<T> {
|
||||||
super::_size--;
|
super::_size--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto span(uint offset, uint length) const -> type {
|
||||||
|
#ifdef DEBUG
|
||||||
|
struct out_of_bounds {};
|
||||||
|
if(offset + length >= super::_size) throw out_of_bounds{};
|
||||||
|
#endif
|
||||||
|
return {super::_data + offset, length};
|
||||||
|
}
|
||||||
|
|
||||||
//array_span<uint8_t> specializations
|
//array_span<uint8_t> specializations
|
||||||
template<typename U> auto writel(U value, uint size) -> void;
|
template<typename U> auto writel(U value, uint size) -> void;
|
||||||
template<typename U> auto writem(U value, uint size) -> void;
|
template<typename U> auto writem(U value, uint size) -> void;
|
||||||
|
|
|
@ -29,7 +29,7 @@ template<typename T> struct array_view {
|
||||||
inline operator const T*() const {
|
inline operator const T*() const {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
struct out_of_bounds {};
|
struct out_of_bounds {};
|
||||||
if(_size <= 0) throw out_of_bounds{};
|
if(_size < 0) throw out_of_bounds{};
|
||||||
#endif
|
#endif
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
@ -72,17 +72,29 @@ template<typename T> struct array_view {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto view(uint offset, uint length) const -> type {
|
||||||
|
#ifdef DEBUG
|
||||||
|
struct out_of_bounds {};
|
||||||
|
if(offset + length >= _size) throw out_of_bounds{};
|
||||||
|
#endif
|
||||||
|
return {_data + offset, length};
|
||||||
|
}
|
||||||
|
|
||||||
//array_view<uint8_t> specializations
|
//array_view<uint8_t> specializations
|
||||||
template<typename U> auto readl(U& value, uint size) -> U;
|
template<typename U> auto readl(U& value, uint size) -> U;
|
||||||
template<typename U> auto readm(U& value, uint size) -> U;
|
template<typename U> auto readm(U& value, uint size) -> U;
|
||||||
template<typename U> auto readvn(U& value, uint size) -> U;
|
template<typename U> auto readvn(U& value, uint size) -> U;
|
||||||
template<typename U> auto readvi(U& value, uint size) -> U;
|
template<typename U> auto readvi(U& value, uint size) -> U;
|
||||||
|
|
||||||
|
template<typename U> auto readl(U& value, uint offset, uint size) -> U { return view(offset, size).readl(value, size); }
|
||||||
|
|
||||||
template<typename U = uint64_t> auto readl(uint size) -> U { U value; return readl(value, size); }
|
template<typename U = uint64_t> auto readl(uint size) -> U { U value; return readl(value, size); }
|
||||||
template<typename U = uint64_t> auto readm(uint size) -> U { U value; return readm(value, size); }
|
template<typename U = uint64_t> auto readm(uint size) -> U { U value; return readm(value, size); }
|
||||||
template<typename U = uint64_t> auto readvn(uint size) -> U { U value; return readvn(value, size); }
|
template<typename U = uint64_t> auto readvn(uint size) -> U { U value; return readvn(value, size); }
|
||||||
template<typename U = int64_t> auto readvi(uint size) -> U { U value; return readvi(value, size); }
|
template<typename U = int64_t> auto readvi(uint size) -> U { U value; return readvi(value, size); }
|
||||||
|
|
||||||
|
template<typename U = uint64_t> auto readl(uint offset, uint size) -> U { U value; return readl(value, offset, size); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const T* _data;
|
const T* _data;
|
||||||
int _size;
|
int _size;
|
||||||
|
@ -92,13 +104,13 @@ protected:
|
||||||
|
|
||||||
template<> template<typename U> inline auto array_view<uint8_t>::readl(U& value, uint size) -> U {
|
template<> template<typename U> inline auto array_view<uint8_t>::readl(U& value, uint size) -> U {
|
||||||
value = 0;
|
value = 0;
|
||||||
for(uint byte : range(size)) value |= read() << byte * 8;
|
for(uint byte : range(size)) value |= (U)read() << byte * 8;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> template<typename U> inline auto array_view<uint8_t>::readm(U& value, uint size) -> U {
|
template<> template<typename U> inline auto array_view<uint8_t>::readm(U& value, uint size) -> U {
|
||||||
value = 0;
|
value = 0;
|
||||||
for(uint byte : reverse(range(size))) value |= read() << byte * 8;
|
for(uint byte : reverse(range(size))) value |= (U)read() << byte * 8;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,272 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <nall/random.hpp>
|
|
||||||
#include <nall/cipher/chacha20.hpp>
|
|
||||||
#include <nall/elliptic-curve/ed25519.hpp>
|
|
||||||
#include <nall/encode/base.hpp>
|
|
||||||
#include <nall/decode/base.hpp>
|
|
||||||
#include <nall/encode/lzsa.hpp>
|
|
||||||
#include <nall/decode/lzsa.hpp>
|
|
||||||
|
|
||||||
namespace nall { namespace Beat {
|
|
||||||
|
|
||||||
struct Archive {
|
|
||||||
struct Encryption {
|
|
||||||
string type;
|
|
||||||
uint256_t key = 0;
|
|
||||||
uint192_t nonce = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Signature {
|
|
||||||
string type;
|
|
||||||
uint256_t privateKey = 0;
|
|
||||||
uint256_t publicKey = 0;
|
|
||||||
uint512_t signature = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Compression {
|
|
||||||
string type;
|
|
||||||
uint size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
//timestamps are human-readable strings in ISO 8601 format; save for T=>space
|
|
||||||
//times are stored in UTC, rather than local times
|
|
||||||
struct Timestamps {
|
|
||||||
string created;
|
|
||||||
string modified;
|
|
||||||
string accessed;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Permissions {
|
|
||||||
string name;
|
|
||||||
bool readable = false;
|
|
||||||
bool writable = false;
|
|
||||||
bool executable = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Node {
|
|
||||||
string name;
|
|
||||||
|
|
||||||
//paths and files
|
|
||||||
Timestamps timestamps;
|
|
||||||
struct {
|
|
||||||
Permissions owner;
|
|
||||||
Permissions group;
|
|
||||||
Permissions other;
|
|
||||||
} permissions;
|
|
||||||
|
|
||||||
//files only
|
|
||||||
uint offset = 0;
|
|
||||||
uint size = 0;
|
|
||||||
|
|
||||||
Compression compression;
|
|
||||||
|
|
||||||
string filename;
|
|
||||||
vector<uint8_t> filedata;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto append(const Node& node) -> bool;
|
|
||||||
auto encryptionManifest() -> string;
|
|
||||||
auto manifest() -> string;
|
|
||||||
auto create() -> vector<uint8_t>;
|
|
||||||
|
|
||||||
//internal functions
|
|
||||||
auto encode() -> vector<uint8_t>;
|
|
||||||
auto encode(Node& node, uint64_t offset) -> vector<uint8_t>;
|
|
||||||
|
|
||||||
Encryption encryption;
|
|
||||||
Signature signature;
|
|
||||||
Compression compression; //solid archiving
|
|
||||||
vector<Node> nodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto Archive::append(const Node& node) -> bool {
|
|
||||||
//prevent multiple nodes with the same name
|
|
||||||
if(nodes.find([&](auto& item) { return item.name == node.name; })) return false;
|
|
||||||
|
|
||||||
nodes.append(node);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Archive::encryptionManifest() -> string {
|
|
||||||
string manifest;
|
|
||||||
manifest.append("encryption\n");
|
|
||||||
manifest.append(" type: ", encryption.type, "\n");
|
|
||||||
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Archive::manifest() -> string {
|
|
||||||
string manifest;
|
|
||||||
manifest.append("archive\n");
|
|
||||||
|
|
||||||
for(auto& node : nodes) {
|
|
||||||
if(node.name.endsWith("/")) {
|
|
||||||
manifest.append(" path: ", string{node.name}.trimRight("/", 1L), "\n");
|
|
||||||
} else {
|
|
||||||
manifest.append(" file: ", node.name, "\n");
|
|
||||||
manifest.append(" offset: ", node.offset, "\n");
|
|
||||||
manifest.append(" size: ", node.size, "\n");
|
|
||||||
if(node.compression.type) {
|
|
||||||
manifest.append(" compression: ", node.compression.type, "\n");
|
|
||||||
manifest.append(" size: ", node.compression.size, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(node.timestamps.created || node.timestamps.modified || node.timestamps.accessed) {
|
|
||||||
manifest.append(" timestamp\n");
|
|
||||||
if(auto timestamp = node.timestamps.created ) manifest.append(" created: ", timestamp, "\n");
|
|
||||||
if(auto timestamp = node.timestamps.modified) manifest.append(" modified: ", timestamp, "\n");
|
|
||||||
if(auto timestamp = node.timestamps.accessed) manifest.append(" accessed: ", timestamp, "\n");
|
|
||||||
}
|
|
||||||
if(node.permissions.owner.name || node.permissions.group.name || node.permissions.other.name) {
|
|
||||||
manifest.append(" permission\n");
|
|
||||||
if(node.permissions.owner.name) {
|
|
||||||
manifest.append(" owner: ", node.permissions.owner.name, "\n");
|
|
||||||
if(node.permissions.owner.readable ) manifest.append(" readable\n");
|
|
||||||
if(node.permissions.owner.writable ) manifest.append(" writable\n");
|
|
||||||
if(node.permissions.owner.executable) manifest.append(" executable\n");
|
|
||||||
}
|
|
||||||
if(node.permissions.group.name) {
|
|
||||||
manifest.append(" group: ", node.permissions.group.name, "\n");
|
|
||||||
if(node.permissions.group.readable ) manifest.append(" readable\n");
|
|
||||||
if(node.permissions.group.writable ) manifest.append(" writable\n");
|
|
||||||
if(node.permissions.group.executable) manifest.append(" executable\n");
|
|
||||||
}
|
|
||||||
if(node.permissions.other.name) {
|
|
||||||
manifest.append(" other\n");
|
|
||||||
if(node.permissions.other.readable ) manifest.append(" readable\n");
|
|
||||||
if(node.permissions.other.writable ) manifest.append(" writable\n");
|
|
||||||
if(node.permissions.other.executable) manifest.append(" executable\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(compression.type) {
|
|
||||||
manifest.append(" compression: ", compression.type, "\n");
|
|
||||||
manifest.append(" size: ", compression.size, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(signature.type == "ed25519") {
|
|
||||||
manifest.append(" signature: ", signature.type, "\n");
|
|
||||||
manifest.append(" publicKey: ", Encode::Base<57>(signature.publicKey), "\n");
|
|
||||||
manifest.append(" signature: ", Encode::Base<57>(signature.signature), "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Archive::create() -> vector<uint8_t> {
|
|
||||||
vector<uint8_t> output;
|
|
||||||
|
|
||||||
output.append('B');
|
|
||||||
output.append('P');
|
|
||||||
output.append('A');
|
|
||||||
output.append('1');
|
|
||||||
|
|
||||||
nodes.sort([&](auto& lhs, auto& rhs) {
|
|
||||||
return string::compare(lhs.name, rhs.name) < 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto content = encode();
|
|
||||||
if(compression.type == "lzsa") {
|
|
||||||
content = Encode::LZSA(content);
|
|
||||||
compression.size = content.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(signature.type == "ed25519") {
|
|
||||||
EllipticCurve::Ed25519 ed25519;
|
|
||||||
signature.publicKey = ed25519.publicKey(signature.privateKey);
|
|
||||||
signature.signature = ed25519.sign(content, signature.privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(encryption.type == "xchacha20") {
|
|
||||||
//a randomly generated nonce is preferred
|
|
||||||
if(!encryption.nonce) {
|
|
||||||
CSPRNG csprng;
|
|
||||||
encryption.nonce = csprng.random<uint192_t>();
|
|
||||||
}
|
|
||||||
Cipher::XChaCha20 xchacha20{encryption.key, encryption.nonce};
|
|
||||||
content = xchacha20.encrypt(content);
|
|
||||||
|
|
||||||
string manifest;
|
|
||||||
manifest.append("encryption\n");
|
|
||||||
manifest.append(" type: ", encryption.type, "\n");
|
|
||||||
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
|
|
||||||
|
|
||||||
output.append(content);
|
|
||||||
for(uint8_t byte : manifest) output.append(byte);
|
|
||||||
output.appendl(manifest.size(), 8);
|
|
||||||
} else {
|
|
||||||
encryption = {};
|
|
||||||
output.append(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sha256 = Hash::SHA256(output).value();
|
|
||||||
output.appendl(sha256, 32);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
auto Archive::encode() -> vector<uint8_t> {
|
|
||||||
vector<uint8_t> output;
|
|
||||||
|
|
||||||
for(auto& node : nodes) {
|
|
||||||
if(node.filename) {
|
|
||||||
node.timestamps.created = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::create));
|
|
||||||
node.timestamps.accessed = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::access));
|
|
||||||
node.timestamps.modified = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::modify));
|
|
||||||
|
|
||||||
uint mode = inode::mode(node.filename);
|
|
||||||
node.permissions.owner.name = inode::user(node.filename);
|
|
||||||
node.permissions.owner.executable = mode & 0100;
|
|
||||||
node.permissions.owner.writable = mode & 0200;
|
|
||||||
node.permissions.owner.readable = mode & 0400;
|
|
||||||
node.permissions.group.name = inode::group(node.filename);
|
|
||||||
node.permissions.group.executable = mode & 0010;
|
|
||||||
node.permissions.group.writable = mode & 0020;
|
|
||||||
node.permissions.group.readable = mode & 0040;
|
|
||||||
node.permissions.other.name = " ";
|
|
||||||
node.permissions.other.executable = mode & 0001;
|
|
||||||
node.permissions.other.writable = mode & 0002;
|
|
||||||
node.permissions.other.readable = mode & 0004;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(node.name.endsWith("/")) continue;
|
|
||||||
|
|
||||||
auto buffer = encode(node, output.size());
|
|
||||||
output.append(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto manifest = this->manifest();
|
|
||||||
for(auto byte : manifest) output.append(byte);
|
|
||||||
for(auto byte : range(8)) output.append((uint64_t)manifest.size() >> byte * 8);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Archive::encode(Node& node, uint64_t offset) -> vector<uint8_t> {
|
|
||||||
node.offset = offset;
|
|
||||||
|
|
||||||
vector<uint8_t> output;
|
|
||||||
|
|
||||||
if(node.filename) {
|
|
||||||
output = file::read(node.filename);
|
|
||||||
} else {
|
|
||||||
output = node.filedata;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.size = output.size();
|
|
||||||
|
|
||||||
if(node.compression.type == "lzsa") {
|
|
||||||
output = Encode::LZSA(output);
|
|
||||||
node.compression.size = output.size();
|
|
||||||
} else {
|
|
||||||
node.compression = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/beat/archive/node.hpp>
|
||||||
|
|
||||||
|
namespace nall { namespace Beat { namespace Archive {
|
||||||
|
|
||||||
|
struct Container {
|
||||||
|
Container(array_view<uint8_t> = {});
|
||||||
|
~Container();
|
||||||
|
|
||||||
|
auto isCompressed() const -> bool { return (bool)compression.type; }
|
||||||
|
auto isSigned() const -> bool { return (bool)signature.type; }
|
||||||
|
auto isEncrypted() const -> bool { return (bool)encryption.type; }
|
||||||
|
|
||||||
|
auto compressLZSA() -> void;
|
||||||
|
auto signEd25519(uint256_t privateKey) -> void;
|
||||||
|
auto encryptXChaCha20(uint256_t privateKey, uint192_t nonce = 0) -> void;
|
||||||
|
|
||||||
|
auto validate() -> bool;
|
||||||
|
auto decryptXChaCha20(uint256_t privateKey) -> bool;
|
||||||
|
auto verifyEd25519(uint256_t publicKey) -> bool;
|
||||||
|
auto decompressLZSA() -> bool;
|
||||||
|
|
||||||
|
auto append(string name, string location) -> shared_pointer<Node>;
|
||||||
|
auto appendPath(string name) -> shared_pointer<Node>;
|
||||||
|
auto appendFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node>;
|
||||||
|
auto remove(string name) -> bool;
|
||||||
|
auto find(string name) -> shared_pointer<Node>;
|
||||||
|
auto sort() -> void;
|
||||||
|
|
||||||
|
auto begin() { return nodes.begin(); }
|
||||||
|
auto end() { return nodes.end(); }
|
||||||
|
|
||||||
|
auto begin() const { return nodes.begin(); }
|
||||||
|
auto end() const { return nodes.end(); }
|
||||||
|
|
||||||
|
auto rbegin() { return nodes.rbegin(); }
|
||||||
|
auto rend() { return nodes.rend(); }
|
||||||
|
|
||||||
|
auto rbegin() const { return nodes.rbegin(); }
|
||||||
|
auto rend() const { return nodes.rend(); }
|
||||||
|
|
||||||
|
vector<shared_pointer<Node>> nodes;
|
||||||
|
vector<uint8_t> memory;
|
||||||
|
string metadata;
|
||||||
|
|
||||||
|
struct Compression {
|
||||||
|
string type;
|
||||||
|
} compression;
|
||||||
|
|
||||||
|
struct Signature {
|
||||||
|
string type;
|
||||||
|
uint256_t privateKey = 0;
|
||||||
|
uint256_t publicKey = 0;
|
||||||
|
uint512_t value = 0;
|
||||||
|
} signature;
|
||||||
|
|
||||||
|
struct Encryption {
|
||||||
|
string type;
|
||||||
|
uint256_t privateKey = 0;
|
||||||
|
uint192_t nonce = 0;
|
||||||
|
} encryption;
|
||||||
|
};
|
||||||
|
|
||||||
|
Container::Container(array_view<uint8_t> memory) {
|
||||||
|
this->memory.resize(memory.size());
|
||||||
|
nall::memory::copy(this->memory.data(), memory.data(), memory.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Container::~Container() {
|
||||||
|
metadata = {};
|
||||||
|
signature = {};
|
||||||
|
encryption = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto Container::compressLZSA() -> void {
|
||||||
|
compression.type = "lzsa";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::signEd25519(uint256_t privateKey) -> void {
|
||||||
|
signature.type = "ed25519";
|
||||||
|
signature.privateKey = privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::encryptXChaCha20(uint256_t privateKey, uint192_t nonce) -> void {
|
||||||
|
if(!nonce) {
|
||||||
|
CSPRNG::XChaCha20 csprng;
|
||||||
|
nonce = csprng.random<uint192_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
encryption.type = "xchacha20";
|
||||||
|
encryption.privateKey = privateKey;
|
||||||
|
encryption.nonce = nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto Container::validate() -> bool {
|
||||||
|
array_view<uint8_t> memory = this->memory;
|
||||||
|
if(memory.size() < 44) return false; //8 (metadata size) + 32 (SHA256) + 4 (signature)
|
||||||
|
|
||||||
|
if(memory[memory.size() - 4] != 'B') return false;
|
||||||
|
if(memory[memory.size() - 3] != 'P') return false;
|
||||||
|
if(memory[memory.size() - 2] != 'A') return false;
|
||||||
|
if(memory[memory.size() - 1] != '1') return false;
|
||||||
|
|
||||||
|
auto sha256 = memory.readl<uint256_t>(memory.size() - 36, 32);
|
||||||
|
if(Hash::SHA256({memory.data(), memory.size() - 36}).value() != sha256) return false;
|
||||||
|
|
||||||
|
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
|
||||||
|
|
||||||
|
if(size & 1ull << 63) {
|
||||||
|
size -= 1ull << 63;
|
||||||
|
metadata = memory.view(memory.size() - 44 - size, size);
|
||||||
|
uint64_t offset = memory.size() - 44 - size;
|
||||||
|
for(auto& byte : metadata) byte ^= offset++;
|
||||||
|
} else {
|
||||||
|
metadata = memory.view(memory.size() - 44 - size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto document = BML::unserialize(metadata);
|
||||||
|
|
||||||
|
if(auto node = document["archive/encryption"]) {
|
||||||
|
if(node.text() == "xchacha20") {
|
||||||
|
encryption.type = node.text();
|
||||||
|
encryption.nonce = Decode::Base<57, uint192_t>(node["nonce"].text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto node = document["archive/signature"]) {
|
||||||
|
if(node.text() == "ed25519") {
|
||||||
|
signature.type = node.text();
|
||||||
|
signature.publicKey = Decode::Base<57, uint256_t>(node["publicKey"].text());
|
||||||
|
signature.value = Decode::Base<57, uint512_t>(node["value"].text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto node = document["archive/compression"]) {
|
||||||
|
compression.type = node.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::decryptXChaCha20(uint256_t privateKey) -> bool {
|
||||||
|
encryption.privateKey = privateKey;
|
||||||
|
Cipher::XChaCha20 xchacha20{encryption.privateKey, encryption.nonce};
|
||||||
|
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
|
||||||
|
memory = xchacha20.decrypt(memory.view(0, memory.size() - 44 - size));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::verifyEd25519(uint256_t publicKey) -> bool {
|
||||||
|
EllipticCurve::Ed25519 ed25519;
|
||||||
|
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
|
||||||
|
return ed25519.verify(memory.view(0, memory.size() - 44 - size), signature.value, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::decompressLZSA() -> bool {
|
||||||
|
memory = Decode::LZSA(memory);
|
||||||
|
return (bool)memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto Container::append(string name, string location) -> shared_pointer<Node> {
|
||||||
|
for(auto& node : nodes) if(node->name == name) return {};
|
||||||
|
if(auto node = Node::create(name, location)) return nodes.append(node), node;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::appendPath(string name) -> shared_pointer<Node> {
|
||||||
|
for(auto& node : nodes) if(node->name == name) return {};
|
||||||
|
if(auto node = Node::createPath(name)) return nodes.append(node), node;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::appendFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node> {
|
||||||
|
for(auto& node : nodes) if(node->name == name) return {};
|
||||||
|
if(auto node = Node::createFile(name, memory)) return nodes.append(node), node;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::remove(string name) -> bool {
|
||||||
|
if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes.remove(*offset), true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::find(string name) -> shared_pointer<Node> {
|
||||||
|
if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes[*offset];
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Container::sort() -> void {
|
||||||
|
nodes.sort([&](auto& lhs, auto& rhs) { return string::icompare(lhs->name, rhs->name) < 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
}}}
|
|
@ -0,0 +1,86 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/beat/archive/node.hpp>
|
||||||
|
#include <nall/beat/archive/container.hpp>
|
||||||
|
|
||||||
|
namespace nall { namespace Beat { namespace Archive {
|
||||||
|
|
||||||
|
auto create(Container& container, string name) -> vector<uint8_t> {
|
||||||
|
auto& metadata = container.metadata;
|
||||||
|
metadata = {};
|
||||||
|
metadata.append("archive: ", Location::file(name), "\n");
|
||||||
|
|
||||||
|
vector<uint8_t> memory;
|
||||||
|
|
||||||
|
container.sort();
|
||||||
|
for(auto& node : container.nodes) {
|
||||||
|
if(node->isFile()) {
|
||||||
|
node->offset = memory.size();
|
||||||
|
memory.append(node->memory);
|
||||||
|
}
|
||||||
|
metadata.append(node->metadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata.append(" size: ", memory.size(), "\n");
|
||||||
|
|
||||||
|
if(container.compression.type == "lzsa") {
|
||||||
|
memory = Encode::LZSA(memory);
|
||||||
|
metadata.append(" compression: lzsa\n");
|
||||||
|
metadata.append(" size: ", memory.size(), "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(container.signature.type == "ed25519") {
|
||||||
|
EllipticCurve::Ed25519 ed25519;
|
||||||
|
container.signature.publicKey = ed25519.publicKey(container.signature.privateKey);
|
||||||
|
container.signature.value = ed25519.sign(memory, container.signature.privateKey);
|
||||||
|
|
||||||
|
metadata.append(" signature: ed25519\n");
|
||||||
|
metadata.append(" publicKey: ", Encode::Base<57>(container.signature.publicKey), "\n");
|
||||||
|
metadata.append(" value: ", Encode::Base<57>(container.signature.value), "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& byte : metadata) memory.append(byte);
|
||||||
|
memory.appendl((uint64_t)metadata.size(), 8);
|
||||||
|
|
||||||
|
auto sha256 = Hash::SHA256(memory).value();
|
||||||
|
memory.appendl((uint256_t)sha256, 32);
|
||||||
|
|
||||||
|
memory.append('B');
|
||||||
|
memory.append('P');
|
||||||
|
memory.append('A');
|
||||||
|
memory.append('1');
|
||||||
|
|
||||||
|
if(container.encryption.type == "xchacha20") {
|
||||||
|
Cipher::XChaCha20 xchacha20{container.encryption.privateKey, container.encryption.nonce};
|
||||||
|
memory = xchacha20.encrypt(memory);
|
||||||
|
|
||||||
|
metadata = {};
|
||||||
|
metadata.append("archive\n");
|
||||||
|
metadata.append(" encryption: xchacha20\n");
|
||||||
|
metadata.append(" nonce: ", Encode::Base<57>(container.encryption.nonce), "\n");
|
||||||
|
|
||||||
|
if(container.signature.type == "ed25519") {
|
||||||
|
EllipticCurve::Ed25519 ed25519;
|
||||||
|
container.signature.value = ed25519.sign(memory, container.signature.privateKey);
|
||||||
|
|
||||||
|
metadata.append(" signature: ed25519\n");
|
||||||
|
//metadata.append(" publicKey: ", Encode::Base<57>(container.signature.publicKey), "\n");
|
||||||
|
metadata.append(" value: ", Encode::Base<57>(container.signature.value), "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& byte : metadata) memory.append(byte ^ memory.size());
|
||||||
|
memory.appendl((uint64_t)metadata.size() | 1ull << 63, 8);
|
||||||
|
|
||||||
|
auto sha256 = Hash::SHA256(memory).value();
|
||||||
|
memory.appendl((uint256_t)sha256, 32);
|
||||||
|
|
||||||
|
memory.append('B');
|
||||||
|
memory.append('P');
|
||||||
|
memory.append('A');
|
||||||
|
memory.append('1');
|
||||||
|
}
|
||||||
|
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}}}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/beat/archive/node.hpp>
|
||||||
|
#include <nall/beat/archive/container.hpp>
|
||||||
|
|
||||||
|
namespace nall { namespace Beat { namespace Archive {
|
||||||
|
|
||||||
|
auto extract(Container& container) -> bool {
|
||||||
|
function<void (Markup::Node)> extract = [&](auto metadata) {
|
||||||
|
if(metadata.name() != "path" && metadata.name() != "file") return;
|
||||||
|
shared_pointer<Node> node = new Node;
|
||||||
|
if(node->unserialize(container.memory, metadata)) {
|
||||||
|
container.nodes.append(node);
|
||||||
|
}
|
||||||
|
if(metadata.name() != "path") return;
|
||||||
|
for(auto node : metadata) extract(node);
|
||||||
|
};
|
||||||
|
|
||||||
|
container.nodes.reset();
|
||||||
|
auto document = BML::unserialize(container.metadata);
|
||||||
|
for(auto node : document["archive"]) extract(node);
|
||||||
|
container.sort();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}}}
|
|
@ -0,0 +1,332 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/arithmetic.hpp>
|
||||||
|
#include <nall/array-view.hpp>
|
||||||
|
#include <nall/random.hpp>
|
||||||
|
#include <nall/cipher/chacha20.hpp>
|
||||||
|
#include <nall/elliptic-curve/ed25519.hpp>
|
||||||
|
#include <nall/decode/base.hpp>
|
||||||
|
#include <nall/encode/base.hpp>
|
||||||
|
#include <nall/decode/lzsa.hpp>
|
||||||
|
#include <nall/encode/lzsa.hpp>
|
||||||
|
|
||||||
|
namespace nall { namespace Beat { namespace Archive {
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
static auto create(string name, string location) -> shared_pointer<Node>;
|
||||||
|
static auto createPath(string name) -> shared_pointer<Node>;
|
||||||
|
static auto createFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node>;
|
||||||
|
|
||||||
|
explicit operator bool() const { return (bool)name; }
|
||||||
|
auto isPath() const -> bool { return name.endsWith("/"); }
|
||||||
|
auto isFile() const -> bool { return !name.endsWith("/"); }
|
||||||
|
auto isCompressed() const -> bool { return (bool)compression.type; }
|
||||||
|
|
||||||
|
auto metadata(bool indented = true) const -> string;
|
||||||
|
auto compressLZSA() -> bool;
|
||||||
|
|
||||||
|
auto unserialize(array_view<uint8_t> container, Markup::Node metadata) -> bool;
|
||||||
|
auto decompress() -> bool;
|
||||||
|
|
||||||
|
auto getTimestamp(string) const -> uint64_t;
|
||||||
|
auto getPermissions() const -> uint;
|
||||||
|
auto getOwner() const -> string;
|
||||||
|
auto getGroup() const -> string;
|
||||||
|
|
||||||
|
//files and paths
|
||||||
|
string name;
|
||||||
|
|
||||||
|
bool timestamps = false;
|
||||||
|
struct Timestamp {
|
||||||
|
string created;
|
||||||
|
string modified;
|
||||||
|
string accessed;
|
||||||
|
} timestamp;
|
||||||
|
|
||||||
|
bool permissions = false;
|
||||||
|
struct Permission {
|
||||||
|
struct Owner {
|
||||||
|
string name;
|
||||||
|
bool readable = false;
|
||||||
|
bool writable = false;
|
||||||
|
bool executable = false;
|
||||||
|
} owner;
|
||||||
|
struct Group {
|
||||||
|
string name;
|
||||||
|
bool readable = false;
|
||||||
|
bool writable = false;
|
||||||
|
bool executable = false;
|
||||||
|
} group;
|
||||||
|
struct Other {
|
||||||
|
bool readable = false;
|
||||||
|
bool writable = false;
|
||||||
|
bool executable = false;
|
||||||
|
} other;
|
||||||
|
} permission;
|
||||||
|
|
||||||
|
//files only
|
||||||
|
vector<uint8_t> memory;
|
||||||
|
uint64_t offset = 0;
|
||||||
|
|
||||||
|
struct Compression {
|
||||||
|
string type;
|
||||||
|
uint size = 0; //decompressed size; memory.size() == compressed size
|
||||||
|
} compression;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto Node::create(string name, string location) -> shared_pointer<Node> {
|
||||||
|
if(!inode::exists(location)) return {};
|
||||||
|
shared_pointer<Node> node = new Node;
|
||||||
|
|
||||||
|
node->name = name;
|
||||||
|
|
||||||
|
node->timestamps = true;
|
||||||
|
node->timestamp.created = chrono::utc::datetime(inode::timestamp(location, inode::time::create));
|
||||||
|
node->timestamp.modified = chrono::utc::datetime(inode::timestamp(location, inode::time::modify));
|
||||||
|
node->timestamp.accessed = chrono::utc::datetime(inode::timestamp(location, inode::time::access));
|
||||||
|
|
||||||
|
uint mode = inode::mode(location);
|
||||||
|
node->permissions = true;
|
||||||
|
node->permission.owner.name = inode::owner(location);
|
||||||
|
node->permission.group.name = inode::group(location);
|
||||||
|
node->permission.owner.readable = mode & 0400;
|
||||||
|
node->permission.owner.writable = mode & 0200;
|
||||||
|
node->permission.owner.executable = mode & 0100;
|
||||||
|
node->permission.group.readable = mode & 0040;
|
||||||
|
node->permission.group.writable = mode & 0020;
|
||||||
|
node->permission.group.executable = mode & 0010;
|
||||||
|
node->permission.other.readable = mode & 0004;
|
||||||
|
node->permission.other.writable = mode & 0002;
|
||||||
|
node->permission.other.executable = mode & 0001;
|
||||||
|
|
||||||
|
if(file::exists(location)) {
|
||||||
|
node->memory = file::read(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::createPath(string name) -> shared_pointer<Node> {
|
||||||
|
if(!name) return {};
|
||||||
|
shared_pointer<Node> node = new Node;
|
||||||
|
node->name = name;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::createFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node> {
|
||||||
|
if(!name) return {};
|
||||||
|
shared_pointer<Node> node = new Node;
|
||||||
|
node->name = name;
|
||||||
|
node->memory.resize(memory.size());
|
||||||
|
memory::copy(node->memory.data(), memory.data(), memory.size());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::metadata(bool indented) const -> string {
|
||||||
|
string metadata;
|
||||||
|
if(!name) return metadata;
|
||||||
|
|
||||||
|
string indent;
|
||||||
|
if(indented) {
|
||||||
|
indent.append(" ");
|
||||||
|
auto bytes = string{name}.trimRight("/");
|
||||||
|
for(auto& byte : bytes) {
|
||||||
|
if(byte == '/') indent.append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isPath()) {
|
||||||
|
metadata.append(indent, "path: ", name, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isFile()) {
|
||||||
|
metadata.append(indent, "file: ", name, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(timestamps) {
|
||||||
|
metadata.append(indent, " timestamp\n");
|
||||||
|
if(timestamp.created != timestamp.modified)
|
||||||
|
metadata.append(indent, " created: ", timestamp.created, "\n");
|
||||||
|
metadata.append(indent, " modified: ", timestamp.modified, "\n");
|
||||||
|
if(timestamp.accessed != timestamp.modified)
|
||||||
|
metadata.append(indent, " accessed: ", timestamp.accessed, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(permissions) {
|
||||||
|
metadata.append(indent, " permission\n");
|
||||||
|
metadata.append(indent, " owner: ", permission.owner.name, "\n");
|
||||||
|
if(permission.owner.readable)
|
||||||
|
metadata.append(indent, " readable\n");
|
||||||
|
if(permission.owner.writable)
|
||||||
|
metadata.append(indent, " writable\n");
|
||||||
|
if(permission.owner.executable)
|
||||||
|
metadata.append(indent, " executable\n");
|
||||||
|
metadata.append(indent, " group: ", permission.group.name, "\n");
|
||||||
|
if(permission.group.readable)
|
||||||
|
metadata.append(indent, " readable\n");
|
||||||
|
if(permission.group.writable)
|
||||||
|
metadata.append(indent, " writable\n");
|
||||||
|
if(permission.group.executable)
|
||||||
|
metadata.append(indent, " executable\n");
|
||||||
|
metadata.append(indent, " other\n");
|
||||||
|
if(permission.other.readable)
|
||||||
|
metadata.append(indent, " readable\n");
|
||||||
|
if(permission.other.writable)
|
||||||
|
metadata.append(indent, " writable\n");
|
||||||
|
if(permission.other.executable)
|
||||||
|
metadata.append(indent, " executable\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isFile()) {
|
||||||
|
metadata.append(indent, " offset: ", offset, "\n");
|
||||||
|
if(!isCompressed()) {
|
||||||
|
metadata.append(indent, " size: ", memory.size(), "\n");
|
||||||
|
} else {
|
||||||
|
metadata.append(indent, " size: ", compression.size, "\n");
|
||||||
|
metadata.append(indent, " compression: ", compression.type, "\n");
|
||||||
|
metadata.append(indent, " size: ", memory.size(), "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::unserialize(array_view<uint8_t> container, Markup::Node metadata) -> bool {
|
||||||
|
*this = {};
|
||||||
|
if(!metadata.text()) return false;
|
||||||
|
|
||||||
|
name = metadata.text();
|
||||||
|
|
||||||
|
if(auto node = metadata["timestamp"]) {
|
||||||
|
timestamps = true;
|
||||||
|
if(auto created = node["created" ]) timestamp.created = created.text();
|
||||||
|
if(auto modified = node["modified"]) timestamp.modified = modified.text();
|
||||||
|
if(auto accessed = node["accessed"]) timestamp.accessed = accessed.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto node = metadata["permission"]) {
|
||||||
|
permissions = true;
|
||||||
|
if(auto owner = node["owner"]) {
|
||||||
|
permission.owner.name = owner.text();
|
||||||
|
permission.owner.readable = (bool)owner["readable"];
|
||||||
|
permission.owner.writable = (bool)owner["writable"];
|
||||||
|
permission.owner.executable = (bool)owner["executable"];
|
||||||
|
}
|
||||||
|
if(auto group = node["group"]) {
|
||||||
|
permission.group.name = group.text();
|
||||||
|
permission.group.readable = (bool)group["readable"];
|
||||||
|
permission.group.writable = (bool)group["writable"];
|
||||||
|
permission.group.executable = (bool)group["executable"];
|
||||||
|
}
|
||||||
|
if(auto other = node["other"]) {
|
||||||
|
permission.other.readable = (bool)other["readable"];
|
||||||
|
permission.other.writable = (bool)other["writable"];
|
||||||
|
permission.other.executable = (bool)other["executable"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isPath()) return true;
|
||||||
|
|
||||||
|
uint offset = metadata["offset"].natural();
|
||||||
|
uint size = metadata["size"].natural();
|
||||||
|
|
||||||
|
if(metadata["compression"]) {
|
||||||
|
size = metadata["compression/size"].natural();
|
||||||
|
compression.type = metadata["compression"].text();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(offset + size >= container.size()) return false;
|
||||||
|
|
||||||
|
memory.reallocate(size);
|
||||||
|
nall::memory::copy(memory.data(), container.view(offset, size), size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::compressLZSA() -> bool {
|
||||||
|
if(!memory) return true; //don't compress empty files
|
||||||
|
if(isCompressed()) return true; //don't recompress files
|
||||||
|
|
||||||
|
auto compressedMemory = Encode::LZSA(memory);
|
||||||
|
if(compressedMemory.size() >= memory.size()) return true; //can't compress smaller than original size
|
||||||
|
|
||||||
|
compression.type = "lzsa";
|
||||||
|
compression.size = memory.size();
|
||||||
|
memory = move(compressedMemory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::decompress() -> bool {
|
||||||
|
if(!isCompressed()) return true;
|
||||||
|
|
||||||
|
if(compression.type == "lzsa") {
|
||||||
|
compression = {};
|
||||||
|
memory = Decode::LZSA(memory);
|
||||||
|
return (bool)memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::getTimestamp(string type) const -> uint64_t {
|
||||||
|
if(!timestamps) return time(nullptr);
|
||||||
|
|
||||||
|
string value = chrono::utc::datetime();
|
||||||
|
if(type == "created" ) value = timestamp.created;
|
||||||
|
if(type == "modified") value = timestamp.modified;
|
||||||
|
if(type == "accessed") value = timestamp.accessed;
|
||||||
|
|
||||||
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
|
struct tm timeInfo{};
|
||||||
|
if(strptime(value, "%Y-%m-%d %H:%M:%S", &timeInfo) != nullptr) {
|
||||||
|
//todo: not thread safe ...
|
||||||
|
auto tz = getenv("TZ");
|
||||||
|
setenv("TZ", "", 1);
|
||||||
|
timeInfo.tm_isdst = -1;
|
||||||
|
auto result = mktime(&timeInfo);
|
||||||
|
if(tz) setenv("TZ", tz, 1);
|
||||||
|
else unsetenv("TZ");
|
||||||
|
if(result != -1) return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return time(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::getPermissions() const -> uint {
|
||||||
|
if(!permissions) return 0755;
|
||||||
|
uint mode = 0;
|
||||||
|
if(permission.owner.readable ) mode |= 0400;
|
||||||
|
if(permission.owner.writable ) mode |= 0200;
|
||||||
|
if(permission.owner.executable) mode |= 0100;
|
||||||
|
if(permission.group.readable ) mode |= 0040;
|
||||||
|
if(permission.group.writable ) mode |= 0020;
|
||||||
|
if(permission.group.executable) mode |= 0010;
|
||||||
|
if(permission.other.readable ) mode |= 0004;
|
||||||
|
if(permission.other.writable ) mode |= 0002;
|
||||||
|
if(permission.other.executable) mode |= 0001;
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::getOwner() const -> string {
|
||||||
|
if(!permissions || !permission.owner.name) {
|
||||||
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
|
struct passwd* pwd = getpwuid(getuid());
|
||||||
|
assert(pwd);
|
||||||
|
return pwd->pw_name;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return permission.owner.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::getGroup() const -> string {
|
||||||
|
if(!permissions || !permission.group.name) {
|
||||||
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
|
struct group* grp = getgrgid(getgid());
|
||||||
|
assert(grp);
|
||||||
|
return grp->gr_name;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return permission.group.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}}}
|
|
@ -4,10 +4,8 @@
|
||||||
|
|
||||||
namespace nall { namespace Decode {
|
namespace nall { namespace Decode {
|
||||||
|
|
||||||
inline auto LZSA(const void* data) -> vector<uint8_t> {
|
inline auto LZSA(array_view<uint8_t> input) -> vector<uint8_t> {
|
||||||
vector<uint8_t> output;
|
vector<uint8_t> output;
|
||||||
|
|
||||||
auto input = (const uint8_t*)data;
|
|
||||||
uint index = 0;
|
uint index = 0;
|
||||||
|
|
||||||
uint size = 0;
|
uint size = 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nall/filemap.hpp>
|
#include <nall/file-map.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
#include <nall/vector.hpp>
|
#include <nall/vector.hpp>
|
||||||
#include <nall/decode/inflate.hpp>
|
#include <nall/decode/inflate.hpp>
|
||||||
|
@ -24,7 +24,7 @@ struct ZIP {
|
||||||
|
|
||||||
auto open(const string& filename) -> bool {
|
auto open(const string& filename) -> bool {
|
||||||
close();
|
close();
|
||||||
if(fm.open(filename, filemap::mode::read) == false) return false;
|
if(fm.open(filename, file::mode::read) == false) return false;
|
||||||
if(open(fm.data(), fm.size()) == false) {
|
if(open(fm.data(), fm.size()) == false) {
|
||||||
fm.close();
|
fm.close();
|
||||||
return false;
|
return false;
|
||||||
|
@ -115,11 +115,11 @@ struct ZIP {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto close() -> void {
|
auto close() -> void {
|
||||||
if(fm.open()) fm.close();
|
if(fm) fm.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
filemap fm;
|
file_map fm;
|
||||||
const uint8_t* filedata;
|
const uint8_t* filedata;
|
||||||
uint filesize;
|
uint filesize;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nall/file.hpp>
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/function.hpp>
|
||||||
#include <nall/inode.hpp>
|
#include <nall/inode.hpp>
|
||||||
#include <nall/intrinsics.hpp>
|
#include <nall/intrinsics.hpp>
|
||||||
#include <nall/merge-sort.hpp>
|
#include <nall/merge-sort.hpp>
|
||||||
|
@ -18,6 +19,8 @@
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
struct directory : inode {
|
struct directory : inode {
|
||||||
|
directory() = delete;
|
||||||
|
|
||||||
static auto create(const string& pathname, uint permissions = 0755) -> bool; //recursive
|
static auto create(const string& pathname, uint permissions = 0755) -> bool; //recursive
|
||||||
static auto remove(const string& pathname) -> bool; //recursive
|
static auto remove(const string& pathname) -> bool; //recursive
|
||||||
static auto exists(const string& pathname) -> bool;
|
static auto exists(const string& pathname) -> bool;
|
||||||
|
@ -64,6 +67,84 @@ struct directory : inode {
|
||||||
return folders;
|
return folders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto rcontents(const string& pathname, const string& pattern = "*") -> vector<string> {
|
||||||
|
vector<string> contents;
|
||||||
|
function<void (const string&, const string&, const string&)>
|
||||||
|
recurse = [&](const string& basename, const string& pathname, const string& pattern) {
|
||||||
|
for(auto& folder : directory::ufolders(pathname)) {
|
||||||
|
contents.append(string{pathname, folder}.trimLeft(basename, 1L));
|
||||||
|
recurse(basename, {pathname, folder}, pattern);
|
||||||
|
}
|
||||||
|
for(auto& file : directory::ufiles(pathname, pattern)) {
|
||||||
|
contents.append(string{pathname, file}.trimLeft(basename, 1L));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for(auto& folder : directory::ufolders(pathname)) {
|
||||||
|
contents.append(folder);
|
||||||
|
recurse(pathname, {pathname, folder}, pattern);
|
||||||
|
}
|
||||||
|
for(auto& file : directory::ufiles(pathname, pattern)) {
|
||||||
|
contents.append(file);
|
||||||
|
}
|
||||||
|
contents.sort();
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto ircontents(const string& pathname, const string& pattern = "*") -> vector<string> {
|
||||||
|
vector<string> contents;
|
||||||
|
function<void (const string&, const string&, const string&)>
|
||||||
|
recurse = [&](const string& basename, const string& pathname, const string& pattern) {
|
||||||
|
for(auto& folder : directory::ufolders(pathname)) {
|
||||||
|
contents.append(string{pathname, folder}.trimLeft(basename, 1L));
|
||||||
|
recurse(basename, {pathname, folder}, pattern);
|
||||||
|
}
|
||||||
|
for(auto& file : directory::ufiles(pathname, pattern)) {
|
||||||
|
contents.append(string{pathname, file}.trimLeft(basename, 1L));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for(auto& folder : directory::ufolders(pathname)) {
|
||||||
|
contents.append(folder);
|
||||||
|
recurse(pathname, {pathname, folder}, pattern);
|
||||||
|
}
|
||||||
|
for(auto& file : directory::ufiles(pathname, pattern)) {
|
||||||
|
contents.append(file);
|
||||||
|
}
|
||||||
|
contents.isort();
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto rfolders(const string& pathname, const string& pattern = "*") -> vector<string> {
|
||||||
|
vector<string> folders;
|
||||||
|
for(auto& folder : rcontents(pathname, pattern)) {
|
||||||
|
if(directory::exists({pathname, folder})) folders.append(folder);
|
||||||
|
}
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto irfolders(const string& pathname, const string& pattern = "*") -> vector<string> {
|
||||||
|
vector<string> folders;
|
||||||
|
for(auto& folder : ircontents(pathname, pattern)) {
|
||||||
|
if(directory::exists({pathname, folder})) folders.append(folder);
|
||||||
|
}
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto rfiles(const string& pathname, const string& pattern = "*") -> vector<string> {
|
||||||
|
vector<string> files;
|
||||||
|
for(auto& file : rcontents(pathname, pattern)) {
|
||||||
|
if(file::exists({pathname, file})) files.append(file);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto irfiles(const string& pathname, const string& pattern = "*") -> vector<string> {
|
||||||
|
vector<string> files;
|
||||||
|
for(auto& file : ircontents(pathname, pattern)) {
|
||||||
|
if(file::exists({pathname, file})) files.append(file);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//internal functions; these return unsorted lists
|
//internal functions; these return unsorted lists
|
||||||
static auto ufolders(const string& pathname, const string& pattern = "*") -> vector<string>;
|
static auto ufolders(const string& pathname, const string& pattern = "*") -> vector<string>;
|
||||||
|
|
|
@ -3,50 +3,52 @@
|
||||||
#if defined(EC_REFERENCE)
|
#if defined(EC_REFERENCE)
|
||||||
#include <nall/elliptic-curve/modulo25519-reference.hpp>
|
#include <nall/elliptic-curve/modulo25519-reference.hpp>
|
||||||
#else
|
#else
|
||||||
#include <nall/elliptic-curve/modulo25519.hpp>
|
#include <nall/elliptic-curve/modulo25519-optimized.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace nall { namespace EllipticCurve {
|
namespace nall { namespace EllipticCurve {
|
||||||
|
|
||||||
struct Curve25519 {
|
struct Curve25519 {
|
||||||
auto sharedKey(uint256_t secretKey, uint256_t basepoint = 9) const -> uint256_t {
|
auto sharedKey(uint256_t secretKey, uint256_t basepoint = 9) const -> uint256_t {
|
||||||
secretKey &= ((0_u256 - 1) >> 2) - 7;
|
secretKey &= (1_u256 << 254) - 8;
|
||||||
secretKey |= 1_u256 << 254;
|
secretKey |= (1_u256 << 254);
|
||||||
basepoint &= (0_u256 - 1) >> 1;
|
basepoint &= ~0_u256 >> 1;
|
||||||
|
|
||||||
point p = scalarMultiply(secretKey, modP(basepoint));
|
point p = scalarMultiply(basepoint % P, secretKey);
|
||||||
return p.x * p.z.reciprocal();
|
field k = p.x * reciprocal(p.z);
|
||||||
|
return k();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using field = Modulo25519;
|
using field = Modulo25519;
|
||||||
struct point { field x, z; };
|
struct point { field x, z; };
|
||||||
|
const BarrettReduction<256> P = BarrettReduction<256>{EllipticCurve::P};
|
||||||
inline auto montgomeryAdd(point p, point q, field b) const -> point {
|
|
||||||
return {
|
|
||||||
(p.x * q.x - p.z * q.z).square(),
|
|
||||||
(p.x * q.z - p.z * q.x).square() * b
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto montgomeryDouble(point p) const -> point {
|
inline auto montgomeryDouble(point p) const -> point {
|
||||||
field a = (p.x + p.z).square();
|
field a = square(p.x + p.z);
|
||||||
field b = (p.x - p.z).square();
|
field b = square(p.x - p.z);
|
||||||
field c = a - b;
|
field c = a - b;
|
||||||
field d = a + c * 121665;
|
field d = a + c * 121665;
|
||||||
return {a * b, c * d};
|
return {a * b, c * d};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto scalarMultiply(uint256_t e, field b) const -> point {
|
inline auto montgomeryAdd(point p, point q, field b) const -> point {
|
||||||
|
return {
|
||||||
|
square(p.x * q.x - p.z * q.z),
|
||||||
|
square(p.x * q.z - p.z * q.x) * b
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto scalarMultiply(field b, uint256_t exponent) const -> point {
|
||||||
point p{1, 0}, q{b, 1};
|
point p{1, 0}, q{b, 1};
|
||||||
for(uint n : reverse(range(255))) {
|
for(uint bit : reverse(range(255))) {
|
||||||
bool bit = e >> n & 1;
|
bool condition = exponent >> bit & 1;
|
||||||
cswap(bit, p.x, q.x);
|
cswap(condition, p.x, q.x);
|
||||||
cswap(bit, p.z, q.z);
|
cswap(condition, p.z, q.z);
|
||||||
q = montgomeryAdd(p, q, b);
|
q = montgomeryAdd(p, q, b);
|
||||||
p = montgomeryDouble(p);
|
p = montgomeryDouble(p);
|
||||||
cswap(bit, p.x, q.x);
|
cswap(condition, p.x, q.x);
|
||||||
cswap(bit, p.z, q.z);
|
cswap(condition, p.z, q.z);
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,64 +4,42 @@
|
||||||
#if defined(EC_REFERENCE)
|
#if defined(EC_REFERENCE)
|
||||||
#include <nall/elliptic-curve/modulo25519-reference.hpp>
|
#include <nall/elliptic-curve/modulo25519-reference.hpp>
|
||||||
#else
|
#else
|
||||||
#include <nall/elliptic-curve/modulo25519.hpp>
|
#include <nall/elliptic-curve/modulo25519-optimized.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace nall { namespace EllipticCurve {
|
namespace nall { namespace EllipticCurve {
|
||||||
|
|
||||||
|
static const uint256_t L = (1_u256 << 252) + 27742317777372353535851937790883648493_u256;
|
||||||
|
|
||||||
struct Ed25519 {
|
struct Ed25519 {
|
||||||
Ed25519() {
|
|
||||||
field y = field(4) * field(5).reciprocal();
|
|
||||||
field x = recoverX(y);
|
|
||||||
point B{x, y, 1, x * y};
|
|
||||||
for(uint n : range(253)) {
|
|
||||||
Bscalar[n] = B;
|
|
||||||
B = edwardsDouble(B);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto publicKey(uint256_t privateKey) const -> uint256_t {
|
auto publicKey(uint256_t privateKey) const -> uint256_t {
|
||||||
auto H = uint512_t{Hash::SHA512(to_vector(privateKey)).output()};
|
return compress(scalarMultiply(B, clamp(hash(privateKey)) % L));
|
||||||
auto a = clamp(H);
|
|
||||||
auto A = compress(scalarMultiplyB(modL(a)));
|
|
||||||
return A;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sign(const vector<uint8_t>& message, uint256_t privateKey) const -> uint512_t {
|
auto sign(array_view<uint8_t> message, uint256_t privateKey) const -> uint512_t {
|
||||||
auto H = uint512_t{Hash::SHA512(to_vector(privateKey)).output()};
|
uint512_t H = hash(privateKey);
|
||||||
auto a = clamp(H);
|
uint256_t a = clamp(H) % L;
|
||||||
auto A = compress(scalarMultiplyB(modL(a)));
|
uint256_t A = compress(scalarMultiply(B, a));
|
||||||
|
|
||||||
Hash::SHA512 hash1;
|
uint512_t r = hash(upper(H), message) % L;
|
||||||
hash1.input(to_vector(upper(H)));
|
uint256_t R = compress(scalarMultiply(B, r));
|
||||||
hash1.input(message);
|
|
||||||
auto r = uint512_t{hash1.output()};
|
|
||||||
auto R = compress(scalarMultiplyB(modL(r)));
|
|
||||||
|
|
||||||
Hash::SHA512 hash2;
|
uint512_t k = hash(R, A, message) % L;
|
||||||
hash2.input(to_vector(R));
|
uint256_t S = (k * a + r) % L;
|
||||||
hash2.input(to_vector(A));
|
|
||||||
hash2.input(message);
|
|
||||||
uint512_t k = modL(uint512_t{hash2.output()});
|
|
||||||
uint256_t S = modL(k * a + r);
|
|
||||||
|
|
||||||
return uint512_t(S) << 256 | R;
|
return uint512_t(S) << 256 | R;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto verify(const vector<uint8_t>& message, uint512_t signature, uint256_t publicKey) const -> bool {
|
auto verify(array_view<uint8_t> message, uint512_t signature, uint256_t publicKey) const -> bool {
|
||||||
auto R = decompress(lower(signature));
|
auto R = decompress(lower(signature));
|
||||||
auto A = decompress(publicKey);
|
auto A = decompress(publicKey);
|
||||||
if(!R || !A) return false;
|
if(!R || !A) return false;
|
||||||
uint256_t S = upper(signature);
|
|
||||||
|
|
||||||
Hash::SHA512 hash;
|
uint256_t S = upper(signature) % L;
|
||||||
hash.input(to_vector(lower(signature)));
|
uint512_t r = hash(lower(signature), publicKey, message) % L;
|
||||||
hash.input(to_vector(publicKey));
|
|
||||||
hash.input(message);
|
|
||||||
auto r = uint512_t{hash.output()};
|
|
||||||
|
|
||||||
auto p = scalarMultiplyB(modL(S));
|
auto p = scalarMultiply(B, S);
|
||||||
auto q = edwardsAdd(R(), scalarMultiply(modL(r), A()));
|
auto q = edwardsAdd(R(), scalarMultiply(A(), r));
|
||||||
if(!onCurve(p) || !onCurve(q)) return false;
|
if(!onCurve(p) || !onCurve(q)) return false;
|
||||||
if(p.x * q.z - q.x * p.z) return false;
|
if(p.x * q.z - q.x * p.z) return false;
|
||||||
if(p.y * q.z - q.y * p.z) return false;
|
if(p.y * q.z - q.y * p.z) return false;
|
||||||
|
@ -71,31 +49,46 @@ struct Ed25519 {
|
||||||
private:
|
private:
|
||||||
using field = Modulo25519;
|
using field = Modulo25519;
|
||||||
struct point { field x, y, z, t; };
|
struct point { field x, y, z, t; };
|
||||||
point Bscalar[253];
|
const field D = -field(121665) * reciprocal(field(121666));
|
||||||
const field D = -field(121665) * field(121666).reciprocal();
|
const point B = *decompress((field(4) * reciprocal(field(5)))());
|
||||||
|
const BarrettReduction<256> L = BarrettReduction<256>{EllipticCurve::L};
|
||||||
|
|
||||||
inline auto clamp(uint256_t p) const -> uint256_t {
|
inline auto input(Hash::SHA512&) const -> void {}
|
||||||
p &= ((0_u256 - 1) >> 2) - 7;
|
|
||||||
p |= 1_u256 << 254;
|
template<typename... P> inline auto input(Hash::SHA512& hash, uint256_t value, P&&... p) const -> void {
|
||||||
return p;
|
for(uint byte : range(32)) hash.input(uint8_t(value >> byte * 8));
|
||||||
|
input(hash, forward<P>(p)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto recoverX(field y) const -> field {
|
template<typename... P> inline auto input(Hash::SHA512& hash, array_view<uint8_t> value, P&&... p) const -> void {
|
||||||
field y2 = y.square();
|
hash.input(value);
|
||||||
field x = ((y2 - 1) * (D * y2 + 1).reciprocal()).squareRoot();
|
input(hash, forward<P>(p)...);
|
||||||
return x() & 1 ? -x : x;
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto hash(P&&... p) const -> uint512_t {
|
||||||
|
Hash::SHA512 hash;
|
||||||
|
input(hash, forward<P>(p)...);
|
||||||
|
uint512_t result;
|
||||||
|
for(auto byte : reverse(hash.output())) result = result << 8 | byte;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto clamp(uint256_t p) const -> uint256_t {
|
||||||
|
p &= (1_u256 << 254) - 8;
|
||||||
|
p |= (1_u256 << 254);
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto onCurve(point p) const -> bool {
|
inline auto onCurve(point p) const -> bool {
|
||||||
if(!p.z) return false;
|
if(!p.z) return false;
|
||||||
if(p.x * p.y != p.z * p.t) return false;
|
if(p.x * p.y - p.z * p.t) return false;
|
||||||
if(p.y.square() - p.x.square() - p.z.square() - p.t.square() * D) return false;
|
if(square(p.y) - square(p.x) - square(p.z) - square(p.t) * D) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto decompress(uint256_t c) const -> maybe<point> {
|
inline auto decompress(uint256_t c) const -> maybe<point> {
|
||||||
field y = c & (1_u256 << 255) - 1;
|
field y = c & ~0_u256 >> 1;
|
||||||
field x = recoverX(y);
|
field x = squareRoot((square(y) - 1) * reciprocal(D * square(y) + 1));
|
||||||
if(c >> 255) x = -x;
|
if(c >> 255) x = -x;
|
||||||
point p{x, y, 1, x * y};
|
point p{x, y, 1, x * y};
|
||||||
if(!onCurve(p)) return nothing;
|
if(!onCurve(p)) return nothing;
|
||||||
|
@ -103,18 +96,18 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto compress(point p) const -> uint256_t {
|
inline auto compress(point p) const -> uint256_t {
|
||||||
field r = p.z.reciprocal();
|
field r = reciprocal(p.z);
|
||||||
field x = p.x * r;
|
field x = p.x * r;
|
||||||
field y = p.y * r;
|
field y = p.y * r;
|
||||||
return (x() & 1) << 255 | (y() & ((0_u256 - 1) >> 1));
|
return (x & 1) << 255 | (y & ~0_u256 >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto edwardsDouble(point p) const -> point {
|
inline auto edwardsDouble(point p) const -> point {
|
||||||
field a = p.x.square();
|
field a = square(p.x);
|
||||||
field b = p.y.square();
|
field b = square(p.y);
|
||||||
field c = p.z.square();
|
field c = square(p.z);
|
||||||
field d = -a;
|
field d = -a;
|
||||||
field e = (p.x + p.y).square() - a - b;
|
field e = square(p.x + p.y) - a - b;
|
||||||
field g = d + b;
|
field g = d + b;
|
||||||
field f = g - (c + c);
|
field f = g - (c + c);
|
||||||
field h = d - b;
|
field h = d - b;
|
||||||
|
@ -133,29 +126,16 @@ private:
|
||||||
return {e * f, g * h, f * g, e * h};
|
return {e * f, g * h, f * g, e * h};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto scalarMultiply(uint512_t e, point q) const -> point {
|
inline auto scalarMultiply(point q, uint256_t exponent) const -> point {
|
||||||
point p{0, 1, 1, 0}, c;
|
point p{0, 1, 1, 0}, c;
|
||||||
for(uint n : reverse(range(253))) {
|
for(uint bit : reverse(range(253))) {
|
||||||
p = edwardsDouble(p);
|
p = edwardsDouble(p);
|
||||||
c = edwardsAdd(p, q);
|
c = edwardsAdd(p, q);
|
||||||
bool bit = e >> n & 1;
|
bool condition = exponent >> bit & 1;
|
||||||
cmove(bit, p.x, c.x);
|
cmove(condition, p.x, c.x);
|
||||||
cmove(bit, p.y, c.y);
|
cmove(condition, p.y, c.y);
|
||||||
cmove(bit, p.z, c.z);
|
cmove(condition, p.z, c.z);
|
||||||
cmove(bit, p.t, c.t);
|
cmove(condition, p.t, c.t);
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto scalarMultiplyB(uint512_t e) const -> point {
|
|
||||||
point p{0, 1, 1, 0}, c;
|
|
||||||
for(uint n : reverse(range(253))) {
|
|
||||||
bool bit = e >> n & 1;
|
|
||||||
c = edwardsAdd(p, Bscalar[n]);
|
|
||||||
cmove(bit, p.x, c.x);
|
|
||||||
cmove(bit, p.y, c.y);
|
|
||||||
cmove(bit, p.z, c.z);
|
|
||||||
cmove(bit, p.t, c.t);
|
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/arithmetic/barrett.hpp>
|
||||||
|
|
||||||
|
namespace nall { namespace EllipticCurve {
|
||||||
|
|
||||||
|
static const uint256_t P = (1_u256 << 255) - 19;
|
||||||
|
|
||||||
|
#define Mask ((1ull << 51) - 1)
|
||||||
|
|
||||||
|
struct Modulo25519 {
|
||||||
|
inline Modulo25519() = default;
|
||||||
|
inline Modulo25519(const Modulo25519&) = default;
|
||||||
|
inline Modulo25519(uint64_t a, uint64_t b = 0, uint64_t c = 0, uint64_t d = 0, uint64_t e = 0) : l{a, b, c, d, e} {}
|
||||||
|
inline Modulo25519(uint256_t n);
|
||||||
|
|
||||||
|
inline explicit operator bool() const { return (bool)operator()(); }
|
||||||
|
inline auto operator[](uint index) -> uint64_t& { return l[index]; }
|
||||||
|
inline auto operator[](uint index) const -> uint64_t { return l[index]; }
|
||||||
|
inline auto operator()() const -> uint256_t;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t l[5]; //51-bits per limb; 255-bits total
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Modulo25519::Modulo25519(uint256_t n) {
|
||||||
|
l[0] = n >> 0 & Mask;
|
||||||
|
l[1] = n >> 51 & Mask;
|
||||||
|
l[2] = n >> 102 & Mask;
|
||||||
|
l[3] = n >> 153 & Mask;
|
||||||
|
l[4] = n >> 204 & Mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Modulo25519::operator()() const -> uint256_t {
|
||||||
|
Modulo25519 o = *this;
|
||||||
|
|
||||||
|
o[1] += (o[0] >> 51); o[0] &= Mask;
|
||||||
|
o[2] += (o[1] >> 51); o[1] &= Mask;
|
||||||
|
o[3] += (o[2] >> 51); o[2] &= Mask;
|
||||||
|
o[4] += (o[3] >> 51); o[3] &= Mask;
|
||||||
|
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
|
||||||
|
|
||||||
|
o[1] += (o[0] >> 51); o[0] &= Mask;
|
||||||
|
o[2] += (o[1] >> 51); o[1] &= Mask;
|
||||||
|
o[3] += (o[2] >> 51); o[2] &= Mask;
|
||||||
|
o[4] += (o[3] >> 51); o[3] &= Mask;
|
||||||
|
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
|
||||||
|
|
||||||
|
o[0] += 19;
|
||||||
|
o[1] += (o[0] >> 51); o[0] &= Mask;
|
||||||
|
o[2] += (o[1] >> 51); o[1] &= Mask;
|
||||||
|
o[3] += (o[2] >> 51); o[2] &= Mask;
|
||||||
|
o[4] += (o[3] >> 51); o[3] &= Mask;
|
||||||
|
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
|
||||||
|
|
||||||
|
o[0] += Mask - 18;
|
||||||
|
o[1] += Mask;
|
||||||
|
o[2] += Mask;
|
||||||
|
o[3] += Mask;
|
||||||
|
o[4] += Mask;
|
||||||
|
|
||||||
|
o[1] += o[0] >> 51; o[0] &= Mask;
|
||||||
|
o[2] += o[1] >> 51; o[1] &= Mask;
|
||||||
|
o[3] += o[2] >> 51; o[2] &= Mask;
|
||||||
|
o[4] += o[3] >> 51; o[3] &= Mask;
|
||||||
|
o[4] &= Mask;
|
||||||
|
|
||||||
|
return (uint256_t)o[0] << 0 | (uint256_t)o[1] << 51 | (uint256_t)o[2] << 102 | (uint256_t)o[3] << 153 | (uint256_t)o[4] << 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto cmove(bool move, Modulo25519& l, const Modulo25519& r) -> void {
|
||||||
|
uint64_t mask = -move;
|
||||||
|
l[0] ^= mask & (l[0] ^ r[0]);
|
||||||
|
l[1] ^= mask & (l[1] ^ r[1]);
|
||||||
|
l[2] ^= mask & (l[2] ^ r[2]);
|
||||||
|
l[3] ^= mask & (l[3] ^ r[3]);
|
||||||
|
l[4] ^= mask & (l[4] ^ r[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto cswap(bool swap, Modulo25519& l, Modulo25519& r) -> void {
|
||||||
|
uint64_t mask = -swap, x;
|
||||||
|
x = mask & (l[0] ^ r[0]); l[0] ^= x; r[0] ^= x;
|
||||||
|
x = mask & (l[1] ^ r[1]); l[1] ^= x; r[1] ^= x;
|
||||||
|
x = mask & (l[2] ^ r[2]); l[2] ^= x; r[2] ^= x;
|
||||||
|
x = mask & (l[3] ^ r[3]); l[3] ^= x; r[3] ^= x;
|
||||||
|
x = mask & (l[4] ^ r[4]); l[4] ^= x; r[4] ^= x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator-(const Modulo25519& l) -> Modulo25519 { //P - l
|
||||||
|
Modulo25519 o;
|
||||||
|
uint64_t c;
|
||||||
|
o[0] = 0xfffffffffffda - l[0]; c = o[0] >> 51; o[0] &= Mask;
|
||||||
|
o[1] = 0xffffffffffffe - l[1] + c; c = o[1] >> 51; o[1] &= Mask;
|
||||||
|
o[2] = 0xffffffffffffe - l[2] + c; c = o[2] >> 51; o[2] &= Mask;
|
||||||
|
o[3] = 0xffffffffffffe - l[3] + c; c = o[3] >> 51; o[3] &= Mask;
|
||||||
|
o[4] = 0xffffffffffffe - l[4] + c; c = o[4] >> 51; o[4] &= Mask;
|
||||||
|
o[0] += c * 19;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator+(const Modulo25519& l, const Modulo25519& r) -> Modulo25519 {
|
||||||
|
Modulo25519 o;
|
||||||
|
uint64_t c;
|
||||||
|
o[0] = l[0] + r[0]; c = o[0] >> 51; o[0] &= Mask;
|
||||||
|
o[1] = l[1] + r[1] + c; c = o[1] >> 51; o[1] &= Mask;
|
||||||
|
o[2] = l[2] + r[2] + c; c = o[2] >> 51; o[2] &= Mask;
|
||||||
|
o[3] = l[3] + r[3] + c; c = o[3] >> 51; o[3] &= Mask;
|
||||||
|
o[4] = l[4] + r[4] + c; c = o[4] >> 51; o[4] &= Mask;
|
||||||
|
o[0] += c * 19;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator-(const Modulo25519& l, const Modulo25519& r) -> Modulo25519 {
|
||||||
|
Modulo25519 o;
|
||||||
|
uint64_t c;
|
||||||
|
o[0] = l[0] + 0x1fffffffffffb4 - r[0]; c = o[0] >> 51; o[0] &= Mask;
|
||||||
|
o[1] = l[1] + 0x1ffffffffffffc - r[1] + c; c = o[1] >> 51; o[1] &= Mask;
|
||||||
|
o[2] = l[2] + 0x1ffffffffffffc - r[2] + c; c = o[2] >> 51; o[2] &= Mask;
|
||||||
|
o[3] = l[3] + 0x1ffffffffffffc - r[3] + c; c = o[3] >> 51; o[3] &= Mask;
|
||||||
|
o[4] = l[4] + 0x1ffffffffffffc - r[4] + c; c = o[4] >> 51; o[4] &= Mask;
|
||||||
|
o[0] += c * 19;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator*(const Modulo25519& l, uint64_t scalar) -> Modulo25519 {
|
||||||
|
Modulo25519 o;
|
||||||
|
uint128_t a;
|
||||||
|
a = (uint128_t)l[0] * scalar; o[0] = a & Mask;
|
||||||
|
a = (uint128_t)l[1] * scalar + (a >> 51 & Mask); o[1] = a & Mask;
|
||||||
|
a = (uint128_t)l[2] * scalar + (a >> 51 & Mask); o[2] = a & Mask;
|
||||||
|
a = (uint128_t)l[3] * scalar + (a >> 51 & Mask); o[3] = a & Mask;
|
||||||
|
a = (uint128_t)l[4] * scalar + (a >> 51 & Mask); o[4] = a & Mask;
|
||||||
|
o[0] += (a >> 51) * 19;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator*(const Modulo25519& l, Modulo25519 r) -> Modulo25519 {
|
||||||
|
uint128_t t[] = {
|
||||||
|
(uint128_t)r[0] * l[0],
|
||||||
|
(uint128_t)r[0] * l[1] + (uint128_t)r[1] * l[0],
|
||||||
|
(uint128_t)r[0] * l[2] + (uint128_t)r[1] * l[1] + (uint128_t)r[2] * l[0],
|
||||||
|
(uint128_t)r[0] * l[3] + (uint128_t)r[1] * l[2] + (uint128_t)r[2] * l[1] + (uint128_t)r[3] * l[0],
|
||||||
|
(uint128_t)r[0] * l[4] + (uint128_t)r[1] * l[3] + (uint128_t)r[2] * l[2] + (uint128_t)r[3] * l[1] + (uint128_t)r[4] * l[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
r[1] *= 19, r[2] *= 19, r[3] *= 19, r[4] *= 19;
|
||||||
|
|
||||||
|
t[0] += (uint128_t)r[4] * l[1] + (uint128_t)r[3] * l[2] + (uint128_t)r[2] * l[3] + (uint128_t)r[1] * l[4];
|
||||||
|
t[1] += (uint128_t)r[4] * l[2] + (uint128_t)r[3] * l[3] + (uint128_t)r[2] * l[4];
|
||||||
|
t[2] += (uint128_t)r[4] * l[3] + (uint128_t)r[3] * l[4];
|
||||||
|
t[3] += (uint128_t)r[4] * l[4];
|
||||||
|
|
||||||
|
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
|
||||||
|
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
|
||||||
|
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
|
||||||
|
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
|
||||||
|
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
|
||||||
|
|
||||||
|
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
|
||||||
|
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
|
||||||
|
r[2] += c;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator&(const Modulo25519& lhs, uint256_t rhs) -> uint256_t {
|
||||||
|
return lhs() & rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto square(const Modulo25519& lhs) -> Modulo25519 {
|
||||||
|
Modulo25519 r{lhs};
|
||||||
|
Modulo25519 d{r[0] * 2, r[1] * 2, r[2] * 2 * 19, r[4] * 19, r[4] * 19 * 2};
|
||||||
|
|
||||||
|
uint128_t t[5];
|
||||||
|
t[0] = (uint128_t)r[0] * r[0] + (uint128_t)d[4] * r[1] + (uint128_t)d[2] * r[3];
|
||||||
|
t[1] = (uint128_t)d[0] * r[1] + (uint128_t)d[4] * r[2] + (uint128_t)r[3] * r[3] * 19;
|
||||||
|
t[2] = (uint128_t)d[0] * r[2] + (uint128_t)r[1] * r[1] + (uint128_t)d[4] * r[3];
|
||||||
|
t[3] = (uint128_t)d[0] * r[3] + (uint128_t)d[1] * r[2] + (uint128_t)r[4] * d[3];
|
||||||
|
t[4] = (uint128_t)d[0] * r[4] + (uint128_t)d[1] * r[3] + (uint128_t)r[2] * r[2];
|
||||||
|
|
||||||
|
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
|
||||||
|
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
|
||||||
|
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
|
||||||
|
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
|
||||||
|
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
|
||||||
|
|
||||||
|
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
|
||||||
|
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
|
||||||
|
r[2] += c;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto exponentiate(const Modulo25519& lhs, uint256_t exponent) -> Modulo25519 {
|
||||||
|
Modulo25519 x = 1, y;
|
||||||
|
for(uint bit : reverse(range(256))) {
|
||||||
|
x = square(x);
|
||||||
|
y = x * lhs;
|
||||||
|
cmove(exponent >> bit & 1, x, y);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto reciprocal(const Modulo25519& lhs) -> Modulo25519 {
|
||||||
|
return exponentiate(lhs, P - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto squareRoot(const Modulo25519& lhs) -> Modulo25519 {
|
||||||
|
static const Modulo25519 I = exponentiate(Modulo25519(2), P - 1 >> 2); //I == sqrt(-1)
|
||||||
|
Modulo25519 x = exponentiate(lhs, P + 3 >> 3);
|
||||||
|
Modulo25519 y = x * I;
|
||||||
|
cmove(bool(square(x) - lhs), x, y);
|
||||||
|
y = -x;
|
||||||
|
cmove(x & 1, x, y);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef Mask
|
||||||
|
|
||||||
|
}}
|
|
@ -1,79 +1,84 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
//warning: this implementation leaks side-channel information
|
||||||
|
//use modulo25519-optimized.hpp in production
|
||||||
|
|
||||||
#include <nall/arithmetic/barrett.hpp>
|
#include <nall/arithmetic/barrett.hpp>
|
||||||
|
|
||||||
namespace nall { namespace EllipticCurve {
|
namespace nall { namespace EllipticCurve {
|
||||||
|
|
||||||
static const uint256_t P = (1_u256 << 255) - 19;
|
static const uint256_t P = (1_u256 << 255) - 19;
|
||||||
static const uint256_t L = (1_u256 << 252) + 27742317777372353535851937790883648493_u256;
|
|
||||||
|
|
||||||
static BarrettReduction modP{P};
|
struct Modulo25519 {
|
||||||
static BarrettReduction modL{L};
|
inline Modulo25519() = default;
|
||||||
|
inline Modulo25519(const Modulo25519& source) : value(source.value) {}
|
||||||
|
template<typename T> inline Modulo25519(const T& value) : value(value) {}
|
||||||
|
inline explicit operator bool() const { return (bool)value; }
|
||||||
|
inline auto operator()() const -> uint256_t { return value; }
|
||||||
|
|
||||||
struct Modulo25519 : uint256_t {
|
private:
|
||||||
using type = Modulo25519;
|
uint256_t value;
|
||||||
using uint256_t::uint256_t;
|
|
||||||
|
|
||||||
alwaysinline auto operator()() const -> uint256_t {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
alwaysinline auto operator-() const -> type {
|
|
||||||
return P.operator-(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> alwaysinline auto operator+(const T& rhs) const -> type {
|
|
||||||
auto lhs = (uint512_t)*this + rhs;
|
|
||||||
if(lhs >= P) lhs -= P;
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> alwaysinline auto operator-(const T& rhs) const -> type {
|
|
||||||
auto lhs = (uint512_t)*this;
|
|
||||||
if(lhs < rhs) lhs += P;
|
|
||||||
return lhs - rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> alwaysinline auto operator*(const T& rhs) const -> type {
|
|
||||||
uint256_t hi, lo;
|
|
||||||
nall::mul(*this, rhs, hi, lo);
|
|
||||||
return modP(uint512_t{hi, lo});
|
|
||||||
}
|
|
||||||
|
|
||||||
alwaysinline auto square() const -> type {
|
|
||||||
uint256_t hi, lo;
|
|
||||||
nall::square(*this, hi, lo);
|
|
||||||
return modP(uint512_t{hi, lo});
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto expmod(uint256_t e) const -> type {
|
|
||||||
type x = 1;
|
|
||||||
for(auto n : rrange(256)) {
|
|
||||||
x = x.square();
|
|
||||||
if(e >> n & 1) x = operator*(x);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto reciprocal() const -> type {
|
|
||||||
return expmod(P - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto squareRoot() const -> type {
|
|
||||||
static const type i = type(2).expmod((P - 1) >> 2); //i = sqrt(-1)
|
|
||||||
type x = expmod((P + 3) >> 3);
|
|
||||||
if(operator!=(x.square())) x = x * i;
|
|
||||||
if(x & 1) x = -x;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline auto cmove(bool bit, Modulo25519& lhs, const Modulo25519& rhs) -> void {
|
inline auto operator-(const Modulo25519& lhs) -> Modulo25519 {
|
||||||
if(bit) lhs = rhs;
|
return P - lhs();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto cswap(bool bit, Modulo25519& lhs, Modulo25519& rhs) -> void {
|
inline auto operator+(const Modulo25519& lhs, const Modulo25519& rhs) -> Modulo25519 {
|
||||||
if(bit) swap(lhs, rhs);
|
uint512_t value = (uint512_t)lhs() + rhs();
|
||||||
|
if(value >= P) value -= P;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator-(const Modulo25519& lhs, const Modulo25519& rhs) -> Modulo25519 {
|
||||||
|
uint512_t value = (uint512_t)lhs();
|
||||||
|
if(value < rhs()) value += P;
|
||||||
|
return uint256_t(value - rhs());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator*(const Modulo25519& lhs, const Modulo25519& rhs) -> Modulo25519 {
|
||||||
|
static const BarrettReduction<256> P{EllipticCurve::P};
|
||||||
|
uint256_t hi, lo;
|
||||||
|
mul(lhs(), rhs(), hi, lo);
|
||||||
|
return uint512_t{hi, lo} % P;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto operator&(const Modulo25519& lhs, uint256_t rhs) -> uint256_t {
|
||||||
|
return lhs() & rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto square(const Modulo25519& lhs) -> Modulo25519 {
|
||||||
|
static const BarrettReduction<256> P{EllipticCurve::P};
|
||||||
|
uint256_t hi, lo;
|
||||||
|
square(lhs(), hi, lo);
|
||||||
|
return uint512_t{hi, lo} % P;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto exponentiate(const Modulo25519& lhs, uint256_t exponent) -> Modulo25519 {
|
||||||
|
if(exponent == 0) return 1;
|
||||||
|
Modulo25519 value = square(exponentiate(lhs, exponent >> 1));
|
||||||
|
if(exponent & 1) value = value * lhs;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto reciprocal(const Modulo25519& lhs) -> Modulo25519 {
|
||||||
|
return exponentiate(lhs, P - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto squareRoot(const Modulo25519& lhs) -> Modulo25519 {
|
||||||
|
static const Modulo25519 I = exponentiate(Modulo25519(2), P - 1 >> 2); //I = sqrt(-1)
|
||||||
|
Modulo25519 value = exponentiate(lhs, P + 3 >> 3);
|
||||||
|
if(square(value) - lhs) value = value * I;
|
||||||
|
if(value & 1) value = -value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto cmove(bool condition, Modulo25519& lhs, const Modulo25519& rhs) -> void {
|
||||||
|
if(condition) lhs = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto cswap(bool condition, Modulo25519& lhs, Modulo25519& rhs) -> void {
|
||||||
|
if(condition) swap(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,234 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <nall/arithmetic/barrett.hpp>
|
|
||||||
|
|
||||||
namespace nall { namespace EllipticCurve {
|
|
||||||
|
|
||||||
static const uint256_t P = (1_u256 << 255) - 19;
|
|
||||||
static const uint256_t L = (1_u256 << 252) + 27742317777372353535851937790883648493_u256;
|
|
||||||
|
|
||||||
static BarrettReduction modP{P};
|
|
||||||
static BarrettReduction modL{L};
|
|
||||||
|
|
||||||
struct Modulo25519;
|
|
||||||
auto cmove(bool move, Modulo25519& l, const Modulo25519& r) -> void;
|
|
||||||
auto cswap(bool swap, Modulo25519& l, Modulo25519& r) -> void;
|
|
||||||
|
|
||||||
struct Modulo25519 {
|
|
||||||
using type = Modulo25519;
|
|
||||||
#define Mask ((1ull << 51) - 1)
|
|
||||||
|
|
||||||
inline Modulo25519() = default;
|
|
||||||
inline Modulo25519(const Modulo25519&) = default;
|
|
||||||
inline Modulo25519(uint64_t a, uint64_t b = 0, uint64_t c = 0, uint64_t d = 0, uint64_t e = 0) : l{a, b, c, d, e} {}
|
|
||||||
|
|
||||||
inline Modulo25519(uint256_t n) {
|
|
||||||
l[0] = n >> 0 & Mask;
|
|
||||||
l[1] = n >> 51 & Mask;
|
|
||||||
l[2] = n >> 102 & Mask;
|
|
||||||
l[3] = n >> 153 & Mask;
|
|
||||||
l[4] = n >> 204 & Mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto operator()() const -> uint256_t { return operator uint256_t(); }
|
|
||||||
inline auto& operator[](uint index) { return l[index]; }
|
|
||||||
inline auto operator[](uint index) const { return l[index]; }
|
|
||||||
|
|
||||||
inline explicit operator bool() const {
|
|
||||||
return operator uint256_t();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline operator uint256_t() const {
|
|
||||||
type o = *this;
|
|
||||||
|
|
||||||
o[1] += (o[0] >> 51); o[0] &= Mask;
|
|
||||||
o[2] += (o[1] >> 51); o[1] &= Mask;
|
|
||||||
o[3] += (o[2] >> 51); o[2] &= Mask;
|
|
||||||
o[4] += (o[3] >> 51); o[3] &= Mask;
|
|
||||||
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
|
|
||||||
|
|
||||||
o[1] += (o[0] >> 51); o[0] &= Mask;
|
|
||||||
o[2] += (o[1] >> 51); o[1] &= Mask;
|
|
||||||
o[3] += (o[2] >> 51); o[2] &= Mask;
|
|
||||||
o[4] += (o[3] >> 51); o[3] &= Mask;
|
|
||||||
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
|
|
||||||
|
|
||||||
o[0] += 19;
|
|
||||||
o[1] += (o[0] >> 51); o[0] &= Mask;
|
|
||||||
o[2] += (o[1] >> 51); o[1] &= Mask;
|
|
||||||
o[3] += (o[2] >> 51); o[2] &= Mask;
|
|
||||||
o[4] += (o[3] >> 51); o[3] &= Mask;
|
|
||||||
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
|
|
||||||
|
|
||||||
o[0] += Mask - 18;
|
|
||||||
o[1] += Mask;
|
|
||||||
o[2] += Mask;
|
|
||||||
o[3] += Mask;
|
|
||||||
o[4] += Mask;
|
|
||||||
|
|
||||||
o[1] += o[0] >> 51; o[0] &= Mask;
|
|
||||||
o[2] += o[1] >> 51; o[1] &= Mask;
|
|
||||||
o[3] += o[2] >> 51; o[2] &= Mask;
|
|
||||||
o[4] += o[3] >> 51; o[3] &= Mask;
|
|
||||||
o[4] &= Mask;
|
|
||||||
|
|
||||||
return (uint256_t)o[0] << 0 | (uint256_t)o[1] << 51 | (uint256_t)o[2] << 102 | (uint256_t)o[3] << 153 | (uint256_t)o[4] << 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto operator!=(type r) const -> bool {
|
|
||||||
bool e = 1;
|
|
||||||
e &= l[0] == r[0];
|
|
||||||
e &= l[1] == r[1];
|
|
||||||
e &= l[2] == r[2];
|
|
||||||
e &= l[3] == r[3];
|
|
||||||
e &= l[4] == r[4];
|
|
||||||
return e == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto operator-() const -> type { //P - l
|
|
||||||
type o;
|
|
||||||
uint64_t c;
|
|
||||||
o[0] = 0xfffffffffffda - l[0]; c = o[0] >> 51; o[0] &= Mask;
|
|
||||||
o[1] = 0xffffffffffffe - l[1] + c; c = o[1] >> 51; o[1] &= Mask;
|
|
||||||
o[2] = 0xffffffffffffe - l[2] + c; c = o[2] >> 51; o[2] &= Mask;
|
|
||||||
o[3] = 0xffffffffffffe - l[3] + c; c = o[3] >> 51; o[3] &= Mask;
|
|
||||||
o[4] = 0xffffffffffffe - l[4] + c; c = o[4] >> 51; o[4] &= Mask;
|
|
||||||
o[0] += c * 19;
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto operator+(type r) const -> type {
|
|
||||||
type o;
|
|
||||||
uint64_t c;
|
|
||||||
o[0] = l[0] + r[0]; c = o[0] >> 51; o[0] &= Mask;
|
|
||||||
o[1] = l[1] + r[1] + c; c = o[1] >> 51; o[1] &= Mask;
|
|
||||||
o[2] = l[2] + r[2] + c; c = o[2] >> 51; o[2] &= Mask;
|
|
||||||
o[3] = l[3] + r[3] + c; c = o[3] >> 51; o[3] &= Mask;
|
|
||||||
o[4] = l[4] + r[4] + c; c = o[4] >> 51; o[4] &= Mask;
|
|
||||||
o[0] += c * 19;
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto operator-(type r) const -> type {
|
|
||||||
type o;
|
|
||||||
uint64_t c;
|
|
||||||
o[0] = l[0] + 0x1fffffffffffb4 - r[0]; c = o[0] >> 51; o[0] &= Mask;
|
|
||||||
o[1] = l[1] + 0x1ffffffffffffc - r[1] + c; c = o[1] >> 51; o[1] &= Mask;
|
|
||||||
o[2] = l[2] + 0x1ffffffffffffc - r[2] + c; c = o[2] >> 51; o[2] &= Mask;
|
|
||||||
o[3] = l[3] + 0x1ffffffffffffc - r[3] + c; c = o[3] >> 51; o[3] &= Mask;
|
|
||||||
o[4] = l[4] + 0x1ffffffffffffc - r[4] + c; c = o[4] >> 51; o[4] &= Mask;
|
|
||||||
o[0] += c * 19;
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto operator*(uint64_t scalar) const -> type {
|
|
||||||
type o;
|
|
||||||
uint128_t a;
|
|
||||||
a = (uint128_t)l[0] * scalar; o[0] = a & Mask;
|
|
||||||
a = (uint128_t)l[1] * scalar + (a >> 51 & Mask); o[1] = a & Mask;
|
|
||||||
a = (uint128_t)l[2] * scalar + (a >> 51 & Mask); o[2] = a & Mask;
|
|
||||||
a = (uint128_t)l[3] * scalar + (a >> 51 & Mask); o[3] = a & Mask;
|
|
||||||
a = (uint128_t)l[4] * scalar + (a >> 51 & Mask); o[4] = a & Mask;
|
|
||||||
o[0] += (a >> 51) * 19;
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto operator*(type r) const -> type {
|
|
||||||
uint128_t t[] = {
|
|
||||||
(uint128_t)r[0] * l[0],
|
|
||||||
(uint128_t)r[0] * l[1] + (uint128_t)r[1] * l[0],
|
|
||||||
(uint128_t)r[0] * l[2] + (uint128_t)r[1] * l[1] + (uint128_t)r[2] * l[0],
|
|
||||||
(uint128_t)r[0] * l[3] + (uint128_t)r[1] * l[2] + (uint128_t)r[2] * l[1] + (uint128_t)r[3] * l[0],
|
|
||||||
(uint128_t)r[0] * l[4] + (uint128_t)r[1] * l[3] + (uint128_t)r[2] * l[2] + (uint128_t)r[3] * l[1] + (uint128_t)r[4] * l[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
r[1] *= 19, r[2] *= 19, r[3] *= 19, r[4] *= 19;
|
|
||||||
|
|
||||||
t[0] += (uint128_t)r[4] * l[1] + (uint128_t)r[3] * l[2] + (uint128_t)r[2] * l[3] + (uint128_t)r[1] * l[4];
|
|
||||||
t[1] += (uint128_t)r[4] * l[2] + (uint128_t)r[3] * l[3] + (uint128_t)r[2] * l[4];
|
|
||||||
t[2] += (uint128_t)r[4] * l[3] + (uint128_t)r[3] * l[4];
|
|
||||||
t[3] += (uint128_t)r[4] * l[4];
|
|
||||||
|
|
||||||
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
|
|
||||||
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
|
|
||||||
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
|
|
||||||
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
|
|
||||||
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
|
|
||||||
|
|
||||||
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
|
|
||||||
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
|
|
||||||
r[2] += c;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto square() const -> type {
|
|
||||||
type r{*this};
|
|
||||||
type d{r[0] * 2, r[1] * 2, r[2] * 2 * 19, r[4] * 19, r[4] * 19 * 2};
|
|
||||||
|
|
||||||
uint128_t t[5];
|
|
||||||
t[0] = (uint128_t)r[0] * r[0] + (uint128_t)d[4] * r[1] + (uint128_t)d[2] * r[3];
|
|
||||||
t[1] = (uint128_t)d[0] * r[1] + (uint128_t)d[4] * r[2] + (uint128_t)r[3] * r[3] * 19;
|
|
||||||
t[2] = (uint128_t)d[0] * r[2] + (uint128_t)r[1] * r[1] + (uint128_t)d[4] * r[3];
|
|
||||||
t[3] = (uint128_t)d[0] * r[3] + (uint128_t)d[1] * r[2] + (uint128_t)r[4] * d[3];
|
|
||||||
t[4] = (uint128_t)d[0] * r[4] + (uint128_t)d[1] * r[3] + (uint128_t)r[2] * r[2];
|
|
||||||
|
|
||||||
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
|
|
||||||
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
|
|
||||||
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
|
|
||||||
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
|
|
||||||
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
|
|
||||||
|
|
||||||
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
|
|
||||||
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
|
|
||||||
r[2] += c;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto expmod(uint256_t e) const -> type {
|
|
||||||
type x = 1, y;
|
|
||||||
for(uint n : reverse(range(256))) {
|
|
||||||
x = x.square();
|
|
||||||
y = operator*(x);
|
|
||||||
cmove(e >> n & 1, x, y);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto reciprocal() const -> type {
|
|
||||||
return expmod(P - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto squareRoot() const -> type {
|
|
||||||
static const type i = type(2).expmod((P - 1) >> 2); //i == sqrt(-1)
|
|
||||||
type x = expmod((P + 3) >> 3);
|
|
||||||
type y = x * i;
|
|
||||||
cmove(operator!=(x.square()), x, y);
|
|
||||||
y = -x;
|
|
||||||
cmove(x() & 1, x, y);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint64_t l[5]; //51-bits per limb; 255-bits total
|
|
||||||
#undef Mask
|
|
||||||
};
|
|
||||||
|
|
||||||
inline auto cmove(bool move, Modulo25519& l, const Modulo25519& r) -> void {
|
|
||||||
uint64_t mask = -move;
|
|
||||||
l[0] ^= mask & (l[0] ^ r[0]);
|
|
||||||
l[1] ^= mask & (l[1] ^ r[1]);
|
|
||||||
l[2] ^= mask & (l[2] ^ r[2]);
|
|
||||||
l[3] ^= mask & (l[3] ^ r[3]);
|
|
||||||
l[4] ^= mask & (l[4] ^ r[4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto cswap(bool swap, Modulo25519& l, Modulo25519& r) -> void {
|
|
||||||
uint64_t mask = -swap, x;
|
|
||||||
x = mask & (l[0] ^ r[0]); l[0] ^= x; r[0] ^= x;
|
|
||||||
x = mask & (l[1] ^ r[1]); l[1] ^= x; r[1] ^= x;
|
|
||||||
x = mask & (l[2] ^ r[2]); l[2] ^= x; r[2] ^= x;
|
|
||||||
x = mask & (l[3] ^ r[3]); l[3] ^= x; r[3] ^= x;
|
|
||||||
x = mask & (l[4] ^ r[4]); l[4] ^= x; r[4] ^= x;
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
|
@ -17,7 +17,7 @@ using uint32 = Natural<32>;
|
||||||
using uint64 = Natural<64>;
|
using uint64 = Natural<64>;
|
||||||
|
|
||||||
struct FX {
|
struct FX {
|
||||||
auto open(vector<string> arguments) -> bool;
|
auto open(Arguments& arguments) -> bool;
|
||||||
auto close() -> void;
|
auto close() -> void;
|
||||||
auto readable() -> bool;
|
auto readable() -> bool;
|
||||||
auto read() -> uint8_t;
|
auto read() -> uint8_t;
|
||||||
|
@ -35,16 +35,10 @@ struct FX {
|
||||||
serial device;
|
serial device;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto FX::open(vector<string> arguments) -> bool {
|
auto FX::open(Arguments& arguments) -> bool {
|
||||||
//device name override support
|
//device name override support
|
||||||
string name;
|
string name;
|
||||||
for(auto argument : arguments) {
|
arguments.take("--device", name);
|
||||||
if(argument.beginsWith("--device=")) {
|
|
||||||
name = argument.trimLeft("--device=", 1L);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!device.open(name)) {
|
if(!device.open(name)) {
|
||||||
print("[21fx] error: unable to open hardware device\n");
|
print("[21fx] error: unable to open hardware device\n");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace nall { namespace Encode {
|
||||||
|
|
||||||
struct BMP {
|
struct BMP {
|
||||||
static auto create(const string& filename, const void* data, uint pitch, uint width, uint height, bool alpha) -> bool {
|
static auto create(const string& filename, const void* data, uint pitch, uint width, uint height, bool alpha) -> bool {
|
||||||
file fp{filename, file::mode::write};
|
auto fp = file::open(filename, file::mode::write);
|
||||||
if(!fp) return false;
|
if(!fp) return false;
|
||||||
|
|
||||||
uint bitsPerPixel = alpha ? 32 : 24;
|
uint bitsPerPixel = alpha ? 32 : 24;
|
||||||
|
|
|
@ -49,14 +49,14 @@ inline auto LZSA(array_view<uint8_t> input) -> vector<uint8_t> {
|
||||||
int length, offset;
|
int length, offset;
|
||||||
suffixArray.previous(length, offset, index);
|
suffixArray.previous(length, offset, index);
|
||||||
|
|
||||||
for(uint ahead = 1; ahead <= 2; ahead++) {
|
/* for(uint ahead = 1; ahead <= 2; ahead++) {
|
||||||
int aheadLength, aheadOffset;
|
int aheadLength, aheadOffset;
|
||||||
suffixArray.previous(aheadLength, aheadOffset, index + ahead);
|
suffixArray.previous(aheadLength, aheadOffset, index + ahead);
|
||||||
if(aheadLength > length && aheadOffset >= 0) {
|
if(aheadLength > length && aheadOffset >= 0) {
|
||||||
length = 0;
|
length = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
if(length < 6 || offset < 0) {
|
if(length < 6 || offset < 0) {
|
||||||
flagWrite(0);
|
flagWrite(0);
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct ZIP {
|
||||||
filename.transform("\\", "/");
|
filename.transform("\\", "/");
|
||||||
if(!timestamp) timestamp = this->timestamp;
|
if(!timestamp) timestamp = this->timestamp;
|
||||||
uint32_t checksum = Hash::CRC32({data, size}).digest().hex();
|
uint32_t checksum = Hash::CRC32({data, size}).digest().hex();
|
||||||
directory.append({filename, timestamp, checksum, size, fp.offset()});
|
directory.append({filename, timestamp, checksum, size, (uint32_t)fp.offset()});
|
||||||
|
|
||||||
fp.writel(0x04034b50, 4); //signature
|
fp.writel(0x04034b50, 4); //signature
|
||||||
fp.writel(0x0014, 2); //minimum version (2.0)
|
fp.writel(0x0014, 2); //minimum version (2.0)
|
||||||
|
@ -34,7 +34,7 @@ struct ZIP {
|
||||||
fp.writel(0x0000, 2); //extra field length
|
fp.writel(0x0000, 2); //extra field length
|
||||||
fp.print(filename); //file name
|
fp.print(filename); //file name
|
||||||
|
|
||||||
fp.write(data, size); //file data
|
fp.write({data, size}); //file data
|
||||||
}
|
}
|
||||||
|
|
||||||
~ZIP() {
|
~ZIP() {
|
||||||
|
@ -86,7 +86,7 @@ protected:
|
||||||
return ((info->tm_year - 80) << 9) | ((1 + info->tm_mon) << 5) + (info->tm_mday);
|
return ((info->tm_year - 80) << 9) | ((1 + info->tm_mon) << 5) + (info->tm_mday);
|
||||||
}
|
}
|
||||||
|
|
||||||
file fp;
|
file_buffer fp;
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
struct entry_t {
|
struct entry_t {
|
||||||
string filename;
|
string filename;
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/array-span.hpp>
|
||||||
|
#include <nall/array-view.hpp>
|
||||||
|
#include <nall/inode.hpp>
|
||||||
|
#include <nall/range.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/utility.hpp>
|
||||||
|
#include <nall/varint.hpp>
|
||||||
|
#include <nall/hash/sha256.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
//on Windows (at least for 7 and earlier), FILE* is not buffered
|
||||||
|
//thus, reading/writing one byte at a time will be dramatically slower
|
||||||
|
//on all other OSes, FILE* is buffered
|
||||||
|
//in order to ensure good performance, file_buffer implements its own buffer
|
||||||
|
//this speeds up Windows substantially, without harming performance elsewhere much
|
||||||
|
|
||||||
|
struct file_buffer {
|
||||||
|
struct mode { enum : uint { read, write, modify, append }; };
|
||||||
|
struct index { enum : uint { absolute, relative }; };
|
||||||
|
|
||||||
|
file_buffer(const file_buffer&) = delete;
|
||||||
|
auto operator=(const file_buffer&) -> file_buffer& = delete;
|
||||||
|
|
||||||
|
file_buffer() = default;
|
||||||
|
file_buffer(const string& filename, uint mode) { open(filename, mode); }
|
||||||
|
|
||||||
|
file_buffer(file_buffer&& source) { operator=(move(source)); }
|
||||||
|
|
||||||
|
~file_buffer() { close(); }
|
||||||
|
|
||||||
|
auto operator=(file_buffer&& source) -> file_buffer& {
|
||||||
|
buffer = source.buffer;
|
||||||
|
bufferOffset = source.bufferOffset;
|
||||||
|
bufferDirty = source.bufferDirty;
|
||||||
|
fileHandle = source.fileHandle;
|
||||||
|
fileOffset = source.fileOffset;
|
||||||
|
fileSize = source.fileSize;
|
||||||
|
fileMode = source.fileMode;
|
||||||
|
|
||||||
|
source.bufferOffset = -1;
|
||||||
|
source.bufferDirty = false;
|
||||||
|
source.fileHandle = nullptr;
|
||||||
|
source.fileOffset = 0;
|
||||||
|
source.fileSize = 0;
|
||||||
|
source.fileMode = mode::read;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return (bool)fileHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto read() -> uint8_t {
|
||||||
|
if(!fileHandle) return 0; //file not open
|
||||||
|
if(fileMode == mode::write) return 0; //reads not permitted
|
||||||
|
if(fileOffset >= fileSize) return 0; //cannot read past end of file
|
||||||
|
bufferSynchronize();
|
||||||
|
return buffer[fileOffset++ & buffer.size() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = uint64_t> auto readl(uint length = 1) -> T {
|
||||||
|
T data = 0;
|
||||||
|
for(uint n : range(length)) {
|
||||||
|
data |= (T)read() << n * 8;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = uint64_t> auto readm(uint length = 1) -> T {
|
||||||
|
T data = 0;
|
||||||
|
while(length--) {
|
||||||
|
data <<= 8;
|
||||||
|
data |= read();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reads(uint length) -> string {
|
||||||
|
string result;
|
||||||
|
result.resize(length);
|
||||||
|
for(auto& byte : result) byte = read();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto read(array_span<uint8_t> memory) -> void {
|
||||||
|
for(auto& byte : memory) byte = read();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto write(uint8_t data) -> void {
|
||||||
|
if(!fileHandle) return; //file not open
|
||||||
|
if(fileMode == mode::read) return; //writes not permitted
|
||||||
|
bufferSynchronize();
|
||||||
|
buffer[fileOffset++ & buffer.size() - 1] = data;
|
||||||
|
bufferDirty = true;
|
||||||
|
if(fileOffset > fileSize) fileSize = fileOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = uint64_t> auto writel(T data, uint length = 1) -> void {
|
||||||
|
while(length--) {
|
||||||
|
write(uint8_t(data));
|
||||||
|
data >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = uint64_t> auto writem(T data, uint length = 1) -> void {
|
||||||
|
for(uint n : reverse(range(length))) {
|
||||||
|
write(uint8_t(data >> n * 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto writes(const string& s) -> void {
|
||||||
|
for(auto& byte : s) write(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto write(array_view<uint8_t> memory) -> void {
|
||||||
|
for(auto& byte : memory) write(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> auto print(P&&... p) -> void {
|
||||||
|
string s{forward<P>(p)...};
|
||||||
|
for(auto& byte : s) write(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto flush() -> void {
|
||||||
|
bufferFlush();
|
||||||
|
fflush(fileHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto seek(int64_t offset, uint index_ = index::absolute) -> void {
|
||||||
|
if(!fileHandle) return;
|
||||||
|
bufferFlush();
|
||||||
|
|
||||||
|
int64_t seekOffset = fileOffset;
|
||||||
|
switch(index_) {
|
||||||
|
case index::absolute: seekOffset = offset; break;
|
||||||
|
case index::relative: seekOffset += offset; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(seekOffset < 0) seekOffset = 0; //cannot seek before start of file
|
||||||
|
if(seekOffset > fileSize) {
|
||||||
|
if(fileMode == mode::read) { //cannot seek past end of file
|
||||||
|
seekOffset = fileSize;
|
||||||
|
} else { //pad file to requested location
|
||||||
|
fileOffset = fileSize;
|
||||||
|
while(fileSize < seekOffset) write(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOffset = seekOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto offset() const -> uint64_t {
|
||||||
|
if(!fileHandle) return 0;
|
||||||
|
return fileOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size() const -> uint64_t {
|
||||||
|
if(!fileHandle) return 0;
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto truncate(uint64_t size) -> bool {
|
||||||
|
if(!fileHandle) return false;
|
||||||
|
#if defined(API_POSIX)
|
||||||
|
return ftruncate(fileno(fileHandle), size) == 0;
|
||||||
|
#elif defined(API_WINDOWS)
|
||||||
|
return _chsize(fileno(fileHandle), size) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() const -> bool {
|
||||||
|
if(!fileHandle) return true;
|
||||||
|
return fileOffset >= fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto open(const string& filename, uint mode_) -> bool {
|
||||||
|
close();
|
||||||
|
|
||||||
|
switch(fileMode = mode_) {
|
||||||
|
#if defined(API_POSIX)
|
||||||
|
case mode::read: fileHandle = fopen(filename, "rb" ); break;
|
||||||
|
case mode::write: fileHandle = fopen(filename, "wb+"); break; //need read permission for buffering
|
||||||
|
case mode::modify: fileHandle = fopen(filename, "rb+"); break;
|
||||||
|
case mode::append: fileHandle = fopen(filename, "wb+"); break;
|
||||||
|
#elif defined(API_WINDOWS)
|
||||||
|
case mode::read: fileHandle = _wfopen(utf16_t(filename), L"rb" ); break;
|
||||||
|
case mode::write: fileHandle = _wfopen(utf16_t(filename), L"wb+"); break;
|
||||||
|
case mode::modify: fileHandle = _wfopen(utf16_t(filename), L"rb+"); break;
|
||||||
|
case mode::append: fileHandle = _wfopen(utf16_t(filename), L"wb+"); break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if(!fileHandle) return false;
|
||||||
|
|
||||||
|
bufferOffset = -1;
|
||||||
|
fileOffset = 0;
|
||||||
|
fseek(fileHandle, 0, SEEK_END);
|
||||||
|
fileSize = ftell(fileHandle);
|
||||||
|
fseek(fileHandle, 0, SEEK_SET);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto close() -> void {
|
||||||
|
if(!fileHandle) return;
|
||||||
|
bufferFlush();
|
||||||
|
fclose(fileHandle);
|
||||||
|
fileHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
array<uint8_t[4096]> buffer;
|
||||||
|
int bufferOffset = -1;
|
||||||
|
bool bufferDirty = false;
|
||||||
|
FILE* fileHandle = nullptr;
|
||||||
|
uint64_t fileOffset = 0;
|
||||||
|
uint64_t fileSize = 0;
|
||||||
|
uint fileMode = mode::read;
|
||||||
|
|
||||||
|
auto bufferSynchronize() -> void {
|
||||||
|
if(!fileHandle) return;
|
||||||
|
if(bufferOffset == (fileOffset & ~(buffer.size() - 1))) return;
|
||||||
|
|
||||||
|
bufferFlush();
|
||||||
|
bufferOffset = fileOffset & ~(buffer.size() - 1);
|
||||||
|
fseek(fileHandle, bufferOffset, SEEK_SET);
|
||||||
|
uint64_t length = bufferOffset + buffer.size() <= fileSize ? buffer.size() : fileSize & buffer.size() - 1;
|
||||||
|
if(length) (void)fread(buffer.data(), 1, length, fileHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bufferFlush() -> void {
|
||||||
|
if(!fileHandle) return; //file not open
|
||||||
|
if(fileMode == mode::read) return; //buffer cannot be written to
|
||||||
|
if(bufferOffset < 0) return; //buffer unused
|
||||||
|
if(!bufferDirty) return; //buffer unmodified since read
|
||||||
|
|
||||||
|
fseek(fileHandle, bufferOffset, SEEK_SET);
|
||||||
|
uint64_t length = bufferOffset + buffer.size() <= fileSize ? buffer.size() : fileSize & buffer.size() - 1;
|
||||||
|
if(length) (void)fwrite(buffer.data(), 1, length, fileHandle);
|
||||||
|
bufferOffset = -1;
|
||||||
|
bufferDirty = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
#else
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct file_map {
|
||||||
|
struct mode { enum : uint { read, write, modify, append }; };
|
||||||
|
|
||||||
|
file_map(const file_map&) = delete;
|
||||||
|
auto operator=(const file_map&) = delete;
|
||||||
|
|
||||||
|
file_map() = default;
|
||||||
|
file_map(file_map&& source) { operator=(move(source)); }
|
||||||
|
file_map(const string& filename, uint mode) { open(filename, mode); }
|
||||||
|
|
||||||
|
~file_map() { close(); }
|
||||||
|
|
||||||
|
explicit operator bool() const { return _open; }
|
||||||
|
auto size() const -> uint64_t { return _size; }
|
||||||
|
auto data() -> uint8_t* { return _data; }
|
||||||
|
auto data() const -> const uint8_t* { return _data; }
|
||||||
|
|
||||||
|
//auto operator=(file_map&& source) -> file_map&;
|
||||||
|
//auto open(const string& filename, uint mode) -> bool;
|
||||||
|
//auto close() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _open = false;
|
||||||
|
uint8_t* _data = nullptr;
|
||||||
|
uint64_t _size = 0;
|
||||||
|
|
||||||
|
#if defined(API_WINDOWS)
|
||||||
|
|
||||||
|
HANDLE _file = INVALID_HANDLE_VALUE;
|
||||||
|
HANDLE _map = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto operator=(file_map&& source) -> file_map& {
|
||||||
|
_open = source._open;
|
||||||
|
_data = source._data;
|
||||||
|
_size = source._size;
|
||||||
|
_file = source._file;
|
||||||
|
_map = source._map;
|
||||||
|
|
||||||
|
source._open = false;
|
||||||
|
source._data = nullptr;
|
||||||
|
source._size = 0;
|
||||||
|
source._file = INVALID_HANDLE_VALUE;
|
||||||
|
source._map = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto open(const string& filename, uint mode_) -> bool {
|
||||||
|
int desiredAccess, creationDisposition, protection, mapAccess;
|
||||||
|
|
||||||
|
switch(mode_) {
|
||||||
|
default: return false;
|
||||||
|
case mode::read:
|
||||||
|
desiredAccess = GENERIC_READ;
|
||||||
|
creationDisposition = OPEN_EXISTING;
|
||||||
|
protection = PAGE_READONLY;
|
||||||
|
mapAccess = FILE_MAP_READ;
|
||||||
|
break;
|
||||||
|
case mode::write:
|
||||||
|
//write access requires read access
|
||||||
|
desiredAccess = GENERIC_WRITE;
|
||||||
|
creationDisposition = CREATE_ALWAYS;
|
||||||
|
protection = PAGE_READWRITE;
|
||||||
|
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||||
|
break;
|
||||||
|
case mode::modify:
|
||||||
|
desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||||
|
creationDisposition = OPEN_EXISTING;
|
||||||
|
protection = PAGE_READWRITE;
|
||||||
|
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||||
|
break;
|
||||||
|
case mode::append:
|
||||||
|
desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||||
|
creationDisposition = CREATE_NEW;
|
||||||
|
protection = PAGE_READWRITE;
|
||||||
|
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_file = CreateFileW(utf16_t(filename), desiredAccess, FILE_SHARE_READ, nullptr,
|
||||||
|
creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if(_file == INVALID_HANDLE_VALUE) return false;
|
||||||
|
|
||||||
|
_size = GetFileSize(_file, nullptr);
|
||||||
|
|
||||||
|
_map = CreateFileMapping(_file, nullptr, protection, 0, _size, nullptr);
|
||||||
|
if(_map == INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(_file);
|
||||||
|
_file = INVALID_HANDLE_VALUE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data = (uint8_t*)MapViewOfFile(_map, mapAccess, 0, 0, _size);
|
||||||
|
return _open = _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto close() -> void {
|
||||||
|
if(_data) {
|
||||||
|
UnmapViewOfFile(_data);
|
||||||
|
_data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_map != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(_map);
|
||||||
|
_map = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_file != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(_file);
|
||||||
|
_file = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int _fd = -1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto operator=(file_map&& source) -> file_map& {
|
||||||
|
_open = source._open;
|
||||||
|
_data = source._data;
|
||||||
|
_size = source._size;
|
||||||
|
_fd = source._fd;
|
||||||
|
|
||||||
|
source._open = false;
|
||||||
|
source._data = nullptr;
|
||||||
|
source._size = 0;
|
||||||
|
source._fd = -1;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto open(const string& filename, uint mode_) -> bool {
|
||||||
|
close();
|
||||||
|
int openFlags = 0;
|
||||||
|
int mmapFlags = 0;
|
||||||
|
|
||||||
|
switch(mode_) {
|
||||||
|
default: return false;
|
||||||
|
case mode::read:
|
||||||
|
openFlags = O_RDONLY;
|
||||||
|
mmapFlags = PROT_READ;
|
||||||
|
break;
|
||||||
|
case mode::write:
|
||||||
|
openFlags = O_RDWR | O_CREAT; //mmap() requires read access
|
||||||
|
mmapFlags = PROT_WRITE;
|
||||||
|
break;
|
||||||
|
case mode::modify:
|
||||||
|
openFlags = O_RDWR;
|
||||||
|
mmapFlags = PROT_READ | PROT_WRITE;
|
||||||
|
break;
|
||||||
|
case mode::append:
|
||||||
|
openFlags = O_RDWR | O_CREAT;
|
||||||
|
mmapFlags = PROT_READ | PROT_WRITE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fd = ::open(filename, openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||||
|
if(_fd < 0) return false;
|
||||||
|
|
||||||
|
struct stat _stat;
|
||||||
|
fstat(_fd, &_stat);
|
||||||
|
_size = _stat.st_size;
|
||||||
|
|
||||||
|
_data = (uint8_t*)mmap(nullptr, _size, mmapFlags, MAP_SHARED | MAP_NORESERVE, _fd, 0);
|
||||||
|
if(_data == MAP_FAILED) {
|
||||||
|
_data = nullptr;
|
||||||
|
::close(_fd);
|
||||||
|
_fd = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _open = _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto close() -> void {
|
||||||
|
if(_data) {
|
||||||
|
munmap(_data, _size);
|
||||||
|
_data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_fd >= 0) {
|
||||||
|
::close(_fd);
|
||||||
|
_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
297
nall/file.hpp
297
nall/file.hpp
|
@ -1,26 +1,28 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/file-buffer.hpp>
|
||||||
#include <nall/inode.hpp>
|
|
||||||
#include <nall/stdint.hpp>
|
|
||||||
#include <nall/string.hpp>
|
|
||||||
#include <nall/utility.hpp>
|
|
||||||
#include <nall/varint.hpp>
|
|
||||||
#include <nall/hash/sha256.hpp>
|
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
struct file : inode, varint {
|
struct file : inode {
|
||||||
enum class mode : uint { read, write, modify, append, readwrite = modify, writeread = append };
|
struct mode { enum : uint { read, write, modify, append }; };
|
||||||
enum class index : uint { absolute, relative };
|
struct index { enum : uint { absolute, relative }; };
|
||||||
|
|
||||||
|
file() = delete;
|
||||||
|
|
||||||
|
static auto open(const string& filename, uint mode) -> file_buffer {
|
||||||
|
return file_buffer{filename, mode};
|
||||||
|
}
|
||||||
|
|
||||||
static auto copy(const string& sourcename, const string& targetname) -> bool {
|
static auto copy(const string& sourcename, const string& targetname) -> bool {
|
||||||
if(sourcename == targetname) return true;
|
if(sourcename == targetname) return true;
|
||||||
file rd, wr;
|
if(auto reader = file::open(sourcename, mode::read)) {
|
||||||
if(rd.open(sourcename, mode::read) == false) return false;
|
if(auto writer = file::open(targetname, mode::write)) {
|
||||||
if(wr.open(targetname, mode::write) == false) return false;
|
for(uint64_t n : range(reader.size())) writer.write(reader.read());
|
||||||
for(uint n = 0; n < rd.size(); n++) wr.write(rd.read());
|
return true;
|
||||||
return true;
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//attempt to rename file first
|
//attempt to rename file first
|
||||||
|
@ -29,14 +31,11 @@ struct file : inode, varint {
|
||||||
if(sourcename == targetname) return true;
|
if(sourcename == targetname) return true;
|
||||||
if(rename(sourcename, targetname)) return true;
|
if(rename(sourcename, targetname)) return true;
|
||||||
if(!writable(sourcename)) return false;
|
if(!writable(sourcename)) return false;
|
||||||
if(copy(sourcename, targetname)) {
|
if(copy(sourcename, targetname)) return remove(sourcename), true;
|
||||||
remove(sourcename);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto truncate(const string& filename, uint size) -> bool {
|
static auto truncate(const string& filename, uint64_t size) -> bool {
|
||||||
#if defined(API_POSIX)
|
#if defined(API_POSIX)
|
||||||
return truncate(filename, size) == 0;
|
return truncate(filename, size) == 0;
|
||||||
#elif defined(API_WINDOWS)
|
#elif defined(API_WINDOWS)
|
||||||
|
@ -61,7 +60,7 @@ struct file : inode, varint {
|
||||||
return !(data.st_mode & S_IFDIR);
|
return !(data.st_mode & S_IFDIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto size(const string& filename) -> uintmax {
|
static auto size(const string& filename) -> uint64_t {
|
||||||
#if defined(API_POSIX)
|
#if defined(API_POSIX)
|
||||||
struct stat data;
|
struct stat data;
|
||||||
stat(filename, &data);
|
stat(filename, &data);
|
||||||
|
@ -72,263 +71,33 @@ struct file : inode, varint {
|
||||||
return S_ISREG(data.st_mode) ? data.st_size : 0u;
|
return S_ISREG(data.st_mode) ? data.st_size : 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto read(const string& filename, uint reserve = 0) -> vector<uint8_t> {
|
static auto read(const string& filename) -> vector<uint8_t> {
|
||||||
vector<uint8_t> memory;
|
vector<uint8_t> memory;
|
||||||
file fp;
|
if(auto fp = file::open(filename, mode::read)) {
|
||||||
if(fp.open(filename, mode::read)) {
|
|
||||||
memory.reserve(fp.size() + reserve);
|
|
||||||
memory.resize(fp.size());
|
memory.resize(fp.size());
|
||||||
fp.read(memory.data(), memory.size());
|
fp.read(memory);
|
||||||
}
|
}
|
||||||
return memory;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto read(const string& filename, void* data, uint size) -> bool {
|
static auto read(const string& filename, array_span<uint8_t> memory) -> bool {
|
||||||
file fp;
|
if(auto fp = file::open(filename, mode::read)) return fp.read(memory), true;
|
||||||
if(fp.open(filename, mode::read) == false) return false;
|
return false;
|
||||||
fp.read(data, size);
|
|
||||||
fp.close();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto write(const string& filename, const string& text) -> bool {
|
static auto write(const string& filename, array_view<uint8_t> memory) -> bool {
|
||||||
return write(filename, text.data(), text.size());
|
if(auto fp = file::open(filename, mode::write)) return fp.write(memory), true;
|
||||||
}
|
return false;
|
||||||
|
|
||||||
static auto write(const string& filename, const vector<uint8_t>& buffer) -> bool {
|
|
||||||
return write(filename, buffer.data(), buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto write(const string& filename, const void* data, uint size) -> bool {
|
|
||||||
file fp;
|
|
||||||
if(fp.open(filename, mode::write) == false) return false;
|
|
||||||
fp.write(data, size);
|
|
||||||
fp.close();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//create an empty file (will replace existing files)
|
||||||
static auto create(const string& filename) -> bool {
|
static auto create(const string& filename) -> bool {
|
||||||
//create an empty file (will replace existing files)
|
if(auto fp = file::open(filename, mode::write)) return true;
|
||||||
file fp;
|
return false;
|
||||||
if(fp.open(filename, mode::write) == false) return false;
|
|
||||||
fp.close();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto sha256(const string& filename) -> string {
|
static auto sha256(const string& filename) -> string {
|
||||||
auto buffer = read(filename);
|
return Hash::SHA256(read(filename)).digest();
|
||||||
return Hash::SHA256(buffer).digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto read() -> uint8_t {
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
auto readl(uint length = 1) -> uintmax {
|
|
||||||
uintmax data = 0;
|
|
||||||
for(int i = 0; i < length; i++) {
|
|
||||||
data |= (uintmax)read() << (i << 3);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto readm(uint length = 1) -> uintmax {
|
|
||||||
uintmax data = 0;
|
|
||||||
while(length--) {
|
|
||||||
data <<= 8;
|
|
||||||
data |= read();
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reads(uint length) -> string {
|
|
||||||
string result;
|
|
||||||
result.resize(length);
|
|
||||||
for(auto& byte : result) byte = read();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto read(void* data, uint size) -> void {
|
|
||||||
auto output = (uint8_t*)data;
|
|
||||||
while(size--) *output++ = read();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto write(uint8_t data) -> void {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto writel(uintmax data, uint length = 1) -> void {
|
|
||||||
while(length--) {
|
|
||||||
write(data);
|
|
||||||
data >>= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto writem(uintmax data, uint length = 1) -> void {
|
|
||||||
for(int i = length - 1; i >= 0; i--) {
|
|
||||||
write(data >> (i << 3));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto writes(const string& s) -> void {
|
|
||||||
for(auto byte : s) write(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto write(const void* data, uint size) -> void {
|
|
||||||
auto input = (const uint8_t*)data;
|
|
||||||
while(size--) write(*input++);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args> auto print(Args... args) -> void {
|
|
||||||
string data(args...);
|
|
||||||
const char* p = data;
|
|
||||||
while(*p) write(*p++);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto flush() -> void {
|
|
||||||
buffer_flush();
|
|
||||||
fflush(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto seek(int offset, index index_ = index::absolute) -> void {
|
|
||||||
if(!fp) return; //file not open
|
|
||||||
buffer_flush();
|
|
||||||
|
|
||||||
intmax 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto offset() const -> uint {
|
|
||||||
if(!fp) return 0; //file not open
|
|
||||||
return file_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto size() const -> uint {
|
|
||||||
if(!fp) return 0; //file not open
|
|
||||||
return file_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto truncate(uint size) -> bool {
|
|
||||||
if(!fp) return false; //file not open
|
|
||||||
#if defined(API_POSIX)
|
|
||||||
return ftruncate(fileno(fp), size) == 0;
|
|
||||||
#elif defined(API_WINDOWS)
|
|
||||||
return _chsize(fileno(fp), size) == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end() const -> bool {
|
|
||||||
if(!fp) return true; //file not open
|
|
||||||
return file_offset >= file_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto open() const -> bool {
|
|
||||||
return fp;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
|
||||||
return open();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto open(const string& filename, mode mode_) -> bool {
|
|
||||||
if(fp) return false;
|
|
||||||
|
|
||||||
switch(file_mode = mode_) {
|
|
||||||
#if defined(API_POSIX)
|
|
||||||
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;
|
|
||||||
#elif defined(API_WINDOWS)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto close() -> void {
|
|
||||||
if(!fp) return;
|
|
||||||
buffer_flush();
|
|
||||||
fclose(fp);
|
|
||||||
fp = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator=(const file&) -> file& = delete;
|
|
||||||
file(const file&) = delete;
|
|
||||||
file() = default;
|
|
||||||
|
|
||||||
file(const string& filename, mode mode_) {
|
|
||||||
open(filename, mode_);
|
|
||||||
}
|
|
||||||
|
|
||||||
~file() {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
uint file_offset = 0;
|
|
||||||
uint file_size = 0;
|
|
||||||
mode file_mode = mode::read;
|
|
||||||
|
|
||||||
auto buffer_sync() -> void {
|
|
||||||
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);
|
|
||||||
uint length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
|
|
||||||
if(length) auto unused = fread(buffer, 1, length, fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto buffer_flush() -> void {
|
|
||||||
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);
|
|
||||||
uint length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
|
|
||||||
if(length) auto unused = fwrite(buffer, 1, length, fp);
|
|
||||||
buffer_offset = -1; //invalidate buffer
|
|
||||||
buffer_dirty = false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
213
nall/filemap.hpp
213
nall/filemap.hpp
|
@ -1,213 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <nall/file.hpp>
|
|
||||||
#include <nall/stdint.hpp>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#if defined(PLATFORM_WINDOWS)
|
|
||||||
#include <nall/windows/utf8.hpp>
|
|
||||||
#else
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nall {
|
|
||||||
|
|
||||||
struct filemap {
|
|
||||||
enum class mode : unsigned { read, write, readwrite, writeread };
|
|
||||||
|
|
||||||
filemap() { p_ctor(); }
|
|
||||||
filemap(const string& filename, mode mode_) { p_ctor(); p_open(filename, mode_); }
|
|
||||||
~filemap() { p_dtor(); }
|
|
||||||
|
|
||||||
explicit operator bool() const { return open(); }
|
|
||||||
auto open() const -> bool { return p_open(); }
|
|
||||||
auto open(const string& filename, mode mode_) -> bool { return p_open(filename, mode_); }
|
|
||||||
auto close() -> void { return p_close(); }
|
|
||||||
auto size() const -> unsigned { return p_size; }
|
|
||||||
auto data() -> uint8_t* { return p_handle; }
|
|
||||||
auto data() const -> const uint8_t* { return p_handle; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t* p_handle = nullptr;
|
|
||||||
unsigned p_size = 0;
|
|
||||||
|
|
||||||
#if defined(API_WINDOWS)
|
|
||||||
//=============
|
|
||||||
//MapViewOfFile
|
|
||||||
//=============
|
|
||||||
|
|
||||||
HANDLE p_filehandle;
|
|
||||||
HANDLE p_maphandle;
|
|
||||||
|
|
||||||
auto p_open() const -> bool {
|
|
||||||
return p_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_open(const string& filename, mode mode_) -> bool {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_close() -> void {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_ctor() -> void {
|
|
||||||
p_filehandle = INVALID_HANDLE_VALUE;
|
|
||||||
p_maphandle = INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_dtor() -> void {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
//====
|
|
||||||
//mmap
|
|
||||||
//====
|
|
||||||
|
|
||||||
int p_fd;
|
|
||||||
|
|
||||||
auto p_open() const -> bool {
|
|
||||||
return p_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_open(const string& filename, mode mode_) -> bool {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_close() -> void {
|
|
||||||
if(p_handle) {
|
|
||||||
munmap(p_handle, p_size);
|
|
||||||
p_handle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(p_fd >= 0) {
|
|
||||||
::close(p_fd);
|
|
||||||
p_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_ctor() -> void {
|
|
||||||
p_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto p_dtor() -> void {
|
|
||||||
p_close();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <nall/filemap.hpp>
|
#include <nall/file-map.hpp>
|
||||||
#include <nall/interpolation.hpp>
|
#include <nall/interpolation.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/decode/bmp.hpp>
|
#include <nall/decode/bmp.hpp>
|
||||||
|
|
|
@ -11,6 +11,10 @@ namespace nall {
|
||||||
struct inode {
|
struct inode {
|
||||||
enum class time : uint { create, modify, access };
|
enum class time : uint { create, modify, access };
|
||||||
|
|
||||||
|
inode() = delete;
|
||||||
|
inode(const inode&) = delete;
|
||||||
|
auto operator=(const inode&) -> inode& = delete;
|
||||||
|
|
||||||
static auto exists(const string& name) -> bool {
|
static auto exists(const string& name) -> bool {
|
||||||
return access(name, F_OK) == 0;
|
return access(name, F_OK) == 0;
|
||||||
}
|
}
|
||||||
|
@ -45,19 +49,21 @@ struct inode {
|
||||||
return data.st_gid;
|
return data.st_gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(PLATFORM_WINDOWS)
|
static auto owner(const string& name) -> string {
|
||||||
static auto user(const string& name) -> string {
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
struct passwd* pw = getpwuid(uid(name));
|
struct passwd* pw = getpwuid(uid(name));
|
||||||
if(pw && pw->pw_name) return pw->pw_name;
|
if(pw && pw->pw_name) return pw->pw_name;
|
||||||
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto group(const string& name) -> string {
|
static auto group(const string& name) -> string {
|
||||||
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
struct group* gr = getgrgid(gid(name));
|
struct group* gr = getgrgid(gid(name));
|
||||||
if(gr && gr->gr_name) return gr->gr_name;
|
if(gr && gr->gr_name) return gr->gr_name;
|
||||||
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static auto timestamp(const string& name, time mode = time::modify) -> uint64_t {
|
static auto timestamp(const string& name, time mode = time::modify) -> uint64_t {
|
||||||
struct stat data{};
|
struct stat data{};
|
||||||
|
@ -83,6 +89,41 @@ struct inode {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto setMode(const string& name, uint mode) -> bool {
|
||||||
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
|
return chmod(name, mode) == 0;
|
||||||
|
#else
|
||||||
|
return _wchmod(utf16_t(name), (mode & 0400 ? _S_IREAD : 0) | (mode & 0200 ? _S_IWRITE : 0)) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto setOwner(const string& name, const string& owner) -> bool {
|
||||||
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
|
struct passwd* pwd = getpwnam(owner);
|
||||||
|
if(!pwd) return false;
|
||||||
|
return chown(name, pwd->pw_uid, inode::gid(name)) == 0;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto setGroup(const string& name, const string& group) -> bool {
|
||||||
|
#if !defined(PLATFORM_WINDOWS)
|
||||||
|
struct group* grp = getgrnam(group);
|
||||||
|
if(!grp) return false;
|
||||||
|
return chown(name, inode::uid(name), grp->gr_gid) == 0;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto setTimestamp(const string& name, uint64_t value, time mode = time::modify) -> bool {
|
||||||
|
struct utimbuf timeBuffer;
|
||||||
|
timeBuffer.modtime = mode == time::modify ? value : inode::timestamp(name, time::modify);
|
||||||
|
timeBuffer.actime = mode == time::access ? value : inode::timestamp(name, time::access);
|
||||||
|
return utime(name, &timeBuffer) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
//returns true if 'name' already exists
|
//returns true if 'name' already exists
|
||||||
static auto create(const string& name, uint permissions = 0755) -> bool {
|
static auto create(const string& name, uint permissions = 0755) -> bool {
|
||||||
if(exists(name)) return true;
|
if(exists(name)) return true;
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//note: gcc 4.9 does not support user-defined literals with arguments other than const char*
|
|
||||||
//once nall increases the minimum required GCC version, the use of nall/atoi.hpp can beremoved
|
|
||||||
|
|
||||||
#include <nall/atoi.hpp>
|
|
||||||
|
|
||||||
namespace nall { namespace Literal {
|
|
||||||
|
|
||||||
struct Capacity { uint value; };
|
|
||||||
struct Size { uint value; };
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
||||||
namespace nall {
|
|
||||||
|
|
||||||
constexpr inline auto operator"" _capacity(const char* s) -> Literal::Capacity {
|
|
||||||
return {(uint)toNatural(s)};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline auto operator"" _size(const char* s) -> Literal::Size {
|
|
||||||
return {(uint)toNatural(s)};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
namespace nall { namespace Location {
|
namespace nall { namespace Location {
|
||||||
|
|
||||||
// (/parent/child.type/)
|
// (/parent/child.type/)
|
||||||
|
|
|
@ -5,6 +5,12 @@
|
||||||
namespace nall { namespace MAC {
|
namespace nall { namespace MAC {
|
||||||
|
|
||||||
struct Poly1305 {
|
struct Poly1305 {
|
||||||
|
auto authenticate(array_view<uint8_t> memory, uint256_t nonce) -> uint128_t {
|
||||||
|
initialize(nonce);
|
||||||
|
process(memory.data(), memory.size());
|
||||||
|
return finish();
|
||||||
|
}
|
||||||
|
|
||||||
auto initialize(uint256_t key) -> void {
|
auto initialize(uint256_t key) -> void {
|
||||||
uint64_t t0 = key >> 0;
|
uint64_t t0 = key >> 0;
|
||||||
uint64_t t1 = key >> 64;
|
uint64_t t1 = key >> 64;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/arguments.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
auto main(vector<string> arguments) -> void;
|
auto main(Arguments arguments) -> void;
|
||||||
|
|
||||||
auto main(int argc, char** argv) -> int {
|
auto main(int argc, char** argv) -> int {
|
||||||
#if defined(PLATFORM_WINDOWS)
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
@ -14,21 +15,8 @@ namespace nall {
|
||||||
_setmode(_fileno(stdin), O_BINARY);
|
_setmode(_fileno(stdin), O_BINARY);
|
||||||
_setmode(_fileno(stdout), O_BINARY);
|
_setmode(_fileno(stdout), O_BINARY);
|
||||||
_setmode(_fileno(stderr), O_BINARY);
|
_setmode(_fileno(stderr), O_BINARY);
|
||||||
utf8_arguments(argc, argv);
|
|
||||||
#endif
|
#endif
|
||||||
|
return main(move(Arguments{argc, argv})), EXIT_SUCCESS;
|
||||||
vector<string> arguments;
|
|
||||||
for(auto n : range(argc)) {
|
|
||||||
string argument = argv[n];
|
|
||||||
|
|
||||||
//normalize directory and file path arguments
|
|
||||||
if(directory::exists(argument)) argument.transform("\\", "/").trimRight("/").append("/");
|
|
||||||
else if(file::exists(argument)) argument.transform("\\", "/").trimRight("/");
|
|
||||||
|
|
||||||
arguments.append(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
return main(move(arguments)), EXIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
#include <nall/any.hpp>
|
#include <nall/any.hpp>
|
||||||
|
//#include <nall/arguments.hpp>
|
||||||
#include <nall/arithmetic.hpp>
|
#include <nall/arithmetic.hpp>
|
||||||
#include <nall/array.hpp>
|
#include <nall/array.hpp>
|
||||||
#include <nall/array-span.hpp>
|
#include <nall/array-span.hpp>
|
||||||
|
@ -28,7 +29,8 @@
|
||||||
#include <nall/dl.hpp>
|
#include <nall/dl.hpp>
|
||||||
#include <nall/endian.hpp>
|
#include <nall/endian.hpp>
|
||||||
#include <nall/file.hpp>
|
#include <nall/file.hpp>
|
||||||
#include <nall/filemap.hpp>
|
#include <nall/file-buffer.hpp>
|
||||||
|
#include <nall/file-map.hpp>
|
||||||
#include <nall/function.hpp>
|
#include <nall/function.hpp>
|
||||||
#include <nall/hashset.hpp>
|
#include <nall/hashset.hpp>
|
||||||
#include <nall/hid.hpp>
|
#include <nall/hid.hpp>
|
||||||
|
@ -58,6 +60,7 @@
|
||||||
#include <nall/simd.hpp>
|
#include <nall/simd.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/terminal.hpp>
|
||||||
#include <nall/thread.hpp>
|
#include <nall/thread.hpp>
|
||||||
#include <nall/traits.hpp>
|
#include <nall/traits.hpp>
|
||||||
#include <nall/unique-pointer.hpp>
|
#include <nall/unique-pointer.hpp>
|
||||||
|
@ -65,6 +68,7 @@
|
||||||
#include <nall/varint.hpp>
|
#include <nall/varint.hpp>
|
||||||
#include <nall/vector.hpp>
|
#include <nall/vector.hpp>
|
||||||
#include <nall/view.hpp>
|
#include <nall/view.hpp>
|
||||||
|
#include <nall/arguments.hpp> //todo: compilation errors when included earlier
|
||||||
#include <nall/decode/base.hpp>
|
#include <nall/decode/base.hpp>
|
||||||
#include <nall/decode/base64.hpp>
|
#include <nall/decode/base64.hpp>
|
||||||
#include <nall/decode/bmp.hpp>
|
#include <nall/decode/bmp.hpp>
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace nall { namespace Path {
|
||||||
|
|
||||||
inline auto active() -> string {
|
inline auto active() -> string {
|
||||||
char path[PATH_MAX] = "";
|
char path[PATH_MAX] = "";
|
||||||
auto unused = getcwd(path, PATH_MAX);
|
(void)getcwd(path, PATH_MAX);
|
||||||
string result = path;
|
string result = path;
|
||||||
if(!result) result = ".";
|
if(!result) result = ".";
|
||||||
result.transform("\\", "/");
|
result.transform("\\", "/");
|
||||||
|
|
|
@ -37,6 +37,7 @@ namespace Math {
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <utime.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
|
@ -54,8 +54,10 @@ protected:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace PRNG {
|
||||||
|
|
||||||
//Galois linear feedback shift register using CRC64 polynomials
|
//Galois linear feedback shift register using CRC64 polynomials
|
||||||
struct PRNG_LFSR : RNG<PRNG_LFSR> {
|
struct LFSR : RNG<LFSR> {
|
||||||
auto seed(maybe<uint64_t> seed = {}) -> void {
|
auto seed(maybe<uint64_t> seed = {}) -> void {
|
||||||
lfsr = seed ? seed() : (uint64_t)randomSeed();
|
lfsr = seed ? seed() : (uint64_t)randomSeed();
|
||||||
for(uint n : range(8)) read(); //hide the CRC64 polynomial from initial output
|
for(uint n : range(8)) read(); //hide the CRC64 polynomial from initial output
|
||||||
|
@ -73,10 +75,10 @@ private:
|
||||||
static const uint64_t crc64 = 0xc96c'5795'd787'0f42;
|
static const uint64_t crc64 = 0xc96c'5795'd787'0f42;
|
||||||
uint64_t lfsr = crc64;
|
uint64_t lfsr = crc64;
|
||||||
|
|
||||||
friend class RNG<PRNG_LFSR>;
|
friend class RNG<LFSR>;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PRNG_PCG : RNG<PRNG_PCG> {
|
struct PCG : RNG<PCG> {
|
||||||
auto seed(maybe<uint32_t> seed = {}, maybe<uint32_t> sequence = {}) -> void {
|
auto seed(maybe<uint32_t> seed = {}, maybe<uint32_t> sequence = {}) -> void {
|
||||||
if(!seed) seed = (uint32_t)randomSeed();
|
if(!seed) seed = (uint32_t)randomSeed();
|
||||||
if(!sequence) sequence = 0;
|
if(!sequence) sequence = 0;
|
||||||
|
@ -105,12 +107,16 @@ private:
|
||||||
uint64_t state = 0;
|
uint64_t state = 0;
|
||||||
uint64_t increment = 0;
|
uint64_t increment = 0;
|
||||||
|
|
||||||
friend class RNG<PRNG_PCG>;
|
friend class RNG<PCG>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSPRNG {
|
||||||
|
|
||||||
//XChaCha20 cryptographically secure pseudo-random number generator
|
//XChaCha20 cryptographically secure pseudo-random number generator
|
||||||
struct CSPRNG_XChaCha20 : RNG<CSPRNG_XChaCha20> {
|
struct XChaCha20 : RNG<XChaCha20> {
|
||||||
CSPRNG_XChaCha20() { seed(); }
|
XChaCha20() { seed(); }
|
||||||
|
|
||||||
auto seed(maybe<uint256_t> key = {}, maybe<uint192_t> nonce = {}) -> void {
|
auto seed(maybe<uint256_t> key = {}, maybe<uint192_t> nonce = {}) -> void {
|
||||||
//the randomness comes from the key; the nonce just adds a bit of added entropy
|
//the randomness comes from the key; the nonce just adds a bit of added entropy
|
||||||
|
@ -130,16 +136,15 @@ private:
|
||||||
Cipher::XChaCha20 context{0, 0};
|
Cipher::XChaCha20 context{0, 0};
|
||||||
uint counter = 0;
|
uint counter = 0;
|
||||||
|
|
||||||
friend class RNG<CSPRNG_XChaCha20>;
|
friend class RNG<XChaCha20>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
using PRNG = PRNG_PCG;
|
|
||||||
using CSPRNG = CSPRNG_XChaCha20;
|
|
||||||
|
|
||||||
template<typename T = uint64_t> inline auto random() -> T {
|
template<typename T = uint64_t> inline auto random() -> T {
|
||||||
static PRNG_PCG pcg; //note: unseeded
|
static PRNG::PCG pcg; //note: unseeded
|
||||||
return pcg.random<T>();
|
return pcg.random<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,10 @@ struct has_serialize {
|
||||||
struct serializer {
|
struct serializer {
|
||||||
enum Mode : uint { Load, Save, Size };
|
enum Mode : uint { Load, Save, Size };
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
auto mode() const -> Mode {
|
auto mode() const -> Mode {
|
||||||
return _mode;
|
return _mode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,6 +234,20 @@ template<> struct stringify<const string_view&> {
|
||||||
const string_view& _view;
|
const string_view& _view;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<array_view<uint8_t>> {
|
||||||
|
stringify(const array_view<uint8_t>& source) : _view(source) {}
|
||||||
|
auto data() const -> const char* { return _view.data<const char>(); }
|
||||||
|
auto size() const -> uint { return _view.size(); }
|
||||||
|
const array_view<uint8_t>& _view;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct stringify<const array_view<uint8_t>&> {
|
||||||
|
stringify(const array_view<uint8_t>& source) : _view(source) {}
|
||||||
|
auto data() const -> const char* { return _view.data<const char>(); }
|
||||||
|
auto size() const -> uint { return _view.size(); }
|
||||||
|
const array_view<uint8_t>& _view;
|
||||||
|
};
|
||||||
|
|
||||||
template<> struct stringify<string_pascal> {
|
template<> struct stringify<string_pascal> {
|
||||||
stringify(const string_pascal& source) : _text(source) {}
|
stringify(const string_pascal& source) : _text(source) {}
|
||||||
auto data() const -> const char* { return _text.data(); }
|
auto data() const -> const char* { return _text.data(); }
|
||||||
|
@ -250,6 +264,7 @@ template<> struct stringify<const string_pascal&> {
|
||||||
|
|
||||||
//pointers
|
//pointers
|
||||||
|
|
||||||
|
//note: T = char* is matched by stringify<string_view>
|
||||||
template<typename T> struct stringify<T*> {
|
template<typename T> struct stringify<T*> {
|
||||||
stringify(const T* source) {
|
stringify(const T* source) {
|
||||||
if(!source) {
|
if(!source) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ auto string::read(string_view filename) -> string {
|
||||||
|
|
||||||
rewind(fp);
|
rewind(fp);
|
||||||
result.resize(filesize);
|
result.resize(filesize);
|
||||||
auto unused = fread(result.get(), 1, filesize, fp);
|
(void)fread(result.get(), 1, filesize, fp);
|
||||||
return fclose(fp), result;
|
return fclose(fp), result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall { namespace terminal {
|
||||||
|
|
||||||
|
inline auto escapable() -> bool {
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
//todo: colors are supported by Windows 10+ and with alternate terminals (eg msys)
|
||||||
|
//disabled for now for compatibility with Windows 7 and 8.1's cmd.exe
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace color {
|
||||||
|
|
||||||
|
template<typename... P> inline auto black(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[30m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto blue(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[94m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto green(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[92m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto cyan(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[96m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto red(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[91m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto magenta(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[95m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto yellow(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[93m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto white(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[97m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... P> inline auto gray(P&&... p) -> string {
|
||||||
|
if(!escapable()) return string{forward<P>(p)...};
|
||||||
|
return {"\e[37m", string{forward<P>(p)...}, "\e[0m"};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
//pull all type traits used by nall from std namespace into nall namespace
|
//pull all type traits used by nall from std namespace into nall namespace
|
||||||
//this removes the requirement to prefix type traits with std:: within nall
|
//this removes the requirement to prefix type traits with std:: within nall
|
||||||
|
@ -29,3 +30,10 @@ namespace nall {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
using std::true_type;
|
using std::true_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
#if INTMAX_BITS >= 128
|
||||||
|
template<> struct is_signed<int128_t> : true_type {};
|
||||||
|
template<> struct is_unsigned<uint128_t> : true_type {};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <nall/bit.hpp>
|
#include <nall/bit.hpp>
|
||||||
#include <nall/function.hpp>
|
#include <nall/function.hpp>
|
||||||
#include <nall/iterator.hpp>
|
#include <nall/iterator.hpp>
|
||||||
#include <nall/literals.hpp>
|
|
||||||
#include <nall/maybe.hpp>
|
#include <nall/maybe.hpp>
|
||||||
#include <nall/memory.hpp>
|
#include <nall/memory.hpp>
|
||||||
#include <nall/merge-sort.hpp>
|
#include <nall/merge-sort.hpp>
|
||||||
|
@ -23,8 +22,6 @@ struct vector_base {
|
||||||
|
|
||||||
//core.hpp
|
//core.hpp
|
||||||
vector_base() = default;
|
vector_base() = default;
|
||||||
vector_base(Literal::Capacity capacity);
|
|
||||||
vector_base(Literal::Size size);
|
|
||||||
vector_base(const initializer_list<T>& values);
|
vector_base(const initializer_list<T>& values);
|
||||||
vector_base(const type& source);
|
vector_base(const type& source);
|
||||||
vector_base(type&& source);
|
vector_base(type&& source);
|
||||||
|
@ -121,6 +118,7 @@ struct vector_base {
|
||||||
//utility.hpp
|
//utility.hpp
|
||||||
auto fill(const T& value = {}) -> void;
|
auto fill(const T& value = {}) -> void;
|
||||||
auto sort(const function<bool (const T& lhs, const T& rhs)>& comparator = [](auto& lhs, auto& rhs) { return lhs < rhs; }) -> void;
|
auto sort(const function<bool (const T& lhs, const T& rhs)>& comparator = [](auto& lhs, auto& rhs) { return lhs < rhs; }) -> void;
|
||||||
|
auto reverse() -> void;
|
||||||
auto find(const function<bool (const T& lhs)>& comparator) -> maybe<uint>;
|
auto find(const function<bool (const T& lhs)>& comparator) -> maybe<uint>;
|
||||||
auto find(const T& value) const -> maybe<uint>;
|
auto find(const T& value) const -> maybe<uint>;
|
||||||
auto findSorted(const T& value) const -> maybe<uint>;
|
auto findSorted(const T& value) const -> maybe<uint>;
|
||||||
|
|
|
@ -2,14 +2,6 @@
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
template<typename T> vector<T>::vector(Literal::Capacity capacity) {
|
|
||||||
reserve(capacity.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> vector<T>::vector(Literal::Size size) {
|
|
||||||
resize(size.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> vector<T>::vector(const initializer_list<T>& values) {
|
template<typename T> vector<T>::vector(const initializer_list<T>& values) {
|
||||||
reserveRight(values.size());
|
reserveRight(values.size());
|
||||||
for(auto& value : values) append(value);
|
for(auto& value : values) append(value);
|
||||||
|
|
|
@ -119,7 +119,7 @@ template<typename T> auto vector<T>::resizeLeft(uint size, const T& value) -> bo
|
||||||
if(size > _size) { //grow
|
if(size > _size) { //grow
|
||||||
reserveLeft(size);
|
reserveLeft(size);
|
||||||
_pool -= size - _size;
|
_pool -= size - _size;
|
||||||
for(uint n : reverse(range(size - _size))) new(_pool + n) T(value);
|
for(uint n : nall::reverse(range(size - _size))) new(_pool + n) T(value);
|
||||||
_left -= size - _size;
|
_left -= size - _size;
|
||||||
_size = size;
|
_size = size;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -11,7 +11,27 @@ template<> struct vector<uint8_t> : vector_base<uint8_t> {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U> auto appendm(U value, uint size) -> void {
|
template<typename U> auto appendm(U value, uint size) -> void {
|
||||||
for(uint byte : reverse(range(size))) append(uint8_t(value >> byte * 8));
|
for(uint byte : nall::reverse(range(size))) append(uint8_t(value >> byte * 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
//note: string_view is not declared here yet ...
|
||||||
|
auto appends(array_view<uint8_t> memory) -> void {
|
||||||
|
for(uint8_t byte : memory) append(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U> auto readl(int offset, uint size) -> U {
|
||||||
|
if(offset < 0) offset = this->size() - abs(offset);
|
||||||
|
U value = 0;
|
||||||
|
for(uint byte : range(size)) value |= (U)operator[](offset + byte) << byte * 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto view(uint offset, uint length) -> array_view<uint8_t> {
|
||||||
|
#ifdef DEBUG
|
||||||
|
struct out_of_bounds {};
|
||||||
|
if(offset + length >= size()) throw out_of_bounds{};
|
||||||
|
#endif
|
||||||
|
return {data() + offset, length};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,12 @@ template<typename T> auto vector<T>::sort(const function<bool (const T& lhs, con
|
||||||
nall::sort(_pool, _size, comparator);
|
nall::sort(_pool, _size, comparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T> auto vector<T>::reverse() -> void {
|
||||||
|
vector<T> reversed;
|
||||||
|
for(uint n : range(size())) reversed.prepend(_pool[n]);
|
||||||
|
operator=(move(reversed));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T> auto vector<T>::find(const function<bool (const T& lhs)>& comparator) -> maybe<uint> {
|
template<typename T> auto vector<T>::find(const function<bool (const T& lhs)>& comparator) -> maybe<uint> {
|
||||||
for(uint n : range(size())) if(comparator(_pool[n])) return n;
|
for(uint n : range(size())) if(comparator(_pool[n])) return n;
|
||||||
return nothing;
|
return nothing;
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct file : vfs::file {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto seek(intmax offset_, index index_) -> void override {
|
auto seek(intmax offset_, index index_) -> void override {
|
||||||
_fp.seek(offset_, (nall::file::index)index_);
|
_fp.seek(offset_, (uint)index_);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto read() -> uint8_t override {
|
auto read() -> uint8_t override {
|
||||||
|
@ -41,11 +41,11 @@ private:
|
||||||
auto operator=(const file&) -> file& = delete;
|
auto operator=(const file&) -> file& = delete;
|
||||||
|
|
||||||
auto _open(string location_, mode mode_) -> bool {
|
auto _open(string location_, mode mode_) -> bool {
|
||||||
if(!_fp.open(location_, (nall::file::mode)mode_)) return false;
|
if(!_fp.open(location_, (uint)mode_)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nall::file _fp;
|
file_buffer _fp;
|
||||||
};
|
};
|
||||||
|
|
||||||
}}}
|
}}}
|
||||||
|
|
|
@ -123,7 +123,7 @@ struct InputJoypadUdev {
|
||||||
play.type = EV_FF;
|
play.type = EV_FF;
|
||||||
play.code = jp.effectID;
|
play.code = jp.effectID;
|
||||||
play.value = enable;
|
play.value = enable;
|
||||||
auto unused = write(jp.fd, &play, sizeof(input_event));
|
(void)write(jp.fd, &play, sizeof(input_event));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in New Issue