mirror of https://github.com/bsnes-emu/bsnes.git
Update to v088 release.
byuu says: Changes to v088: - OBJ mosaic Y fix - Laevateinn compilation - Remove filebrowser extra code - Fix -march=native on Windows - Fix purify mkdir - GBA sound volume - Add .gbb - free firmware memory after file load - Add GBA game to profile list (Yoshi's Island should work)
This commit is contained in:
parent
4b2944c39b
commit
77bb5b7891
|
@ -0,0 +1,3 @@
|
|||
purify/*.o
|
||||
purify/purify
|
||||
purify/analyze-gba
|
|
@ -13,7 +13,7 @@ target := ui
|
|||
# compiler
|
||||
c := $(compiler) -std=gnu99
|
||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||
flags := -I. -march=native -O3 -fomit-frame-pointer
|
||||
flags := -I. -O3 -fomit-frame-pointer
|
||||
link := -s
|
||||
objects := libco
|
||||
|
||||
|
@ -30,6 +30,7 @@ endif
|
|||
|
||||
# platform
|
||||
ifeq ($(platform),x)
|
||||
flags += -march=native
|
||||
link += -ldl -lX11 -lXext
|
||||
else ifeq ($(platform),osx)
|
||||
else ifeq ($(platform),win)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BASE_HPP
|
||||
#define BASE_HPP
|
||||
|
||||
static const char Version[] = "087.30";
|
||||
static const char Version[] = "088";
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/algorithm.hpp>
|
||||
|
|
|
@ -70,7 +70,7 @@ void APU::main() {
|
|||
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
|
||||
|
||||
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
|
||||
interface->audioSample(lsample << 5, rsample << 5);
|
||||
interface->audioSample(sclamp<16>(lsample << 7), sclamp<16>(rsample << 7)); //should be <<5, use <<7 for added volume
|
||||
step(512);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,6 @@ void PPU::render_object(Object &obj) {
|
|||
unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
|
||||
unsigned baseaddr = obj.character * 32;
|
||||
|
||||
if(obj.vflip && obj.affine == 0) {
|
||||
py ^= obj.height - 1;
|
||||
}
|
||||
|
||||
if(obj.mosaic && regs.mosaic.objvsize) {
|
||||
signed mosaicy = (regs.vcounter / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize);
|
||||
py = obj.y >= 160 || mosaicy - obj.y >= 0 ? mosaicy - obj.y : 0;
|
||||
|
@ -48,6 +44,7 @@ void PPU::render_object(Object &obj) {
|
|||
x = px;
|
||||
y = py;
|
||||
if(obj.hflip) x ^= obj.width - 1;
|
||||
if(obj.vflip) y ^= obj.height - 1;
|
||||
} else {
|
||||
x = (fx >> 8) + centerx;
|
||||
y = (fy >> 8) + centery;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
options += debugger
|
||||
|
||||
processor := arm hg51b
|
||||
processors := arm hg51b upd96050
|
||||
include processor/Makefile
|
||||
|
||||
include $(snes)/Makefile
|
||||
|
@ -67,18 +67,19 @@ else
|
|||
endif
|
||||
|
||||
install:
|
||||
ifeq ($(platform),x)
|
||||
install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
|
||||
ifeq ($(USER),root)
|
||||
@echo Please do not run make install as root.
|
||||
@echo The installer needs to know your home directory to install important files.
|
||||
else ifeq ($(platform),x)
|
||||
sudo install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
|
||||
|
||||
mkdir -p ~/.config/$(name)
|
||||
# install -D -m 644 data/$(name).png $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
|
||||
# install -D -m 644 data/$(name).desktop $(DESTDIR)$(prefix)/share/applications/$(name).desktop
|
||||
cp -R profile/* ~/.config/$(name)
|
||||
cp data/cheats.xml ~/.config/$(name)/cheats.xml
|
||||
chmod 777 ~/.config/$(name) ~/.config/$(name)/cheats.xml
|
||||
chmod -R 777 ~/.config/$(name)
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
ifeq ($(platform),x)
|
||||
rm $(DESTDIR)$(prefix)/bin/$(name)
|
||||
# rm $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
|
||||
# rm $(DESTDIR)$(prefix)/share/applications/$(name).desktop
|
||||
sudo rm $(DESTDIR)$(prefix)/bin/$(name)
|
||||
endif
|
||||
|
|
|
@ -17,7 +17,7 @@ bool Interface::loadCartridge(const string &foldername) {
|
|||
|
||||
string markup;
|
||||
markup.readfile({ pathName, "manifest.xml" });
|
||||
if(markup.empty()) markup = SnesCartridge(data, size).markup;
|
||||
if(markup.empty()) markup = SuperFamicomCartridge(data, size).markup;
|
||||
|
||||
SNES::cartridge.rom.copy(data, size);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
|
||||
|
@ -156,4 +156,11 @@ void Interface::message(const string &text) {
|
|||
Interface::Interface() {
|
||||
SNES::interface = this;
|
||||
SNES::system.init();
|
||||
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read({application->userpath, "Super Famicom.sys/spc700.rom"}, data, size)) {
|
||||
memcpy(SNES::smp.iplrom, data, min(64u, size));
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,10 +43,10 @@ FileBrowser::FileBrowser() {
|
|||
fileList.onActivate = openButton.onActivate = { &FileBrowser::fileListActivate, this };
|
||||
|
||||
filterModes.append({ "Default", "", { "*" } });
|
||||
filterModes.append({ "NES", "", { "*.fc", "*.nes" } });
|
||||
filterModes.append({ "SNES", "", { "*.sfc" } });
|
||||
filterModes.append({ "GameBoy", "", { "*.gb", "*.gbc" } });
|
||||
filterModes.append({ "GameBoyColor", "", { "*.gbc" } });
|
||||
filterModes.append({ "Famicom", "", { "*.fc", "*.nes" } });
|
||||
filterModes.append({ "SuperFamicom", "", { "*.sfc" } });
|
||||
filterModes.append({ "GameBoy", "", { "*.gb", "*.gbb" } });
|
||||
filterModes.append({ "GameBoyColor", "", { "*.gbc", "*.gbb" } });
|
||||
filterModes.append({ "GameBoyAdvance", "", { "*.gba" } });
|
||||
filterModes.append({ "Satellaview", "", { "*.bs" } });
|
||||
filterModes.append({ "SufamiTurbo", "", { "*.st" } });
|
||||
|
@ -133,21 +133,6 @@ bool FileBrowser::loadFolder(const string &requestedPath) {
|
|||
if(accept == false) return false;
|
||||
loadFile(requestedPath);
|
||||
return true;
|
||||
|
||||
// lstring contentsList = directory::contents(requestedPath);
|
||||
// lstring fileNameList;
|
||||
// for(auto &fileName : contentsList) {
|
||||
// for(auto &filter : mode->filter) {
|
||||
// if(fileName.wildcard(filter)) {
|
||||
// fileNameList.append(fileName);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if(fileNameList.size() != 1) return false;
|
||||
// loadFile({ requestedPath, fileNameList[0] });
|
||||
// return true;
|
||||
}
|
||||
|
||||
void FileBrowser::loadFile(const string &filename) {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
bool InterfaceCore::loadFirmware(string filename, string keyname, uint8_t *targetdata, unsigned targetsize) {
|
||||
bool result = false;
|
||||
|
||||
filename = application->path(filename);
|
||||
string markup;
|
||||
markup.readfile(filename);
|
||||
|
@ -15,12 +17,13 @@ bool InterfaceCore::loadFirmware(string filename, string keyname, uint8_t *targe
|
|||
if(file::read({dir(filename),firmware}, data, size) == true) {
|
||||
if(nall::sha256(data, size) == hash) {
|
||||
memcpy(targetdata, data, min(targetsize, size));
|
||||
return true;
|
||||
result = true;
|
||||
} else {
|
||||
MessageWindow::information(Window::None, {"Warning: Firmware SHA256 sum is incorrect:\n\n", dir(filename), firmware});
|
||||
}
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
include nall/Makefile
|
||||
|
||||
application := purify
|
||||
flags := -std=gnu++0x -I. -O3 -fomit-frame-pointer
|
||||
link := -s
|
||||
|
||||
ifeq ($(platform),win)
|
||||
flags += -DPHOENIX_WINDOWS
|
||||
link += -lkernel32 -luser32 -lgdi32 -ladvapi32 -lole32 -lcomctl32 -lcomdlg32
|
||||
else ifeq ($(phoenix),qt)
|
||||
flags += -DPHOENIX_QT `pkg-config --cflags QtCore QtGui`
|
||||
link += `pkg-config --libs QtCore QtGui`
|
||||
else
|
||||
flags += -DPHOENIX_GTK `pkg-config --cflags gtk+-2.0`
|
||||
link += `pkg-config --libs gtk+-2.0`
|
||||
endif
|
||||
|
||||
all: phoenix.o $(application).o
|
||||
$(cpp) -o $(application) phoenix.o $(application).o $(link)
|
||||
|
||||
phoenix.o: phoenix/phoenix.cpp
|
||||
$(cpp) -c -o phoenix.o phoenix/phoenix.cpp $(flags)
|
||||
|
||||
$(application).o: $(application).cpp
|
||||
$(cpp) -c -o $(application).o $(application).cpp $(flags)
|
||||
|
||||
install:
|
||||
sudo cp $(application) /usr/local/bin/$(application)
|
||||
|
||||
clean:
|
||||
$(delete) *.o
|
||||
|
||||
sync:
|
||||
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
|
||||
cp -r ../nall ./nall
|
||||
cp -r ../phoenix ./phoenix
|
||||
rm -r nall/test
|
||||
rm -r phoenix/test
|
|
@ -0,0 +1,47 @@
|
|||
#include <nall/platform.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/string.hpp>
|
||||
using namespace nall;
|
||||
|
||||
int main() {
|
||||
string filedata;
|
||||
if(filedata.readfile("database-gameboyadvance.bsv") == false) return 0;
|
||||
lstring lines = filedata.split("\n");
|
||||
unsigned count;
|
||||
|
||||
file fp;
|
||||
if(fp.open("database-gameboyadvance-analysis.txt", file::mode::write) == false) return 0;
|
||||
|
||||
fp.print("Multiple Tags:\n");
|
||||
fp.print("--------------\n\n");
|
||||
count = 0;
|
||||
for(auto &line : lines) {
|
||||
if(line.empty()) continue;
|
||||
lstring part = line.split("{}");
|
||||
if(part(2).position(",")) fp.print(part(3), "\n", part(2), "\n\n"), count++;
|
||||
}
|
||||
fp.print("Total: ", count, "\n\n");
|
||||
|
||||
fp.print("EEPROM:\n");
|
||||
fp.print("-------\n");
|
||||
count = 0;
|
||||
for(auto &line : lines) {
|
||||
if(line.empty()) continue;
|
||||
lstring part = line.split("{}");
|
||||
if(part(2).position("EEPROM")) fp.print(part(3), "\n", part(2), "\n\n"), count++;
|
||||
}
|
||||
fp.print("Total: ", count, "\n\n");
|
||||
|
||||
fp.print("No RAM:\n");
|
||||
fp.print("-------\n");
|
||||
count = 0;
|
||||
for(auto &line : lines) {
|
||||
if(line.empty()) continue;
|
||||
lstring part = line.split("{}");
|
||||
if(part(2).empty()) fp.print(part(3), "\n"), count++;
|
||||
}
|
||||
fp.print("\nTotal: ", count, "\n\n");
|
||||
|
||||
fp.close();
|
||||
return 0;
|
||||
}
|
|
@ -19,6 +19,9 @@ ifeq ($(platform),)
|
|||
ifeq ($(uname),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
else ifneq ($(findstring CYGWIN,$(uname)),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
else ifneq ($(findstring Darwin,$(uname)),)
|
||||
platform := osx
|
||||
delete = rm -f $1
|
||||
|
@ -32,12 +35,15 @@ ifeq ($(compiler),)
|
|||
ifeq ($(platform),win)
|
||||
compiler := gcc
|
||||
else ifeq ($(platform),osx)
|
||||
compiler := gcc-mp-4.5
|
||||
compiler := gcc-mp-4.6
|
||||
else
|
||||
compiler := gcc-4.5
|
||||
compiler := gcc-4.6
|
||||
endif
|
||||
endif
|
||||
|
||||
c := $(compiler) -std=gnu99
|
||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||
|
||||
ifeq ($(prefix),)
|
||||
prefix := /usr/local
|
||||
endif
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef NALL_ANY_HPP
|
||||
#define NALL_ANY_HPP
|
||||
|
||||
#include <typeinfo>
|
||||
#include <nall/type_traits.hpp>
|
||||
|
||||
namespace nall {
|
||||
struct any {
|
||||
bool empty() const { return container; }
|
||||
const std::type_info& type() const { return container ? container->type() : typeid(void); }
|
||||
|
||||
template<typename T> any& operator=(const T& value_) {
|
||||
typedef typename type_if<
|
||||
std::is_array<T>::value,
|
||||
typename std::remove_extent<typename std::add_const<T>::type>::type*,
|
||||
T
|
||||
>::type auto_t;
|
||||
|
||||
if(type() == typeid(auto_t)) {
|
||||
static_cast<holder<auto_t>*>(container)->value = (auto_t)value_;
|
||||
} else {
|
||||
if(container) delete container;
|
||||
container = new holder<auto_t>((auto_t)value_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
any() : container(nullptr) {}
|
||||
~any() { if(container) delete container; }
|
||||
template<typename T> any(const T& value_) : container(nullptr) { operator=(value_); }
|
||||
|
||||
private:
|
||||
struct placeholder {
|
||||
virtual const std::type_info& type() const = 0;
|
||||
} *container;
|
||||
|
||||
template<typename T> struct holder : placeholder {
|
||||
T value;
|
||||
const std::type_info& type() const { return typeid(T); }
|
||||
holder(const T& value_) : value(value_) {}
|
||||
};
|
||||
|
||||
template<typename T> friend T any_cast(any&);
|
||||
template<typename T> friend T any_cast(const any&);
|
||||
template<typename T> friend T* any_cast(any*);
|
||||
template<typename T> friend const T* any_cast(const any*);
|
||||
};
|
||||
|
||||
template<typename T> T any_cast(any &value) {
|
||||
typedef typename std::remove_reference<T>::type nonref;
|
||||
if(value.type() != typeid(nonref)) throw;
|
||||
return static_cast<any::holder<nonref>*>(value.container)->value;
|
||||
}
|
||||
|
||||
template<typename T> T any_cast(const any &value) {
|
||||
typedef const typename std::remove_reference<T>::type nonref;
|
||||
if(value.type() != typeid(nonref)) throw;
|
||||
return static_cast<any::holder<nonref>*>(value.container)->value;
|
||||
}
|
||||
|
||||
template<typename T> T* any_cast(any *value) {
|
||||
if(!value || value->type() != typeid(T)) return nullptr;
|
||||
return &static_cast<any::holder<T>*>(value->container)->value;
|
||||
}
|
||||
|
||||
template<typename T> const T* any_cast(const any *value) {
|
||||
if(!value || value->type() != typeid(T)) return nullptr;
|
||||
return &static_cast<any::holder<T>*>(value->container)->value;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,289 @@
|
|||
#ifndef NALL_ARRAY_HPP
|
||||
#define NALL_ARRAY_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/type_traits.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename T, typename Enable = void> struct array;
|
||||
|
||||
//non-reference array
|
||||
//===================
|
||||
|
||||
template<typename T> struct array<T, typename std::enable_if<!std::is_reference<T>::value>::type> {
|
||||
struct exception_out_of_bounds{};
|
||||
|
||||
protected:
|
||||
T *pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
objectsize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
if(newsize == poolsize) return;
|
||||
|
||||
pool = (T*)realloc(pool, newsize * sizeof(T));
|
||||
poolsize = newsize;
|
||||
objectsize = min(objectsize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
|
||||
objectsize = newsize;
|
||||
}
|
||||
|
||||
T* get(unsigned minsize = 0) {
|
||||
if(minsize > objectsize) resize(minsize);
|
||||
return pool;
|
||||
}
|
||||
|
||||
void append(const T data) {
|
||||
operator()(objectsize) = data;
|
||||
}
|
||||
|
||||
void append(const T data[], unsigned length) {
|
||||
for(unsigned n = 0; n < length; n++) operator()(objectsize) = data[n];
|
||||
}
|
||||
|
||||
void remove() {
|
||||
if(size() > 0) resize(size - 1); //remove last element only
|
||||
}
|
||||
|
||||
void remove(unsigned index, unsigned count = 1) {
|
||||
for(unsigned i = index; count + i < objectsize; i++) {
|
||||
pool[i] = pool[count + i];
|
||||
}
|
||||
if(count + index >= objectsize) resize(index); //every element >= index was removed
|
||||
else resize(objectsize - count);
|
||||
}
|
||||
|
||||
void sort() {
|
||||
nall::sort(pool, objectsize);
|
||||
}
|
||||
|
||||
template<typename Comparator> void sort(const Comparator &lessthan) {
|
||||
nall::sort(pool, objectsize, lessthan);
|
||||
}
|
||||
|
||||
optional<unsigned> find(const T data) {
|
||||
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
|
||||
return { false, 0u };
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(pool, 0, objectsize * sizeof(T));
|
||||
}
|
||||
|
||||
array() : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
}
|
||||
|
||||
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
for(auto &data : list) append(data);
|
||||
}
|
||||
|
||||
~array() {
|
||||
reset();
|
||||
}
|
||||
|
||||
//copy
|
||||
array& operator=(const array &source) {
|
||||
if(pool) free(pool);
|
||||
objectsize = source.objectsize;
|
||||
poolsize = source.poolsize;
|
||||
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
|
||||
memcpy(pool, source.pool, sizeof(T) * objectsize); //... but only copy used pool objects
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(const array &source) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
//move
|
||||
array& operator=(array &&source) {
|
||||
if(pool) free(pool);
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
objectsize = source.objectsize;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(array &&source) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
//access
|
||||
inline T& operator[](unsigned position) {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline const T& operator[](unsigned position) const {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline T& operator()(unsigned position) {
|
||||
if(position >= objectsize) resize(position + 1);
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
inline const T& operator()(unsigned position, const T& data) {
|
||||
if(position >= objectsize) return data;
|
||||
return pool[position];
|
||||
}
|
||||
|
||||
//iteration
|
||||
T* begin() { return &pool[0]; }
|
||||
T* end() { return &pool[objectsize]; }
|
||||
const T* begin() const { return &pool[0]; }
|
||||
const T* end() const { return &pool[objectsize]; }
|
||||
};
|
||||
|
||||
//reference array
|
||||
//===============
|
||||
|
||||
template<typename TR> struct array<TR, typename std::enable_if<std::is_reference<TR>::value>::type> {
|
||||
struct exception_out_of_bounds{};
|
||||
|
||||
protected:
|
||||
typedef typename std::remove_reference<TR>::type T;
|
||||
T **pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
objectsize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
if(newsize == poolsize) return;
|
||||
|
||||
pool = (T**)realloc(pool, sizeof(T*) * newsize);
|
||||
poolsize = newsize;
|
||||
objectsize = min(objectsize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(bit::round(newsize));
|
||||
objectsize = newsize;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool append(T& data, Args&&... args) {
|
||||
bool result = append(data);
|
||||
append(std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool append(T& data) {
|
||||
if(find(data)) return false;
|
||||
unsigned offset = objectsize++;
|
||||
if(offset >= poolsize) resize(offset + 1);
|
||||
pool[offset] = &data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(T& data) {
|
||||
if(auto position = find(data)) {
|
||||
for(signed i = position(); i < objectsize - 1; i++) pool[i] = pool[i + 1];
|
||||
resize(objectsize - 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<unsigned> find(const T& data) {
|
||||
for(unsigned n = 0; n < objectsize; n++) if(pool[n] == &data) return { true, n };
|
||||
return { false, 0u };
|
||||
}
|
||||
|
||||
template<typename... Args> array(Args&&... args) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
construct(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
~array() {
|
||||
reset();
|
||||
}
|
||||
|
||||
array& operator=(const array &source) {
|
||||
if(pool) free(pool);
|
||||
objectsize = source.objectsize;
|
||||
poolsize = source.poolsize;
|
||||
pool = (T**)malloc(sizeof(T*) * poolsize);
|
||||
memcpy(pool, source.pool, sizeof(T*) * objectsize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
array& operator=(const array &&source) {
|
||||
if(pool) free(pool);
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
objectsize = source.objectsize;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator[](unsigned position) const {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
return *pool[position];
|
||||
}
|
||||
|
||||
//iteration
|
||||
struct iterator {
|
||||
bool operator!=(const iterator &source) const { return position != source.position; }
|
||||
T& operator*() { return source.operator[](position); }
|
||||
iterator& operator++() { position++; return *this; }
|
||||
iterator(const array &source, unsigned position) : source(source), position(position) {}
|
||||
private:
|
||||
const array &source;
|
||||
unsigned position;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(*this, 0); }
|
||||
iterator end() { return iterator(*this, objectsize); }
|
||||
const iterator begin() const { return iterator(*this, 0); }
|
||||
const iterator end() const { return iterator(*this, objectsize); }
|
||||
|
||||
private:
|
||||
void construct() {
|
||||
}
|
||||
|
||||
void construct(const array& source) { operator=(source); }
|
||||
void construct(const array&& source) { operator=(std::move(source)); }
|
||||
|
||||
template<typename... Args> void construct(T& data, Args&&... args) {
|
||||
append(data);
|
||||
construct(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,101 @@
|
|||
#ifndef NALL_ATOI_HPP
|
||||
#define NALL_ATOI_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//note: this header is intended to form the base for user-defined literals;
|
||||
//once they are supported by GCC. eg:
|
||||
//unsigned operator "" b(const char *s) { return binary(s); }
|
||||
//-> signed data = 1001b;
|
||||
//(0b1001 is nicer, but is not part of the C++ standard)
|
||||
|
||||
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
|
||||
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
|
||||
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
constexpr inline uintmax_t binary(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||
*s == '%' ? binary_(s + 1) :
|
||||
binary_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t octal(const char *s) {
|
||||
return (
|
||||
octal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline intmax_t integer(const char *s) {
|
||||
return (
|
||||
*s == '+' ? +decimal_(s + 1) :
|
||||
*s == '-' ? -decimal_(s + 1) :
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t decimal(const char *s) {
|
||||
return (
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t hex(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||
*s == '$' ? hex_(s + 1) :
|
||||
hex_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline intmax_t numeral(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||
*s == '0' ? octal_(s + 1) :
|
||||
*s == '+' ? +decimal_(s + 1) :
|
||||
*s == '-' ? -decimal_(s + 1) :
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
inline double fp(const char *s) {
|
||||
return atof(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,8 +5,7 @@
|
|||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class base64 {
|
||||
public:
|
||||
struct base64 {
|
||||
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||
output = new char[inlength * 8 / 6 + 6]();
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#ifndef NALL_BIT_HPP
|
||||
#define NALL_BIT_HPP
|
||||
|
||||
namespace nall {
|
||||
template<unsigned bits>
|
||||
inline uintmax_t uclamp(const uintmax_t x) {
|
||||
enum : uintmax_t { b = 1ull << (bits - 1), y = b * 2 - 1 };
|
||||
return y + ((x - y) & -(x < y)); //min(x, y);
|
||||
}
|
||||
|
||||
template<unsigned bits>
|
||||
inline uintmax_t uclip(const uintmax_t x) {
|
||||
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
|
||||
return (x & m);
|
||||
}
|
||||
|
||||
template<unsigned bits>
|
||||
inline intmax_t sclamp(const intmax_t x) {
|
||||
enum : intmax_t { b = 1ull << (bits - 1), m = b - 1 };
|
||||
return (x > m) ? m : (x < -b) ? -b : x;
|
||||
}
|
||||
|
||||
template<unsigned bits>
|
||||
inline intmax_t sclip(const intmax_t x) {
|
||||
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
|
||||
return ((x & m) ^ b) - b;
|
||||
}
|
||||
|
||||
namespace bit {
|
||||
constexpr inline uintmax_t mask(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s == '0' || *s == '1' ? mask(s + 1, (sum << 1) | 1) :
|
||||
*s == ' ' || *s == '_' ? mask(s + 1, sum) :
|
||||
*s ? mask(s + 1, sum << 1) :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t test(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s == '0' || *s == '1' ? test(s + 1, (sum << 1) | (*s - '0')) :
|
||||
*s == ' ' || *s == '_' ? test(s + 1, sum) :
|
||||
*s ? test(s + 1, sum << 1) :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
//lowest(0b1110) == 0b0010
|
||||
constexpr inline uintmax_t lowest(const uintmax_t x) {
|
||||
return x & -x;
|
||||
}
|
||||
|
||||
//clear_lowest(0b1110) == 0b1100
|
||||
constexpr inline uintmax_t clear_lowest(const uintmax_t x) {
|
||||
return x & (x - 1);
|
||||
}
|
||||
|
||||
//set_lowest(0b0101) == 0b0111
|
||||
constexpr inline uintmax_t set_lowest(const uintmax_t x) {
|
||||
return x | (x + 1);
|
||||
}
|
||||
|
||||
//count number of bits set in a byte
|
||||
inline unsigned count(uintmax_t x) {
|
||||
unsigned count = 0;
|
||||
do count += x & 1; while(x >>= 1);
|
||||
return count;
|
||||
}
|
||||
|
||||
//round up to next highest single bit:
|
||||
//round(15) == 16, round(16) == 16, round(17) == 32
|
||||
inline uintmax_t round(uintmax_t x) {
|
||||
if((x & (x - 1)) == 0) return x;
|
||||
while(x & (x - 1)) x &= x - 1;
|
||||
return x << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef NALL_BITARRAY_HPP
|
||||
#define NALL_BITARRAY_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
//statically-sized bit array
|
||||
//no bounds-checking on read/write
|
||||
//packed into uint8_t array (8 bits per byte)
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bitarray {
|
||||
uint8_t *pool;
|
||||
unsigned poolsize;
|
||||
|
||||
uint8_t* data() { return pool; }
|
||||
const uint8_t* data() const { return pool; }
|
||||
unsigned size() const { return poolsize; }
|
||||
unsigned bytesize() const { return (poolsize >> 3) + ((poolsize & 7) > 0); }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0u;
|
||||
}
|
||||
|
||||
void resize(unsigned allocsize) {
|
||||
if(allocsize == poolsize) return;
|
||||
pool = (uint8_t*)realloc(pool, allocsize);
|
||||
poolsize = allocsize;
|
||||
}
|
||||
|
||||
bool operator[](unsigned offset) const {
|
||||
return pool[offset >> 3] & (0x80 >> (offset & 7));
|
||||
}
|
||||
|
||||
void set() {
|
||||
memset(pool, 0xff, (poolsize >> 3) + ((poolsize & 7) > 0));
|
||||
}
|
||||
|
||||
void set(unsigned offset) {
|
||||
pool[offset >> 3] |= 0x80 >> (offset & 7);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(pool, 0, (poolsize >> 3) + ((poolsize & 7) > 0));
|
||||
}
|
||||
|
||||
void clear(unsigned offset) {
|
||||
pool[offset >> 3] &=~0x80 >> (offset & 7);
|
||||
}
|
||||
|
||||
void set(unsigned offset, bool data) {
|
||||
data ? set(offset) : clear(offset);
|
||||
}
|
||||
|
||||
struct bit {
|
||||
bitarray &array;
|
||||
unsigned offset;
|
||||
operator bool() const { return const_cast<const bitarray&>(array)[offset]; }
|
||||
bit& operator=(bool data) { array.set(offset, data); return *this; }
|
||||
bit& operator=(const bit& data) { return operator=((bool)data); }
|
||||
bit(bitarray &array, unsigned offset) : array(array), offset(offset) {}
|
||||
};
|
||||
|
||||
bit operator[](unsigned offset) {
|
||||
return bit(*this, offset);
|
||||
}
|
||||
|
||||
bitarray() : pool(nullptr), poolsize(0u) {}
|
||||
bitarray(unsigned allocsize) {
|
||||
pool = (uint8_t*)malloc((allocsize >> 3) + ((allocsize & 7) > 0));
|
||||
poolsize = allocsize;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -24,7 +24,7 @@ protected:
|
|||
struct Node {
|
||||
unsigned offset;
|
||||
Node *next;
|
||||
inline Node() : offset(0), next(0) {}
|
||||
inline Node() : offset(0), next(nullptr) {}
|
||||
inline ~Node() { if(next) delete next; }
|
||||
};
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
#ifndef NALL_COMPOSITOR_HPP
|
||||
#define NALL_COMPOSITOR_HPP
|
||||
|
||||
#include <nall/intrinsics.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct compositor {
|
||||
inline static bool enabled();
|
||||
inline static bool enable(bool status);
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
|
||||
inline static Compositor detect();
|
||||
|
||||
inline static bool enabled_metacity();
|
||||
inline static bool enable_metacity(bool status);
|
||||
|
||||
inline static bool enabled_xfwm4();
|
||||
inline static bool enable_xfwm4(bool status);
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
|
||||
//Metacity
|
||||
|
||||
bool compositor::enabled_metacity() {
|
||||
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
|
||||
if(fp == 0) return false;
|
||||
|
||||
char buffer[512];
|
||||
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||
|
||||
if(!memcmp(buffer, "true", 4)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable_metacity(bool status) {
|
||||
FILE *fp;
|
||||
if(status) {
|
||||
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
|
||||
} else {
|
||||
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
|
||||
}
|
||||
if(fp == 0) return false;
|
||||
pclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Xfwm4
|
||||
|
||||
bool compositor::enabled_xfwm4() {
|
||||
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
|
||||
if(fp == 0) return false;
|
||||
|
||||
char buffer[512];
|
||||
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||
|
||||
if(!memcmp(buffer, "true", 4)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable_xfwm4(bool status) {
|
||||
FILE *fp;
|
||||
if(status) {
|
||||
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
|
||||
} else {
|
||||
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
|
||||
}
|
||||
if(fp == 0) return false;
|
||||
pclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
//General
|
||||
|
||||
compositor::Compositor compositor::detect() {
|
||||
Compositor result = Compositor::Unknown;
|
||||
|
||||
FILE *fp;
|
||||
char buffer[512];
|
||||
|
||||
fp = popen("pidof metacity", "r");
|
||||
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
|
||||
pclose(fp);
|
||||
|
||||
fp = popen("pidof xfwm4", "r");
|
||||
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
|
||||
pclose(fp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool compositor::enabled() {
|
||||
switch(detect()) {
|
||||
case Compositor::Metacity: return enabled_metacity();
|
||||
case Compositor::Xfwm4: return enabled_xfwm4();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
switch(detect()) {
|
||||
case Compositor::Metacity: return enable_metacity(status);
|
||||
case Compositor::Xfwm4: return enable_xfwm4(status);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
|
||||
bool compositor::enabled() {
|
||||
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||
if(module == 0) return false;
|
||||
|
||||
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
|
||||
if(pDwmIsCompositionEnabled == 0) return false;
|
||||
|
||||
BOOL result;
|
||||
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||
if(module == 0) return false;
|
||||
|
||||
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
|
||||
if(pDwmEnableComposition == 0) return false;
|
||||
|
||||
if(pDwmEnableComposition(status) != S_OK) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool compositor::enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -32,7 +32,7 @@ namespace nall {
|
|||
string desc;
|
||||
type_t type;
|
||||
|
||||
string get() const {
|
||||
inline string get() const {
|
||||
switch(type) {
|
||||
case boolean_t: return { *(bool*)data };
|
||||
case signed_t: return { *(signed*)data };
|
||||
|
@ -43,7 +43,7 @@ namespace nall {
|
|||
return "???";
|
||||
}
|
||||
|
||||
void set(string s) {
|
||||
inline void set(string s) {
|
||||
switch(type) {
|
||||
case boolean_t: *(bool*)data = (s == "true"); break;
|
||||
case signed_t: *(signed*)data = integer(s); break;
|
||||
|
@ -53,24 +53,27 @@ namespace nall {
|
|||
}
|
||||
}
|
||||
};
|
||||
linear_vector<item_t> list;
|
||||
vector<item_t> list;
|
||||
|
||||
template<typename T>
|
||||
void attach(T &data, const char *name, const char *desc = "") {
|
||||
unsigned n = list.size();
|
||||
list[n].data = (uintptr_t)&data;
|
||||
list[n].name = name;
|
||||
list[n].desc = desc;
|
||||
|
||||
if(configuration_traits::is_boolean<T>::value) list[n].type = boolean_t;
|
||||
else if(configuration_traits::is_signed<T>::value) list[n].type = signed_t;
|
||||
else if(configuration_traits::is_unsigned<T>::value) list[n].type = unsigned_t;
|
||||
else if(configuration_traits::is_double<T>::value) list[n].type = double_t;
|
||||
else if(configuration_traits::is_string<T>::value) list[n].type = string_t;
|
||||
else list[n].type = unknown_t;
|
||||
inline void append(T &data, const char *name, const char *desc = "") {
|
||||
item_t item = { (uintptr_t)&data, name, desc };
|
||||
if(configuration_traits::is_boolean<T>::value) item.type = boolean_t;
|
||||
else if(configuration_traits::is_signed<T>::value) item.type = signed_t;
|
||||
else if(configuration_traits::is_unsigned<T>::value) item.type = unsigned_t;
|
||||
else if(configuration_traits::is_double<T>::value) item.type = double_t;
|
||||
else if(configuration_traits::is_string<T>::value) item.type = string_t;
|
||||
else item.type = unknown_t;
|
||||
list.append(item);
|
||||
}
|
||||
|
||||
virtual bool load(const char *filename) {
|
||||
//deprecated
|
||||
template<typename T>
|
||||
inline void attach(T &data, const char *name, const char *desc = "") {
|
||||
append(data, name, desc);
|
||||
}
|
||||
|
||||
inline virtual bool load(const string &filename) {
|
||||
string data;
|
||||
if(data.readfile(filename) == true) {
|
||||
data.replace("\r", "");
|
||||
|
@ -100,7 +103,7 @@ namespace nall {
|
|||
}
|
||||
}
|
||||
|
||||
virtual bool save(const char *filename) const {
|
||||
inline virtual bool save(const string &filename) const {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write)) {
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
|
@ -1,11 +1,12 @@
|
|||
#ifndef NALL_DIRECTORY_HPP
|
||||
#define NALL_DIRECTORY_HPP
|
||||
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
|
@ -22,7 +23,7 @@ struct directory {
|
|||
static lstring contents(const string &pathname, const string &pattern = "*");
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
inline bool directory::exists(const string &pathname) {
|
||||
DWORD result = GetFileAttributes(utf16_t(pathname));
|
||||
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
||||
|
@ -55,8 +56,8 @@ struct directory {
|
|||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
foreach(name, list) name.append("/"); //must append after sorting
|
||||
if(list.size() > 0) list.sort();
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -82,14 +83,14 @@ struct directory {
|
|||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
if(list.size() > 0) list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#else
|
||||
|
@ -115,8 +116,8 @@ struct directory {
|
|||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
foreach(name, list) name.append("/"); //must append after sorting
|
||||
if(list.size() > 0) list.sort();
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -135,14 +136,14 @@ struct directory {
|
|||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
if(list.size() > 0) list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#endif
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
//dynamic linking support
|
||||
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
|
||||
#include <dlfcn.h>
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
@ -81,7 +81,7 @@ namespace nall {
|
|||
dlclose((void*)handle);
|
||||
handle = 0;
|
||||
}
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
inline bool library::open(const char *name, const char *path) {
|
||||
if(handle) close();
|
||||
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
|
|
@ -1,6 +1,11 @@
|
|||
#ifndef NALL_DSP_HPP
|
||||
#define NALL_DSP_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#ifdef __SSE__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#define NALL_DSP_INTERNAL_HPP
|
||||
#include <nall/dsp/core.hpp>
|
||||
#undef NALL_DSP_INTERNAL_HPP
|
|
@ -0,0 +1,51 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct Buffer {
|
||||
double **sample;
|
||||
uint16_t rdoffset;
|
||||
uint16_t wroffset;
|
||||
unsigned channels;
|
||||
|
||||
void setChannels(unsigned channels) {
|
||||
for(unsigned c = 0; c < this->channels; c++) {
|
||||
if(sample[c]) delete[] sample[c];
|
||||
}
|
||||
if(sample) delete[] sample;
|
||||
|
||||
this->channels = channels;
|
||||
if(channels == 0) return;
|
||||
|
||||
sample = new double*[channels];
|
||||
for(unsigned c = 0; c < channels; c++) {
|
||||
sample[c] = new double[65536]();
|
||||
}
|
||||
}
|
||||
|
||||
inline double& read(unsigned channel, signed offset = 0) {
|
||||
return sample[channel][(uint16_t)(rdoffset + offset)];
|
||||
}
|
||||
|
||||
inline double& write(unsigned channel, signed offset = 0) {
|
||||
return sample[channel][(uint16_t)(wroffset + offset)];
|
||||
}
|
||||
|
||||
inline void clear() {
|
||||
for(unsigned c = 0; c < channels; c++) {
|
||||
for(unsigned n = 0; n < 65536; n++) {
|
||||
sample[c][n] = 0;
|
||||
}
|
||||
}
|
||||
rdoffset = 0;
|
||||
wroffset = 0;
|
||||
}
|
||||
|
||||
Buffer() {
|
||||
channels = 0;
|
||||
}
|
||||
|
||||
~Buffer() {
|
||||
setChannels(0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,167 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
#include <math.h>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
//precision: can be float, double or long double
|
||||
#define real float
|
||||
|
||||
struct DSP;
|
||||
|
||||
struct Resampler {
|
||||
DSP &dsp;
|
||||
real frequency;
|
||||
|
||||
virtual void setFrequency() = 0;
|
||||
virtual void clear() = 0;
|
||||
virtual void sample() = 0;
|
||||
Resampler(DSP &dsp) : dsp(dsp) {}
|
||||
};
|
||||
|
||||
struct DSP {
|
||||
enum class ResampleEngine : unsigned {
|
||||
Nearest,
|
||||
Linear,
|
||||
Cosine,
|
||||
Cubic,
|
||||
Hermite,
|
||||
Average,
|
||||
Sinc,
|
||||
};
|
||||
|
||||
inline void setChannels(unsigned channels);
|
||||
inline void setPrecision(unsigned precision);
|
||||
inline void setFrequency(real frequency); //inputFrequency
|
||||
inline void setVolume(real volume);
|
||||
inline void setBalance(real balance);
|
||||
|
||||
inline void setResampler(ResampleEngine resamplingEngine);
|
||||
inline void setResamplerFrequency(real frequency); //outputFrequency
|
||||
|
||||
inline void sample(signed channel[]);
|
||||
inline bool pending();
|
||||
inline void read(signed channel[]);
|
||||
|
||||
inline void clear();
|
||||
inline DSP();
|
||||
inline ~DSP();
|
||||
|
||||
protected:
|
||||
friend class ResampleNearest;
|
||||
friend class ResampleLinear;
|
||||
friend class ResampleCosine;
|
||||
friend class ResampleCubic;
|
||||
friend class ResampleAverage;
|
||||
friend class ResampleHermite;
|
||||
friend class ResampleSinc;
|
||||
|
||||
struct Settings {
|
||||
unsigned channels;
|
||||
unsigned precision;
|
||||
real frequency;
|
||||
real volume;
|
||||
real balance;
|
||||
|
||||
//internal
|
||||
real intensity;
|
||||
real intensityInverse;
|
||||
} settings;
|
||||
|
||||
Resampler *resampler;
|
||||
inline void write(real channel[]);
|
||||
|
||||
#include "buffer.hpp"
|
||||
Buffer buffer;
|
||||
Buffer output;
|
||||
|
||||
inline void adjustVolume();
|
||||
inline void adjustBalance();
|
||||
inline signed clamp(const unsigned bits, const signed x);
|
||||
};
|
||||
|
||||
#include "resample/nearest.hpp"
|
||||
#include "resample/linear.hpp"
|
||||
#include "resample/cosine.hpp"
|
||||
#include "resample/cubic.hpp"
|
||||
#include "resample/hermite.hpp"
|
||||
#include "resample/average.hpp"
|
||||
#include "resample/sinc.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
void DSP::sample(signed channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
|
||||
}
|
||||
buffer.wroffset++;
|
||||
resampler->sample();
|
||||
}
|
||||
|
||||
bool DSP::pending() {
|
||||
return output.rdoffset != output.wroffset;
|
||||
}
|
||||
|
||||
void DSP::read(signed channel[]) {
|
||||
adjustVolume();
|
||||
adjustBalance();
|
||||
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
|
||||
}
|
||||
output.rdoffset++;
|
||||
}
|
||||
|
||||
void DSP::write(real channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) = channel[c];
|
||||
}
|
||||
output.wroffset++;
|
||||
}
|
||||
|
||||
void DSP::adjustVolume() {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.read(c) *= settings.volume;
|
||||
}
|
||||
}
|
||||
|
||||
void DSP::adjustBalance() {
|
||||
if(settings.channels != 2) return; //TODO: support > 2 channels
|
||||
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
|
||||
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
|
||||
}
|
||||
|
||||
signed DSP::clamp(const unsigned bits, const signed x) {
|
||||
const signed b = 1U << (bits - 1);
|
||||
const signed m = (1U << (bits - 1)) - 1;
|
||||
return (x > m) ? m : (x < -b) ? -b : x;
|
||||
}
|
||||
|
||||
void DSP::clear() {
|
||||
buffer.clear();
|
||||
output.clear();
|
||||
resampler->clear();
|
||||
}
|
||||
|
||||
DSP::DSP() {
|
||||
setResampler(ResampleEngine::Hermite);
|
||||
setResamplerFrequency(44100.0);
|
||||
|
||||
setChannels(2);
|
||||
setPrecision(16);
|
||||
setFrequency(44100.0);
|
||||
setVolume(1.0);
|
||||
setBalance(0.0);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
DSP::~DSP() {
|
||||
if(resampler) delete resampler;
|
||||
}
|
||||
|
||||
#undef real
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,72 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleAverage : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
inline void sampleLinear();
|
||||
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleAverage::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleAverage::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleAverage::sample() {
|
||||
//can only average if input frequency >= output frequency
|
||||
if(step < 1.0) return sampleLinear();
|
||||
|
||||
fraction += 1.0;
|
||||
|
||||
real scalar = 1.0;
|
||||
if(fraction > step) scalar = 1.0 - (fraction - step);
|
||||
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
|
||||
}
|
||||
|
||||
if(fraction >= step) {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) /= step;
|
||||
}
|
||||
dsp.output.wroffset++;
|
||||
|
||||
fraction -= step;
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
|
||||
}
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
}
|
||||
|
||||
void ResampleAverage::sampleLinear() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,44 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleCosine : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleCosine::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleCosine::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleCosine::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,50 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleCubic : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleCubic::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleCubic::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleCubic::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -3);
|
||||
real b = dsp.buffer.read(n, -2);
|
||||
real c = dsp.buffer.read(n, -1);
|
||||
real d = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
real A = d - c - a + b;
|
||||
real B = a - b - A;
|
||||
real C = c - a;
|
||||
real D = b;
|
||||
|
||||
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleHermite : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleHermite::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleHermite::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleHermite::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -3);
|
||||
real b = dsp.buffer.read(n, -2);
|
||||
real c = dsp.buffer.read(n, -1);
|
||||
real d = dsp.buffer.read(n, -0);
|
||||
|
||||
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||
|
||||
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
|
||||
mu1 = fraction;
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
a2 = mu3 - mu2;
|
||||
a3 = -2 * mu3 + 3 * mu2;
|
||||
|
||||
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,600 @@
|
|||
// If these types are changed to anything other than "float", you should comment out the SSE detection directives below
|
||||
// so that the SSE code is not used.
|
||||
|
||||
typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16
|
||||
typedef float resample_samp_t;
|
||||
|
||||
|
||||
// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE.
|
||||
#define RESAMPLE_SSEREGPARM
|
||||
|
||||
#if defined(__SSE__)
|
||||
#define SINCRESAMPLE_USE_SSE 1
|
||||
#ifndef __x86_64__
|
||||
#undef RESAMPLE_SSEREGPARM
|
||||
#define RESAMPLE_SSEREGPARM __attribute__((sseregparm))
|
||||
#endif
|
||||
#else
|
||||
// TODO: altivec here
|
||||
#endif
|
||||
|
||||
namespace ResampleUtility
|
||||
{
|
||||
inline void kaiser_window(double* io, int count, double beta);
|
||||
inline void gen_sinc(double* out, int size, double cutoff, double kaiser);
|
||||
inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser);
|
||||
inline void normalize(double* io, int size, double gain = 1.0);
|
||||
|
||||
inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2
|
||||
}
|
||||
|
||||
class SincResampleHR
|
||||
{
|
||||
private:
|
||||
|
||||
inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d);
|
||||
|
||||
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||
inline bool output_avail(void);
|
||||
|
||||
private:
|
||||
|
||||
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count);
|
||||
|
||||
unsigned ratio;
|
||||
unsigned num_convolutions;
|
||||
|
||||
resample_coeff_t *coeffs;
|
||||
std::vector<unsigned char> coeffs_mem;
|
||||
|
||||
// second half of ringbuffer should be copy of first half.
|
||||
resample_samp_t *rb;
|
||||
std::vector<unsigned char> rb_mem;
|
||||
|
||||
signed rb_readpos;
|
||||
signed rb_writepos;
|
||||
signed rb_in;
|
||||
signed rb_eff_size;
|
||||
|
||||
friend class SincResample;
|
||||
};
|
||||
|
||||
class SincResample
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
QUALITY_LOW = 0,
|
||||
QUALITY_MEDIUM = 2,
|
||||
QUALITY_HIGH = 4
|
||||
};
|
||||
|
||||
inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH);
|
||||
|
||||
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||
inline bool output_avail(void);
|
||||
|
||||
private:
|
||||
|
||||
inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min);
|
||||
|
||||
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM;
|
||||
|
||||
unsigned num_convolutions;
|
||||
unsigned num_phases;
|
||||
|
||||
unsigned step_int;
|
||||
double step_fract;
|
||||
|
||||
double input_pos_fract;
|
||||
|
||||
|
||||
std::vector<resample_coeff_t *> coeffs; // Pointers into coeff_mem.
|
||||
std::vector<unsigned char> coeff_mem;
|
||||
|
||||
|
||||
std::vector<resample_samp_t> rb; // second half should be copy of first half.
|
||||
signed rb_readpos;
|
||||
signed rb_writepos;
|
||||
signed rb_in;
|
||||
|
||||
bool hr_used;
|
||||
SincResampleHR hr;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Code:
|
||||
//
|
||||
//#include "resample.hpp"
|
||||
|
||||
#if 0
|
||||
namespace bit
|
||||
{
|
||||
inline unsigned round(unsigned x) {
|
||||
if((x & (x - 1)) == 0) return x;
|
||||
while(x & (x - 1)) x &= x - 1;
|
||||
return x << 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d)
|
||||
{
|
||||
const unsigned align_boundary = 16;
|
||||
std::vector<double> coeffs_tmp;
|
||||
double cutoff; // 1.0 = f/2
|
||||
|
||||
ratio = ratio_arg;
|
||||
|
||||
//num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even
|
||||
num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1);
|
||||
|
||||
cutoff = (1.0 / ratio) - (d / num_convolutions);
|
||||
|
||||
//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff);
|
||||
assert(num_convolutions > ratio);
|
||||
|
||||
|
||||
// Generate windowed sinc of POWER
|
||||
coeffs_tmp.resize(num_convolutions);
|
||||
//ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||
ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||
ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions);
|
||||
|
||||
// Copy from coeffs_tmp to coeffs~
|
||||
// We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really
|
||||
// exist are allocated, zero'd mem.
|
||||
|
||||
coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1));
|
||||
coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary);
|
||||
|
||||
|
||||
for(unsigned i = 0; i < num_convolutions; i++)
|
||||
coeffs[i] = coeffs_tmp[i];
|
||||
|
||||
rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1;
|
||||
rb_readpos = 0;
|
||||
rb_writepos = 0;
|
||||
rb_in = 0;
|
||||
|
||||
rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1));
|
||||
rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary);
|
||||
}
|
||||
|
||||
|
||||
inline bool SincResampleHR::output_avail(void)
|
||||
{
|
||||
return(rb_in >= (signed)num_convolutions);
|
||||
}
|
||||
|
||||
inline void SincResampleHR::write(resample_samp_t sample)
|
||||
{
|
||||
assert(!output_avail());
|
||||
|
||||
rb[rb_writepos] = sample;
|
||||
rb[rb_writepos + rb_eff_size] = sample;
|
||||
rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1);
|
||||
rb_in++;
|
||||
}
|
||||
|
||||
resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count)
|
||||
{
|
||||
#if SINCRESAMPLE_USE_SSE
|
||||
__m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
|
||||
resample_samp_t accum;
|
||||
|
||||
for(unsigned c = 0; c < count; c += 8)
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
__m128 co[2];
|
||||
__m128 w[2];
|
||||
|
||||
co[i] = _mm_load_ps(&coeff[c + i * 4]);
|
||||
w[i] = _mm_load_ps(&wave[c + i * 4]);
|
||||
|
||||
w[i] = _mm_mul_ps(w[i], co[i]);
|
||||
|
||||
accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3]));
|
||||
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||
|
||||
_mm_store_ss(&accum, accum_vec);
|
||||
|
||||
return accum;
|
||||
#else
|
||||
resample_samp_t accum[4] = { 0, 0, 0, 0 };
|
||||
|
||||
for(unsigned c = 0; c < count; c+= 4)
|
||||
{
|
||||
accum[0] += wave[c + 0] * coeff[c + 0];
|
||||
accum[1] += wave[c + 1] * coeff[c + 1];
|
||||
accum[2] += wave[c + 2] * coeff[c + 2];
|
||||
accum[3] += wave[c + 3] * coeff[c + 3];
|
||||
}
|
||||
|
||||
return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may...
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
resample_samp_t SincResampleHR::read(void)
|
||||
{
|
||||
assert(output_avail());
|
||||
resample_samp_t ret;
|
||||
|
||||
ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions);
|
||||
|
||||
rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1);
|
||||
rb_in -= ratio;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality)
|
||||
{
|
||||
const struct
|
||||
{
|
||||
double beta;
|
||||
double d;
|
||||
unsigned pn_nume;
|
||||
unsigned phases_min;
|
||||
} qtab[5] =
|
||||
{
|
||||
{ 5.658, 3.62, 4096, 4 },
|
||||
{ 6.764, 4.32, 8192, 4 },
|
||||
{ 7.865, 5.0, 16384, 8 },
|
||||
{ 8.960, 5.7, 32768, 16 },
|
||||
{ 10.056, 6.4, 65536, 32 }
|
||||
};
|
||||
|
||||
// Sanity checks
|
||||
assert(ceil(input_rate) > 0);
|
||||
assert(ceil(output_rate) > 0);
|
||||
assert(ceil(input_rate / output_rate) <= 1024);
|
||||
assert(ceil(output_rate / input_rate) <= 1024);
|
||||
|
||||
// The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when
|
||||
// upsampling.
|
||||
assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96);
|
||||
assert(quality >= 0 && quality <= 4);
|
||||
|
||||
hr_used = false;
|
||||
|
||||
#if 1
|
||||
// Round down to the nearest multiple of 4(so wave buffer remains aligned)
|
||||
// It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2
|
||||
// aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately
|
||||
// to virtually eliminate over-attenuation.
|
||||
unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3;
|
||||
|
||||
if(ioratio_rd >= 8)
|
||||
{
|
||||
hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4);
|
||||
hr_used = true;
|
||||
|
||||
input_rate /= ioratio_rd;
|
||||
}
|
||||
#endif
|
||||
|
||||
Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min);
|
||||
}
|
||||
|
||||
void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min)
|
||||
{
|
||||
const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2.
|
||||
const unsigned max_mult_minus1 = (max_mult_atatime - 1);
|
||||
const unsigned conv_alignment_bytes = 16; // must be power of 2
|
||||
const double input_to_output_ratio = input_rate / output_rate;
|
||||
const double output_to_input_ratio = output_rate / input_rate;
|
||||
double cutoff; // 1.0 = input_rate / 2
|
||||
std::vector<double> coeff_init_buffer;
|
||||
|
||||
// Round up num_convolutions to be even.
|
||||
if(output_rate > input_rate)
|
||||
num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1;
|
||||
else
|
||||
num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1;
|
||||
|
||||
if(output_rate > input_rate) // Upsampling
|
||||
cutoff = desired_bandwidth;
|
||||
else // Downsampling
|
||||
cutoff = output_to_input_ratio * desired_bandwidth;
|
||||
|
||||
// Round up to be even.
|
||||
num_phases = (std::max<unsigned>(pn_nume / num_convolutions, phases_min) + 1) &~1;
|
||||
|
||||
// Adjust cutoff to account for the multiple phases.
|
||||
cutoff = cutoff / num_phases;
|
||||
|
||||
assert((num_convolutions & 1) == 0);
|
||||
assert((num_phases & 1) == 0);
|
||||
|
||||
// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases,
|
||||
// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes));
|
||||
|
||||
coeff_init_buffer.resize(num_phases * num_convolutions);
|
||||
|
||||
coeffs.resize(num_phases + 1 + 1);
|
||||
|
||||
coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes);
|
||||
|
||||
// Assign aligned pointers into coeff_mem
|
||||
{
|
||||
resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes);
|
||||
|
||||
for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++)
|
||||
{
|
||||
coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase);
|
||||
}
|
||||
}
|
||||
|
||||
ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta);
|
||||
ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases);
|
||||
|
||||
// Reorder coefficients to allow for more efficient convolution.
|
||||
for(int phase = -1; phase < ((int)num_phases + 1); phase++)
|
||||
{
|
||||
for(int conv = 0; conv < (int)num_convolutions; conv++)
|
||||
{
|
||||
double coeff;
|
||||
|
||||
if(phase == -1 && conv == 0)
|
||||
coeff = 0;
|
||||
else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1))
|
||||
coeff = 0;
|
||||
else
|
||||
coeff = coeff_init_buffer[conv * num_phases + phase];
|
||||
|
||||
coeffs[phase + 1][conv] = coeff;
|
||||
}
|
||||
}
|
||||
|
||||
// Free a bit of mem
|
||||
coeff_init_buffer.resize(0);
|
||||
|
||||
step_int = floor(input_to_output_ratio);
|
||||
step_fract = input_to_output_ratio - step_int;
|
||||
|
||||
input_pos_fract = 0;
|
||||
|
||||
// Do NOT use rb.size() later in the code, since it'll include the padding.
|
||||
// We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time
|
||||
// rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half,
|
||||
// since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to
|
||||
// prevent illegal memory accesses.
|
||||
rb.resize(num_convolutions * 2 + max_mult_minus1);
|
||||
|
||||
rb_readpos = 0;
|
||||
rb_writepos = 0;
|
||||
rb_in = 0;
|
||||
}
|
||||
|
||||
resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count)
|
||||
{
|
||||
resample_samp_t accum = 0;
|
||||
#if SINCRESAMPLE_USE_SSE
|
||||
__m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
__m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
|
||||
for(unsigned c = 0; c < count; c += 8) //8) //4)
|
||||
{
|
||||
__m128 coeff_a[2];
|
||||
__m128 coeff_b[2];
|
||||
__m128 w[2];
|
||||
__m128 result_a[2], result_b[2];
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]);
|
||||
coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]);
|
||||
w[i] = _mm_loadu_ps(&wave[c + (i * 4)]);
|
||||
|
||||
result_a[i] = _mm_mul_ps(coeff_a[i], w[i]);
|
||||
result_b[i] = _mm_mul_ps(coeff_b[i], w[i]);
|
||||
|
||||
accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]);
|
||||
accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__m128 accum_vec, av_a, av_b;
|
||||
__m128 mult_a_vec = _mm_set1_ps(1.0 - ffract);
|
||||
__m128 mult_b_vec = _mm_set1_ps(ffract);
|
||||
|
||||
av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1]));
|
||||
av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1]));
|
||||
|
||||
accum_vec = _mm_add_ps(av_a, av_b);
|
||||
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||
|
||||
_mm_store_ss(&accum, accum_vec);
|
||||
#else
|
||||
resample_coeff_t mult_a = 1.0 - ffract;
|
||||
resample_coeff_t mult_b = ffract;
|
||||
|
||||
for(unsigned c = 0; c < count; c += 4)
|
||||
{
|
||||
accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b);
|
||||
accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b);
|
||||
accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b);
|
||||
accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b);
|
||||
}
|
||||
#endif
|
||||
|
||||
return accum;
|
||||
}
|
||||
|
||||
inline bool SincResample::output_avail(void)
|
||||
{
|
||||
return(rb_in >= (int)num_convolutions);
|
||||
}
|
||||
|
||||
resample_samp_t SincResample::read(void)
|
||||
{
|
||||
assert(output_avail());
|
||||
double phase = input_pos_fract * num_phases - 0.5;
|
||||
signed phase_int = (signed)floor(phase);
|
||||
double phase_fract = phase - phase_int;
|
||||
unsigned phase_a = num_phases - 1 - phase_int;
|
||||
unsigned phase_b = phase_a - 1;
|
||||
resample_samp_t ret;
|
||||
|
||||
ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions);
|
||||
|
||||
unsigned int_increment = step_int;
|
||||
|
||||
input_pos_fract += step_fract;
|
||||
int_increment += floor(input_pos_fract);
|
||||
input_pos_fract -= floor(input_pos_fract);
|
||||
|
||||
rb_readpos = (rb_readpos + int_increment) % num_convolutions;
|
||||
rb_in -= int_increment;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void SincResample::write(resample_samp_t sample)
|
||||
{
|
||||
assert(!output_avail());
|
||||
|
||||
if(hr_used)
|
||||
{
|
||||
hr.write(sample);
|
||||
|
||||
if(hr.output_avail())
|
||||
{
|
||||
sample = hr.read();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rb[rb_writepos + 0 * num_convolutions] = sample;
|
||||
rb[rb_writepos + 1 * num_convolutions] = sample;
|
||||
rb_writepos = (rb_writepos + 1) % num_convolutions;
|
||||
rb_in++;
|
||||
}
|
||||
|
||||
void ResampleUtility::kaiser_window( double* io, int count, double beta)
|
||||
{
|
||||
int const accuracy = 24; //16; //12;
|
||||
|
||||
double* end = io + count;
|
||||
|
||||
double beta2 = beta * beta * (double) -0.25;
|
||||
double to_fract = beta2 / ((double) count * count);
|
||||
double i = 0;
|
||||
double rescale = 0; // Doesn't need an initializer, to shut up gcc
|
||||
|
||||
for ( ; io < end; ++io, i += 1 )
|
||||
{
|
||||
double x = i * i * to_fract - beta2;
|
||||
double u = x;
|
||||
double k = x + 1;
|
||||
|
||||
double n = 2;
|
||||
do
|
||||
{
|
||||
u *= x / (n * n);
|
||||
n += 1;
|
||||
k += u;
|
||||
}
|
||||
while ( k <= u * (1 << accuracy) );
|
||||
|
||||
if ( !i )
|
||||
rescale = 1 / k; // otherwise values get large
|
||||
|
||||
*io *= k * rescale;
|
||||
}
|
||||
}
|
||||
|
||||
void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser)
|
||||
{
|
||||
assert( size % 2 == 0 ); // size must be even
|
||||
|
||||
int const half_size = size / 2;
|
||||
double* const mid = &out [half_size];
|
||||
|
||||
// Generate right half of sinc
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
{
|
||||
double angle = (i * 2 + 1) * (M_PI / 2);
|
||||
mid [i] = sin( angle * cutoff ) / angle;
|
||||
}
|
||||
|
||||
kaiser_window( mid, half_size, kaiser );
|
||||
|
||||
// Mirror for left half
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
out [i] = mid [half_size - 1 - i];
|
||||
}
|
||||
|
||||
void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser)
|
||||
{
|
||||
assert( size % 2 == 1); // size must be odd
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
if(i == (size / 2))
|
||||
out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||
else
|
||||
out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||
|
||||
// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1));
|
||||
//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1));
|
||||
|
||||
// printf("%d %f\n", i, out[i]);
|
||||
}
|
||||
|
||||
kaiser_window(&out[size / 2], size / 2 + 1, kaiser);
|
||||
|
||||
// Mirror for left half
|
||||
for ( int i = 0; i < size / 2; i++ )
|
||||
out [i] = out [size - 1 - i];
|
||||
|
||||
}
|
||||
|
||||
void ResampleUtility::normalize(double* io, int size, double gain)
|
||||
{
|
||||
double sum = 0;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
sum += io [i];
|
||||
|
||||
double scale = gain / sum;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
io [i] *= scale;
|
||||
}
|
||||
|
||||
void* ResampleUtility::make_aligned(void* ptr, unsigned boundary)
|
||||
{
|
||||
unsigned char* null_ptr = (unsigned char *)NULL;
|
||||
unsigned char* uc_ptr = (unsigned char *)ptr;
|
||||
|
||||
uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1);
|
||||
|
||||
//while((uc_ptr - null_ptr) & (boundary - 1))
|
||||
// uc_ptr++;
|
||||
|
||||
//printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr);
|
||||
|
||||
assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr));
|
||||
|
||||
return uc_ptr;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleLinear : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleLinear::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleLinear::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleLinear::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleNearest : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleNearest(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleNearest::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleNearest::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleNearest::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = mu < 0.5 ? a : b;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,54 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
#include "lib/sinc.hpp"
|
||||
|
||||
struct ResampleSinc : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
inline ResampleSinc(DSP &dsp);
|
||||
|
||||
private:
|
||||
inline void remakeSinc();
|
||||
SincResample *sinc_resampler[8];
|
||||
};
|
||||
|
||||
void ResampleSinc::setFrequency() {
|
||||
remakeSinc();
|
||||
}
|
||||
|
||||
void ResampleSinc::clear() {
|
||||
remakeSinc();
|
||||
}
|
||||
|
||||
void ResampleSinc::sample() {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
sinc_resampler[c]->write(dsp.buffer.read(c));
|
||||
}
|
||||
|
||||
if(sinc_resampler[0]->output_avail()) {
|
||||
do {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) = sinc_resampler[c]->read();
|
||||
}
|
||||
dsp.output.wroffset++;
|
||||
} while(sinc_resampler[0]->output_avail());
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
}
|
||||
|
||||
ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) {
|
||||
for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0;
|
||||
}
|
||||
|
||||
void ResampleSinc::remakeSinc() {
|
||||
assert(dsp.settings.channels < 8);
|
||||
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
if(sinc_resampler[c]) delete sinc_resampler[c];
|
||||
sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,50 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::setChannels(unsigned channels) {
|
||||
assert(channels > 0);
|
||||
buffer.setChannels(channels);
|
||||
output.setChannels(channels);
|
||||
settings.channels = channels;
|
||||
}
|
||||
|
||||
void DSP::setPrecision(unsigned precision) {
|
||||
settings.precision = precision;
|
||||
settings.intensity = 1 << (settings.precision - 1);
|
||||
settings.intensityInverse = 1.0 / settings.intensity;
|
||||
}
|
||||
|
||||
void DSP::setFrequency(real frequency) {
|
||||
settings.frequency = frequency;
|
||||
resampler->setFrequency();
|
||||
}
|
||||
|
||||
void DSP::setVolume(real volume) {
|
||||
settings.volume = volume;
|
||||
}
|
||||
|
||||
void DSP::setBalance(real balance) {
|
||||
settings.balance = balance;
|
||||
}
|
||||
|
||||
void DSP::setResampler(ResampleEngine engine) {
|
||||
if(resampler) delete resampler;
|
||||
|
||||
switch(engine) {
|
||||
case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return;
|
||||
case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return;
|
||||
case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return;
|
||||
case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return;
|
||||
case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return;
|
||||
case ResampleEngine::Average: resampler = new ResampleAverage(*this); return;
|
||||
case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void DSP::setResamplerFrequency(real frequency) {
|
||||
resampler->frequency = frequency;
|
||||
resampler->setFrequency();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef NALL_ENDIAN_HPP
|
||||
#define NALL_ENDIAN_HPP
|
||||
|
||||
#if !defined(ARCH_MSB)
|
||||
#include <nall/intrinsics.hpp>
|
||||
|
||||
#if defined(ENDIAN_LSB)
|
||||
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
||||
#define order_lsb2(a,b) a,b
|
||||
#define order_lsb3(a,b,c) a,b,c
|
||||
|
@ -17,7 +19,7 @@
|
|||
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||
#else
|
||||
#elif defined(ENDIAN_MSB)
|
||||
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
||||
#define order_lsb2(a,b) b,a
|
||||
#define order_lsb3(a,b,c) c,b,a
|
||||
|
@ -33,6 +35,8 @@
|
|||
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||
#else
|
||||
#error "Unknown endian. Please specify in nall/intrinsics.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -23,6 +23,7 @@ namespace nall {
|
|||
enum class time : unsigned { create, modify, access };
|
||||
|
||||
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
|
||||
data = 0;
|
||||
file fp;
|
||||
if(fp.open(filename, mode::read) == false) return false;
|
||||
size = fp.size();
|
||||
|
@ -133,12 +134,12 @@ namespace nall {
|
|||
file_offset = req_offset;
|
||||
}
|
||||
|
||||
int offset() {
|
||||
int offset() const {
|
||||
if(!fp) return -1; //file not open
|
||||
return file_offset;
|
||||
}
|
||||
|
||||
int size() {
|
||||
int size() const {
|
||||
if(!fp) return -1; //file not open
|
||||
return file_size;
|
||||
}
|
||||
|
@ -193,7 +194,7 @@ namespace nall {
|
|||
}
|
||||
}
|
||||
|
||||
bool open() {
|
||||
bool open() const {
|
||||
return fp;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef NALL_FILEMAP_HPP
|
||||
#define NALL_FILEMAP_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
|
|
@ -36,19 +36,19 @@ namespace nall {
|
|||
public:
|
||||
operator bool() const { return callback; }
|
||||
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
|
||||
void reset() { if(callback) { delete callback; callback = 0; } }
|
||||
void reset() { if(callback) { delete callback; callback = nullptr; } }
|
||||
|
||||
function& operator=(const function &source) {
|
||||
if(this != &source) {
|
||||
if(callback) { delete callback; callback = 0; }
|
||||
if(callback) { delete callback; callback = nullptr; }
|
||||
if(source.callback) callback = source.callback->copy();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
function(const function &source) : callback(0) { operator=(source); }
|
||||
function() : callback(0) {}
|
||||
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
|
||||
function(const function &source) : callback(nullptr) { operator=(source); }
|
||||
function() : callback(nullptr) {}
|
||||
function(void *function) : callback(nullptr) { if(function) callback = new global((R (*)(P...))function); }
|
||||
function(R (*function)(P...)) { callback = new global(function); }
|
||||
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
|
||||
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }
|
|
@ -1,11 +1,10 @@
|
|||
#ifndef NALL_GAMEBOY_CARTRIDGE_HPP
|
||||
#define NALL_GAMEBOY_CARTRIDGE_HPP
|
||||
#ifndef NALL_GB_CARTRIDGE_HPP
|
||||
#define NALL_GB_CARTRIDGE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
class GameBoyCartridge {
|
||||
public:
|
||||
string xml;
|
||||
struct GameBoyCartridge {
|
||||
string markup;
|
||||
inline GameBoyCartridge(uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
|
@ -22,7 +21,7 @@ public:
|
|||
};
|
||||
|
||||
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
markup = "";
|
||||
if(romsize < 0x4000) return;
|
||||
|
||||
info.mapper = "unknown";
|
||||
|
@ -100,18 +99,12 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
|||
|
||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||
|
||||
xml.append("<cartridge mapper='", info.mapper, "'");
|
||||
if(info.rtc) xml.append(" rtc='true'");
|
||||
if(info.rumble) xml.append(" rumble='true'");
|
||||
xml.append(">\n");
|
||||
|
||||
xml.append(" <rom size='", hex(romsize), "'/>\n"); //TODO: trust/check info.romsize?
|
||||
|
||||
if(info.ramsize > 0)
|
||||
xml.append(" <ram size='", hex(info.ramsize), "' battery='", info.battery, "'/>\n");
|
||||
|
||||
xml.append("</cartridge>\n");
|
||||
xml.transform("'", "\"");
|
||||
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||
markup.append("<cartridge mapper='", info.mapper, "' rtc='", info.rtc, "' rumble='", info.rumble, "'>\n");
|
||||
markup.append(" <rom size='0x", hex(romsize), "'/>\n");
|
||||
if(info.ramsize > 0) markup.append(" <ram size='0x", hex(info.ramsize), "' nonvolatile='", info.battery, "'/>\n");
|
||||
markup.append("</cartridge>\n");
|
||||
markup.transform("'", "\"");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef NALL_GBA_CARTRIDGE_HPP
|
||||
#define NALL_GBA_CARTRIDGE_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct GameBoyAdvanceCartridge {
|
||||
string markup;
|
||||
string identifiers;
|
||||
inline GameBoyAdvanceCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned size) {
|
||||
struct Identifier {
|
||||
string name;
|
||||
unsigned size;
|
||||
};
|
||||
vector<Identifier> idlist;
|
||||
idlist.append({"SRAM_V", 6});
|
||||
idlist.append({"SRAM_F_V", 8});
|
||||
idlist.append({"EEPROM_V", 8});
|
||||
idlist.append({"FLASH_V", 7});
|
||||
idlist.append({"FLASH512_V", 10});
|
||||
idlist.append({"FLASH1M_V", 9});
|
||||
|
||||
lstring list;
|
||||
for(auto &id : idlist) {
|
||||
for(signed n = 0; n < size - 16; n++) {
|
||||
if(!memcmp(data + n, (const char*)id.name, id.size)) {
|
||||
const char *p = (const char*)data + n + id.size;
|
||||
if(p[0] >= '0' && p[0] <= '9'
|
||||
&& p[1] >= '0' && p[1] <= '9'
|
||||
&& p[2] >= '0' && p[2] <= '9'
|
||||
) {
|
||||
char text[16];
|
||||
memcpy(text, data + n, id.size + 3);
|
||||
text[id.size + 3] = 0;
|
||||
list.appendonce(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
identifiers = list.concatenate(",");
|
||||
|
||||
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
|
||||
markup.append(" <rom size='", size, "'/>\n");
|
||||
if(0);
|
||||
else if(identifiers.beginswith("SRAM_V" )) markup.append(" <ram type='SRAM' size='32768'/>\n");
|
||||
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" <ram type='FRAM' size='32768'/>\n");
|
||||
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" <ram type='EEPROM' size='0'/>\n");
|
||||
else if(identifiers.beginswith("FLASH_V" )) markup.append(" <ram type='FlashROM' size='65536'/>\n");
|
||||
else if(identifiers.beginswith("FLASH512_V")) markup.append(" <ram type='FlashROM' size='65536'/>\n");
|
||||
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" <ram type='FlashROM' size='131072'/>\n");
|
||||
if(identifiers.empty() == false) markup.append(" <!-- detected: ", identifiers, " -->\n");
|
||||
|
||||
markup.append("</cartridge>\n");
|
||||
markup.transform("'", "\"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -11,11 +11,11 @@ struct gzip {
|
|||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
bool decompress(const string &filename);
|
||||
bool decompress(const uint8_t *data, unsigned size);
|
||||
inline bool decompress(const string &filename);
|
||||
inline bool decompress(const uint8_t *data, unsigned size);
|
||||
|
||||
gzip();
|
||||
~gzip();
|
||||
inline gzip();
|
||||
inline ~gzip();
|
||||
};
|
||||
|
||||
bool gzip::decompress(const string &filename) {
|
||||
|
@ -75,7 +75,7 @@ bool gzip::decompress(const uint8_t *data, unsigned size) {
|
|||
return inflate(this->data, this->size, data + p, size - p - 8);
|
||||
}
|
||||
|
||||
gzip::gzip() : data(0) {
|
||||
gzip::gzip() : data(nullptr) {
|
||||
}
|
||||
|
||||
gzip::~gzip() {
|
|
@ -117,7 +117,7 @@ struct http {
|
|||
}
|
||||
}
|
||||
} else if(auto position = header.iposition("\r\nContent-Length: ")) {
|
||||
unsigned length = decimal((const char*)header + position() + 16);
|
||||
unsigned length = decimal((const char*)header + position() + 18);
|
||||
while(length) {
|
||||
char buffer[256];
|
||||
int packetlength = recv(serversocket, buffer, min(256, length), 0);
|
|
@ -0,0 +1,465 @@
|
|||
#ifndef NALL_IMAGE_HPP
|
||||
#define NALL_IMAGE_HPP
|
||||
|
||||
#include <nall/bmp.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/interpolation.hpp>
|
||||
#include <nall/png.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct image {
|
||||
uint8_t *data;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned pitch;
|
||||
|
||||
bool endian; //0 = little, 1 = big
|
||||
unsigned depth;
|
||||
unsigned stride;
|
||||
|
||||
struct Channel {
|
||||
uint64_t mask;
|
||||
unsigned depth;
|
||||
unsigned shift;
|
||||
} alpha, red, green, blue;
|
||||
|
||||
typedef double (*interpolation)(double, double, double, double, double);
|
||||
static inline unsigned bitDepth(uint64_t color);
|
||||
static inline unsigned bitShift(uint64_t color);
|
||||
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
|
||||
|
||||
inline image& operator=(const image &source);
|
||||
inline image& operator=(image &&source);
|
||||
inline image(const image &source);
|
||||
inline image(image &&source);
|
||||
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||
inline image();
|
||||
inline ~image();
|
||||
|
||||
inline uint64_t read(const uint8_t *data) const;
|
||||
inline void write(uint8_t *data, uint64_t value) const;
|
||||
|
||||
inline void free();
|
||||
inline void allocate(unsigned width, unsigned height);
|
||||
inline void clear(uint64_t color);
|
||||
inline bool load(const string &filename);
|
||||
//inline bool loadBMP(const uint8_t *data, unsigned size);
|
||||
inline bool loadPNG(const uint8_t *data, unsigned size);
|
||||
inline void scale(unsigned width, unsigned height, interpolation op);
|
||||
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||
inline void alphaBlend(uint64_t alphaColor);
|
||||
|
||||
protected:
|
||||
inline uint64_t interpolate(double mu, const uint64_t *s, interpolation op);
|
||||
inline void scaleX(unsigned width, interpolation op);
|
||||
inline void scaleY(unsigned height, interpolation op);
|
||||
inline bool loadBMP(const string &filename);
|
||||
inline bool loadPNG(const string &filename);
|
||||
};
|
||||
|
||||
//static
|
||||
|
||||
unsigned image::bitDepth(uint64_t color) {
|
||||
unsigned depth = 0;
|
||||
if(color) while((color & 1) == 0) color >>= 1;
|
||||
while((color & 1) == 1) { color >>= 1; depth++; }
|
||||
return depth;
|
||||
}
|
||||
|
||||
unsigned image::bitShift(uint64_t color) {
|
||||
unsigned shift = 0;
|
||||
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
|
||||
return shift;
|
||||
}
|
||||
|
||||
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
|
||||
while(sourceDepth < targetDepth) {
|
||||
color = (color << sourceDepth) | color;
|
||||
sourceDepth += sourceDepth;
|
||||
}
|
||||
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
|
||||
return color;
|
||||
}
|
||||
|
||||
//public
|
||||
|
||||
image& image::operator=(const image &source) {
|
||||
free();
|
||||
|
||||
width = source.width;
|
||||
height = source.height;
|
||||
pitch = source.pitch;
|
||||
|
||||
endian = source.endian;
|
||||
stride = source.stride;
|
||||
|
||||
alpha = source.alpha;
|
||||
red = source.red;
|
||||
green = source.green;
|
||||
blue = source.blue;
|
||||
|
||||
data = new uint8_t[width * height * stride];
|
||||
memcpy(data, source.data, width * height * stride);
|
||||
return *this;
|
||||
}
|
||||
|
||||
image& image::operator=(image &&source) {
|
||||
width = source.width;
|
||||
height = source.height;
|
||||
pitch = source.pitch;
|
||||
|
||||
endian = source.endian;
|
||||
stride = source.stride;
|
||||
|
||||
alpha = source.alpha;
|
||||
red = source.red;
|
||||
green = source.green;
|
||||
blue = source.blue;
|
||||
|
||||
data = source.data;
|
||||
source.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
image::image(const image &source) : data(nullptr) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
image::image(image &&source) : data(nullptr) {
|
||||
operator=(std::forward<image>(source));
|
||||
}
|
||||
|
||||
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) : data(nullptr) {
|
||||
width = 0, height = 0, pitch = 0;
|
||||
|
||||
this->endian = endian;
|
||||
this->depth = depth;
|
||||
this->stride = (depth / 8) + ((depth & 7) > 0);
|
||||
|
||||
alpha.mask = alphaMask, red.mask = redMask, green.mask = greenMask, blue.mask = blueMask;
|
||||
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||
}
|
||||
|
||||
image::image() : data(nullptr) {
|
||||
width = 0, height = 0, pitch = 0;
|
||||
|
||||
this->endian = 0;
|
||||
this->depth = 32;
|
||||
this->stride = 4;
|
||||
|
||||
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
|
||||
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||
}
|
||||
|
||||
image::~image() {
|
||||
free();
|
||||
}
|
||||
|
||||
uint64_t image::read(const uint8_t *data) const {
|
||||
uint64_t result = 0;
|
||||
if(endian == 0) {
|
||||
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
|
||||
} else {
|
||||
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void image::write(uint8_t *data, uint64_t value) const {
|
||||
if(endian == 0) {
|
||||
for(signed n = 0; n < stride; n++) { data[n] = value; value >>= 8; }
|
||||
} else {
|
||||
for(signed n = stride - 1; n >= 0; n--) { data[n] = value; value >>= 8; }
|
||||
}
|
||||
}
|
||||
|
||||
void image::free() {
|
||||
if(data) delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
void image::allocate(unsigned width, unsigned height) {
|
||||
if(data != nullptr && this->width == width && this->height == height) return;
|
||||
free();
|
||||
data = new uint8_t[width * height * stride]();
|
||||
pitch = width * stride;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
void image::clear(uint64_t color) {
|
||||
uint8_t *dp = data;
|
||||
for(unsigned n = 0; n < width * height; n++) {
|
||||
write(dp, color);
|
||||
dp += stride;
|
||||
}
|
||||
}
|
||||
|
||||
bool image::load(const string &filename) {
|
||||
if(loadBMP(filename) == true) return true;
|
||||
if(loadPNG(filename) == true) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void image::scale(unsigned outputWidth, unsigned outputHeight, interpolation op) {
|
||||
if(width != outputWidth) scaleX(outputWidth, op);
|
||||
if(height != outputHeight) scaleY(outputHeight, op);
|
||||
}
|
||||
|
||||
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
|
||||
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
|
||||
output.allocate(width, height);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t *dp = output.data + output.pitch * y;
|
||||
uint8_t *sp = data + pitch * y;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint64_t color = read(sp);
|
||||
sp += stride;
|
||||
|
||||
uint64_t a = (color & alpha.mask) >> alpha.shift;
|
||||
uint64_t r = (color & red.mask) >> red.shift;
|
||||
uint64_t g = (color & green.mask) >> green.shift;
|
||||
uint64_t b = (color & blue.mask) >> blue.shift;
|
||||
|
||||
a = normalize(a, alpha.depth, output.alpha.depth);
|
||||
r = normalize(r, red.depth, output.red.depth);
|
||||
g = normalize(g, green.depth, output.green.depth);
|
||||
b = normalize(b, blue.depth, output.blue.depth);
|
||||
|
||||
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
|
||||
dp += output.stride;
|
||||
}
|
||||
}
|
||||
|
||||
operator=(std::move(output));
|
||||
}
|
||||
|
||||
void image::alphaBlend(uint64_t alphaColor) {
|
||||
uint64_t alphaR = (alphaColor & red.mask) >> red.shift;
|
||||
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
|
||||
uint64_t alphaB = (alphaColor & blue.mask) >> blue.shift;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t *dp = data + pitch * y;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint64_t color = read(dp);
|
||||
|
||||
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
|
||||
uint64_t colorR = (color & red.mask) >> red.shift;
|
||||
uint64_t colorG = (color & green.mask) >> green.shift;
|
||||
uint64_t colorB = (color & blue.mask) >> blue.shift;
|
||||
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
|
||||
|
||||
colorA = (1 << alpha.depth) - 1;
|
||||
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
|
||||
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
|
||||
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
|
||||
|
||||
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
|
||||
dp += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//protected
|
||||
|
||||
uint64_t image::interpolate(double mu, const uint64_t *s, double (*op)(double, double, double, double, double)) {
|
||||
uint64_t aa = (s[0] & alpha.mask) >> alpha.shift, ar = (s[0] & red.mask) >> red.shift,
|
||||
ag = (s[0] & green.mask) >> green.shift, ab = (s[0] & blue.mask) >> blue.shift;
|
||||
uint64_t ba = (s[1] & alpha.mask) >> alpha.shift, br = (s[1] & red.mask) >> red.shift,
|
||||
bg = (s[1] & green.mask) >> green.shift, bb = (s[1] & blue.mask) >> blue.shift;
|
||||
uint64_t ca = (s[2] & alpha.mask) >> alpha.shift, cr = (s[2] & red.mask) >> red.shift,
|
||||
cg = (s[2] & green.mask) >> green.shift, cb = (s[2] & blue.mask) >> blue.shift;
|
||||
uint64_t da = (s[3] & alpha.mask) >> alpha.shift, dr = (s[3] & red.mask) >> red.shift,
|
||||
dg = (s[3] & green.mask) >> green.shift, db = (s[3] & blue.mask) >> blue.shift;
|
||||
|
||||
int64_t A = op(mu, aa, ba, ca, da);
|
||||
int64_t R = op(mu, ar, br, cr, dr);
|
||||
int64_t G = op(mu, ag, bg, cg, dg);
|
||||
int64_t B = op(mu, ab, bb, cb, db);
|
||||
|
||||
A = max(0, min(A, (1 << alpha.depth) - 1));
|
||||
R = max(0, min(R, (1 << red.depth) - 1));
|
||||
G = max(0, min(G, (1 << green.depth) - 1));
|
||||
B = max(0, min(B, (1 << blue.depth) - 1));
|
||||
|
||||
return (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift);
|
||||
}
|
||||
|
||||
void image::scaleX(unsigned outputWidth, interpolation op) {
|
||||
uint8_t *outputData = new uint8_t[outputWidth * height * stride];
|
||||
unsigned outputPitch = outputWidth * stride;
|
||||
double step = (double)width / (double)outputWidth;
|
||||
const uint8_t *terminal = data + pitch * height;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t *dp = outputData + outputPitch * y;
|
||||
uint8_t *sp = data + pitch * y;
|
||||
|
||||
double fraction = 0.0;
|
||||
uint64_t s[4] = { sp < terminal ? read(sp) : 0 }; //B,C (0,1) = center of kernel { 0, 0, 1, 2 }
|
||||
s[1] = s[0];
|
||||
s[2] = sp + stride < terminal ? read(sp += stride) : s[1];
|
||||
s[3] = sp + stride < terminal ? read(sp += stride) : s[2];
|
||||
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
while(fraction <= 1.0) {
|
||||
if(dp >= outputData + outputPitch * height) break;
|
||||
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
|
||||
dp += stride;
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
|
||||
if(sp + stride < terminal) s[3] = read(sp += stride);
|
||||
fraction -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
width = outputWidth;
|
||||
pitch = width * stride;
|
||||
}
|
||||
|
||||
void image::scaleY(unsigned outputHeight, interpolation op) {
|
||||
uint8_t *outputData = new uint8_t[width * outputHeight * stride];
|
||||
double step = (double)height / (double)outputHeight;
|
||||
const uint8_t *terminal = data + pitch * height;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint8_t *dp = outputData + stride * x;
|
||||
uint8_t *sp = data + stride * x;
|
||||
|
||||
double fraction = 0.0;
|
||||
uint64_t s[4] = { sp < terminal ? read(sp) : 0 };
|
||||
s[1] = s[0];
|
||||
s[2] = sp + pitch < terminal ? read(sp += pitch) : s[1];
|
||||
s[3] = sp + pitch < terminal ? read(sp += pitch) : s[2];
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
while(fraction <= 1.0) {
|
||||
if(dp >= outputData + pitch * outputHeight) break;
|
||||
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
|
||||
dp += pitch;
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
|
||||
if(sp + pitch < terminal) s[3] = read(sp += pitch);
|
||||
fraction -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
height = outputHeight;
|
||||
}
|
||||
|
||||
bool image::loadBMP(const string &filename) {
|
||||
uint32_t *outputData;
|
||||
unsigned outputWidth, outputHeight;
|
||||
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
|
||||
|
||||
allocate(outputWidth, outputHeight);
|
||||
const uint32_t *sp = outputData;
|
||||
uint8_t *dp = data;
|
||||
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
for(unsigned x = 0; x < outputWidth; x++) {
|
||||
uint32_t color = *sp++;
|
||||
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
|
||||
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
|
||||
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
|
||||
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
|
||||
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
|
||||
dp += stride;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] outputData;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image::loadPNG(const uint8_t *pngData, unsigned pngSize) {
|
||||
png source;
|
||||
if(source.decode(pngData, pngSize) == false) return false;
|
||||
|
||||
allocate(source.info.width, source.info.height);
|
||||
const uint8_t *sp = source.data;
|
||||
uint8_t *dp = data;
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t p, r, g, b, a;
|
||||
|
||||
switch(source.info.colorType) {
|
||||
case 0: //L
|
||||
r = g = b = source.readbits(sp);
|
||||
a = (1 << source.info.bitDepth) - 1;
|
||||
break;
|
||||
case 2: //R,G,B
|
||||
r = source.readbits(sp);
|
||||
g = source.readbits(sp);
|
||||
b = source.readbits(sp);
|
||||
a = (1 << source.info.bitDepth) - 1;
|
||||
break;
|
||||
case 3: //P
|
||||
p = source.readbits(sp);
|
||||
r = source.info.palette[p][0];
|
||||
g = source.info.palette[p][1];
|
||||
b = source.info.palette[p][2];
|
||||
a = (1 << source.info.bitDepth) - 1;
|
||||
break;
|
||||
case 4: //L,A
|
||||
r = g = b = source.readbits(sp);
|
||||
a = source.readbits(sp);
|
||||
break;
|
||||
case 6: //R,G,B,A
|
||||
r = source.readbits(sp);
|
||||
g = source.readbits(sp);
|
||||
b = source.readbits(sp);
|
||||
a = source.readbits(sp);
|
||||
break;
|
||||
}
|
||||
|
||||
a = normalize(a, source.info.bitDepth, alpha.depth);
|
||||
r = normalize(r, source.info.bitDepth, red.depth);
|
||||
g = normalize(g, source.info.bitDepth, green.depth);
|
||||
b = normalize(b, source.info.bitDepth, blue.depth);
|
||||
|
||||
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
|
||||
};
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
write(dp, decode());
|
||||
dp += stride;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image::loadPNG(const string &filename) {
|
||||
filemap map;
|
||||
if(map.open(filename, filemap::mode::read) == false) return false;
|
||||
return loadPNG(map.data(), map.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef NALL_INTERPOLATION_HPP
|
||||
#define NALL_INTERPOLATION_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct Interpolation {
|
||||
static inline double Nearest(double mu, double a, double b, double c, double d) {
|
||||
return (mu <= 0.5 ? b : c);
|
||||
}
|
||||
|
||||
static inline double Sublinear(double mu, double a, double b, double c, double d) {
|
||||
mu = ((mu - 0.5) * 2.0) + 0.5;
|
||||
if(mu < 0) mu = 0;
|
||||
if(mu > 1) mu = 1;
|
||||
return b * (1.0 - mu) + c * mu;
|
||||
}
|
||||
|
||||
static inline double Linear(double mu, double a, double b, double c, double d) {
|
||||
return b * (1.0 - mu) + c * mu;
|
||||
}
|
||||
|
||||
static inline double Cosine(double mu, double a, double b, double c, double d) {
|
||||
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||
return b * (1.0 - mu) + c * mu;
|
||||
}
|
||||
|
||||
static inline double Cubic(double mu, double a, double b, double c, double d) {
|
||||
double A = d - c - a + b;
|
||||
double B = a - b - A;
|
||||
double C = c - a;
|
||||
double D = b;
|
||||
return A * (mu * mu * mu) + B * (mu * mu) + C * mu + D;
|
||||
}
|
||||
|
||||
static inline double Hermite(double mu1, double a, double b, double c, double d) {
|
||||
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||
double mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
a2 = mu3 - mu2;
|
||||
a3 = -2 * mu3 + 3 * mu2;
|
||||
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef NALL_INTRINSICS_HPP
|
||||
#define NALL_INTRINSICS_HPP
|
||||
|
||||
struct Intrinsics {
|
||||
enum class Compiler : unsigned { GCC, VisualC, Unknown };
|
||||
enum class Platform : unsigned { X, OSX, Windows, Unknown };
|
||||
enum class Endian : unsigned { LSB, MSB, Unknown };
|
||||
|
||||
static inline Compiler compiler();
|
||||
static inline Platform platform();
|
||||
static inline Endian endian();
|
||||
};
|
||||
|
||||
/* Compiler detection */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define COMPILER_GCC
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::GCC; }
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_VISUALC
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::VisualC; }
|
||||
#else
|
||||
#warning "unable to detect compiler"
|
||||
#define COMPILER_UNKNOWN
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; }
|
||||
#endif
|
||||
|
||||
/* Platform detection */
|
||||
|
||||
#if defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define PLATFORM_X
|
||||
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::X; }
|
||||
#elif defined(__APPLE__)
|
||||
#define PLATFORM_OSX
|
||||
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::OSX; }
|
||||
#elif defined(_WIN32)
|
||||
#define PLATFORM_WINDOWS
|
||||
#define PLATFORM_WIN
|
||||
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; }
|
||||
#else
|
||||
#warning "unable to detect platform"
|
||||
#define PLATFORM_UNKNOWN
|
||||
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Unknown; }
|
||||
#endif
|
||||
|
||||
/* Endian detection */
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
|
||||
#define ENDIAN_LSB
|
||||
#define ARCH_LSB
|
||||
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::LSB; }
|
||||
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
|
||||
#define ENDIAN_MSB
|
||||
#define ARCH_MSB
|
||||
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::MSB; }
|
||||
#else
|
||||
#warning "unable to detect endian"
|
||||
#define ENDIAN_UNKNOWN
|
||||
#define ARCH_UNKNOWN
|
||||
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::Unknown; }
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -76,7 +76,7 @@ bool ips::apply() {
|
|||
}
|
||||
|
||||
delete[] data;
|
||||
data = 0;
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ bool ips::modify(const string &filename) {
|
|||
return file::read(filename, modifyData, modifySize);
|
||||
}
|
||||
|
||||
ips::ips() : data(0), sourceData(0), modifyData(0) {
|
||||
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
|
||||
}
|
||||
|
||||
ips::~ips() {
|
|
@ -25,7 +25,7 @@ protected:
|
|||
struct Node {
|
||||
unsigned offset;
|
||||
Node *next;
|
||||
inline Node() : offset(0), next(0) {}
|
||||
inline Node() : offset(0), next(nullptr) {}
|
||||
inline ~Node() { if(next) delete next; }
|
||||
} *tree[65536];
|
||||
|
||||
|
@ -34,7 +34,7 @@ protected:
|
|||
unsigned sourceSize;
|
||||
|
||||
public:
|
||||
inline lzss() : sourceData(0), sourceSize(0) {}
|
||||
inline lzss() : sourceData(nullptr), sourceSize(0) {}
|
||||
};
|
||||
|
||||
void lzss::source(const uint8_t *data, unsigned size) {
|
|
@ -0,0 +1,116 @@
|
|||
#ifndef NALL_MAP_HPP
|
||||
#define NALL_MAP_HPP
|
||||
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
struct map {
|
||||
struct pair {
|
||||
LHS name;
|
||||
RHS data;
|
||||
};
|
||||
|
||||
inline void reset() {
|
||||
list.reset();
|
||||
}
|
||||
|
||||
inline unsigned size() const {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
//O(log n) find
|
||||
inline optional<unsigned> find(const LHS &name) const {
|
||||
signed first = 0, last = size() - 1;
|
||||
while(first <= last) {
|
||||
signed middle = (first + last) / 2;
|
||||
if(name < list[middle].name) last = middle - 1; //search lower half
|
||||
else if(list[middle].name < name) first = middle + 1; //search upper half
|
||||
else return { true, (unsigned)middle }; //match found
|
||||
}
|
||||
return { false, 0u };
|
||||
}
|
||||
|
||||
//O(n) insert + O(log n) find
|
||||
inline RHS& insert(const LHS &name, const RHS &data) {
|
||||
if(auto position = find(name)) {
|
||||
list[position()].data = data;
|
||||
return list[position()].data;
|
||||
}
|
||||
signed offset = size();
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(name < list[n].name) { offset = n; break; }
|
||||
}
|
||||
list.insert(offset, { name, data });
|
||||
return list[offset].data;
|
||||
}
|
||||
|
||||
//O(log n) find
|
||||
inline void modify(const LHS &name, const RHS &data) {
|
||||
if(auto position = find(name)) list[position()].data = data;
|
||||
}
|
||||
|
||||
//O(n) remove + O(log n) find
|
||||
inline void remove(const LHS &name) {
|
||||
if(auto position = find(name)) list.remove(position());
|
||||
}
|
||||
|
||||
//O(log n) find
|
||||
inline RHS& operator[](const LHS &name) {
|
||||
if(auto position = find(name)) return list[position()].data;
|
||||
throw;
|
||||
}
|
||||
|
||||
inline const RHS& operator[](const LHS &name) const {
|
||||
if(auto position = find(name)) return list[position()].data;
|
||||
throw;
|
||||
}
|
||||
|
||||
inline RHS& operator()(const LHS &name) {
|
||||
return insert(name, RHS());
|
||||
}
|
||||
|
||||
inline const RHS& operator()(const LHS &name, const RHS &data) const {
|
||||
if(auto position = find(name)) return list[position()].data;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline pair* begin() { return list.begin(); }
|
||||
inline pair* end() { return list.end(); }
|
||||
inline const pair* begin() const { return list.begin(); }
|
||||
inline const pair* end() const { return list.end(); }
|
||||
|
||||
protected:
|
||||
vector<pair> list;
|
||||
};
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
struct bidirectional_map {
|
||||
const map<LHS, RHS> &lhs;
|
||||
const map<RHS, LHS> &rhs;
|
||||
|
||||
inline void reset() {
|
||||
llist.reset();
|
||||
rlist.reset();
|
||||
}
|
||||
|
||||
inline unsigned size() const {
|
||||
return llist.size();
|
||||
}
|
||||
|
||||
inline void insert(const LHS &ldata, const RHS &rdata) {
|
||||
llist.insert(ldata, rdata);
|
||||
rlist.insert(rdata, ldata);
|
||||
}
|
||||
|
||||
inline bidirectional_map() : lhs(llist), rhs(rlist) {}
|
||||
|
||||
protected:
|
||||
map<LHS, RHS> llist;
|
||||
map<RHS, LHS> rlist;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef NALL_MOSAIC_HPP
|
||||
#define NALL_MOSAIC_HPP
|
||||
|
||||
#define NALL_MOSAIC_INTERNAL_HPP
|
||||
#include <nall/mosaic/bitstream.hpp>
|
||||
#include <nall/mosaic/context.hpp>
|
||||
#include <nall/mosaic/parser.hpp>
|
||||
#undef NALL_MOSAIC_INTERNAL_HPP
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
#ifdef NALL_MOSAIC_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
namespace mosaic {
|
||||
|
||||
struct bitstream {
|
||||
filemap fp;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
bool readonly;
|
||||
bool endian;
|
||||
|
||||
inline bool read(uint64_t addr) const {
|
||||
if(data == nullptr || (addr >> 3) >= size) return 0;
|
||||
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
|
||||
return data[addr >> 3] & mask;
|
||||
}
|
||||
|
||||
inline void write(uint64_t addr, bool value) {
|
||||
if(data == nullptr || readonly == true || (addr >> 3) >= size) return;
|
||||
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
|
||||
if(value == 0) data[addr >> 3] &= ~mask;
|
||||
if(value == 1) data[addr >> 3] |= mask;
|
||||
}
|
||||
|
||||
inline bool open(const string &filename) {
|
||||
readonly = false;
|
||||
if(fp.open(filename, filemap::mode::readwrite) == false) {
|
||||
readonly = true;
|
||||
if(fp.open(filename, filemap::mode::read) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
data = fp.data();
|
||||
size = fp.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void close() {
|
||||
fp.close();
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
inline bitstream() : data(nullptr), endian(1) {
|
||||
}
|
||||
|
||||
inline ~bitstream() {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,224 @@
|
|||
#ifdef NALL_MOSAIC_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
namespace mosaic {
|
||||
|
||||
struct context {
|
||||
unsigned offset;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned count;
|
||||
|
||||
bool endian; //0 = lsb, 1 = msb
|
||||
bool order; //0 = linear, 1 = planar
|
||||
unsigned depth; //1 - 24bpp
|
||||
|
||||
unsigned blockWidth;
|
||||
unsigned blockHeight;
|
||||
unsigned blockStride;
|
||||
unsigned blockOffset;
|
||||
array<unsigned> block;
|
||||
|
||||
unsigned tileWidth;
|
||||
unsigned tileHeight;
|
||||
unsigned tileStride;
|
||||
unsigned tileOffset;
|
||||
array<unsigned> tile;
|
||||
|
||||
unsigned mosaicWidth;
|
||||
unsigned mosaicHeight;
|
||||
unsigned mosaicStride;
|
||||
unsigned mosaicOffset;
|
||||
array<unsigned> mosaic;
|
||||
|
||||
unsigned paddingWidth;
|
||||
unsigned paddingHeight;
|
||||
unsigned paddingColor;
|
||||
array<unsigned> palette;
|
||||
|
||||
inline unsigned objectWidth() const { return blockWidth * tileWidth * mosaicWidth + paddingWidth; }
|
||||
inline unsigned objectHeight() const { return blockHeight * tileHeight * mosaicHeight + paddingHeight; }
|
||||
inline unsigned objectSize() const {
|
||||
unsigned size = blockStride * tileWidth * tileHeight * mosaicWidth * mosaicHeight
|
||||
+ blockOffset * tileHeight * mosaicWidth * mosaicHeight
|
||||
+ tileStride * mosaicWidth * mosaicHeight
|
||||
+ tileOffset * mosaicHeight;
|
||||
return max(1u, size);
|
||||
}
|
||||
|
||||
inline unsigned eval(const string &expression) {
|
||||
intmax_t result;
|
||||
if(fixedpoint::eval(expression, result) == false) return 0u;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void eval(array<unsigned> &buffer, const string &expression_) {
|
||||
string expression = expression_;
|
||||
bool function = false;
|
||||
for(auto &c : expression) {
|
||||
if(c == '(') function = true;
|
||||
if(c == ')') function = false;
|
||||
if(c == ',' && function == true) c = ';';
|
||||
}
|
||||
|
||||
lstring list = expression.split(",");
|
||||
for(auto &item : list) {
|
||||
item.trim();
|
||||
if(item.wildcard("f(?*) ?*")) {
|
||||
item.ltrim<1>("f(");
|
||||
lstring part = item.split<1>(") ");
|
||||
lstring args = part[0].split<3>(";");
|
||||
for(auto &item : args) item.trim();
|
||||
|
||||
unsigned length = eval(args(0, "0"));
|
||||
unsigned offset = eval(args(1, "0"));
|
||||
unsigned stride = eval(args(2, "0"));
|
||||
if(args.size() < 2) offset = buffer.size();
|
||||
if(args.size() < 3) stride = 1;
|
||||
|
||||
for(unsigned n = 0; n < length; n++) {
|
||||
string fn = part[1];
|
||||
fn.replace("n", decimal(n));
|
||||
fn.replace("o", decimal(offset));
|
||||
fn.replace("p", decimal(buffer.size()));
|
||||
buffer.resize(offset + 1);
|
||||
buffer[offset] = eval(fn);
|
||||
offset += stride;
|
||||
}
|
||||
} else if(item.wildcard("base64*")) {
|
||||
unsigned offset = 0;
|
||||
item.ltrim<1>("base64");
|
||||
if(item.wildcard("(?*) *")) {
|
||||
item.ltrim<1>("(");
|
||||
lstring part = item.split<1>(") ");
|
||||
offset = eval(part[0]);
|
||||
item = part(1, "");
|
||||
}
|
||||
item.trim();
|
||||
for(auto &c : item) {
|
||||
if(c >= 'A' && c <= 'Z') buffer.append(offset + c - 'A' + 0);
|
||||
if(c >= 'a' && c <= 'z') buffer.append(offset + c - 'a' + 26);
|
||||
if(c >= '0' && c <= '9') buffer.append(offset + c - '0' + 52);
|
||||
if(c == '-') buffer.append(offset + 62);
|
||||
if(c == '_') buffer.append(offset + 63);
|
||||
}
|
||||
} else if(item.wildcard("file *")) {
|
||||
item.ltrim<1>("file ");
|
||||
item.trim();
|
||||
//...
|
||||
} else if(item.empty() == false) {
|
||||
buffer.append(eval(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void parse(const string &data) {
|
||||
reset();
|
||||
|
||||
lstring lines = data.split("\n");
|
||||
for(auto &line : lines) {
|
||||
lstring part = line.split<1>(":");
|
||||
if(part.size() != 2) continue;
|
||||
part[0].trim();
|
||||
part[1].trim();
|
||||
|
||||
if(part[0] == "offset") offset = eval(part[1]);
|
||||
if(part[0] == "width") width = eval(part[1]);
|
||||
if(part[0] == "height") height = eval(part[1]);
|
||||
if(part[0] == "count") count = eval(part[1]);
|
||||
|
||||
if(part[0] == "endian") endian = eval(part[1]);
|
||||
if(part[0] == "order") order = eval(part[1]);
|
||||
if(part[0] == "depth") depth = eval(part[1]);
|
||||
|
||||
if(part[0] == "blockWidth") blockWidth = eval(part[1]);
|
||||
if(part[0] == "blockHeight") blockHeight = eval(part[1]);
|
||||
if(part[0] == "blockStride") blockStride = eval(part[1]);
|
||||
if(part[0] == "blockOffset") blockOffset = eval(part[1]);
|
||||
if(part[0] == "block") eval(block, part[1]);
|
||||
|
||||
if(part[0] == "tileWidth") tileWidth = eval(part[1]);
|
||||
if(part[0] == "tileHeight") tileHeight = eval(part[1]);
|
||||
if(part[0] == "tileStride") tileStride = eval(part[1]);
|
||||
if(part[0] == "tileOffset") tileOffset = eval(part[1]);
|
||||
if(part[0] == "tile") eval(tile, part[1]);
|
||||
|
||||
if(part[0] == "mosaicWidth") mosaicWidth = eval(part[1]);
|
||||
if(part[0] == "mosaicHeight") mosaicHeight = eval(part[1]);
|
||||
if(part[0] == "mosaicStride") mosaicStride = eval(part[1]);
|
||||
if(part[0] == "mosaicOffset") mosaicOffset = eval(part[1]);
|
||||
if(part[0] == "mosaic") eval(mosaic, part[1]);
|
||||
|
||||
if(part[0] == "paddingWidth") paddingWidth = eval(part[1]);
|
||||
if(part[0] == "paddingHeight") paddingHeight = eval(part[1]);
|
||||
if(part[0] == "paddingColor") paddingColor = eval(part[1]);
|
||||
if(part[0] == "palette") eval(palette, part[1]);
|
||||
}
|
||||
|
||||
sanitize();
|
||||
}
|
||||
|
||||
inline bool load(const string &filename) {
|
||||
string filedata;
|
||||
if(filedata.readfile(filename) == false) return false;
|
||||
parse(filedata);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void sanitize() {
|
||||
if(depth < 1) depth = 1;
|
||||
if(depth > 24) depth = 24;
|
||||
|
||||
if(blockWidth < 1) blockWidth = 1;
|
||||
if(blockHeight < 1) blockHeight = 1;
|
||||
|
||||
if(tileWidth < 1) tileWidth = 1;
|
||||
if(tileHeight < 1) tileHeight = 1;
|
||||
|
||||
if(mosaicWidth < 1) mosaicWidth = 1;
|
||||
if(mosaicHeight < 1) mosaicHeight = 1;
|
||||
}
|
||||
|
||||
inline void reset() {
|
||||
offset = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
count = 0;
|
||||
|
||||
endian = 1;
|
||||
order = 0;
|
||||
depth = 1;
|
||||
|
||||
blockWidth = 1;
|
||||
blockHeight = 1;
|
||||
blockStride = 0;
|
||||
blockOffset = 0;
|
||||
block.reset();
|
||||
|
||||
tileWidth = 1;
|
||||
tileHeight = 1;
|
||||
tileStride = 0;
|
||||
tileOffset = 0;
|
||||
tile.reset();
|
||||
|
||||
mosaicWidth = 1;
|
||||
mosaicHeight = 1;
|
||||
mosaicStride = 0;
|
||||
mosaicOffset = 0;
|
||||
mosaic.reset();
|
||||
|
||||
paddingWidth = 0;
|
||||
paddingHeight = 0;
|
||||
paddingColor = 0x000000;
|
||||
palette.reset();
|
||||
}
|
||||
|
||||
inline context() {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,126 @@
|
|||
#ifdef NALL_MOSAIC_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
namespace mosaic {
|
||||
|
||||
struct parser {
|
||||
image canvas;
|
||||
|
||||
//export from bitstream to canvas
|
||||
inline void load(bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
|
||||
canvas.allocate(width, height);
|
||||
canvas.clear(ctx.paddingColor);
|
||||
parse(1, stream, offset, ctx, width, height);
|
||||
}
|
||||
|
||||
//import from canvas to bitstream
|
||||
inline bool save(bitstream &stream, uint64_t offset, context &ctx) {
|
||||
if(stream.readonly) return false;
|
||||
parse(0, stream, offset, ctx, canvas.width, canvas.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline parser() : canvas(0, 32, 0u, 255u << 16, 255u << 8, 255u << 0) {
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint32_t read(unsigned x, unsigned y) const {
|
||||
unsigned addr = y * canvas.width + x;
|
||||
if(addr >= canvas.width * canvas.height) return 0u;
|
||||
uint32_t *buffer = (uint32_t*)canvas.data;
|
||||
return buffer[addr];
|
||||
}
|
||||
|
||||
inline void write(unsigned x, unsigned y, uint32_t data) {
|
||||
unsigned addr = y * canvas.width + x;
|
||||
if(addr >= canvas.width * canvas.height) return;
|
||||
uint32_t *buffer = (uint32_t*)canvas.data;
|
||||
buffer[addr] = data;
|
||||
}
|
||||
|
||||
inline void parse(bool load, bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
|
||||
stream.endian = ctx.endian;
|
||||
unsigned canvasWidth = width / (ctx.mosaicWidth * ctx.tileWidth * ctx.blockWidth + ctx.paddingWidth);
|
||||
unsigned canvasHeight = height / (ctx.mosaicHeight * ctx.tileHeight * ctx.blockHeight + ctx.paddingHeight);
|
||||
unsigned bitsPerBlock = ctx.depth * ctx.blockWidth * ctx.blockHeight;
|
||||
|
||||
unsigned objectOffset = 0;
|
||||
for(unsigned objectY = 0; objectY < canvasHeight; objectY++) {
|
||||
for(unsigned objectX = 0; objectX < canvasWidth; objectX++) {
|
||||
if(objectOffset >= ctx.count && ctx.count > 0) break;
|
||||
unsigned objectIX = objectX * ctx.objectWidth();
|
||||
unsigned objectIY = objectY * ctx.objectHeight();
|
||||
objectOffset++;
|
||||
|
||||
unsigned mosaicOffset = 0;
|
||||
for(unsigned mosaicY = 0; mosaicY < ctx.mosaicHeight; mosaicY++) {
|
||||
for(unsigned mosaicX = 0; mosaicX < ctx.mosaicWidth; mosaicX++) {
|
||||
unsigned mosaicData = ctx.mosaic(mosaicOffset, mosaicOffset);
|
||||
unsigned mosaicIX = (mosaicData % ctx.mosaicWidth) * (ctx.tileWidth * ctx.blockWidth);
|
||||
unsigned mosaicIY = (mosaicData / ctx.mosaicWidth) * (ctx.tileHeight * ctx.blockHeight);
|
||||
mosaicOffset++;
|
||||
|
||||
unsigned tileOffset = 0;
|
||||
for(unsigned tileY = 0; tileY < ctx.tileHeight; tileY++) {
|
||||
for(unsigned tileX = 0; tileX < ctx.tileWidth; tileX++) {
|
||||
unsigned tileData = ctx.tile(tileOffset, tileOffset);
|
||||
unsigned tileIX = (tileData % ctx.tileWidth) * ctx.blockWidth;
|
||||
unsigned tileIY = (tileData / ctx.tileWidth) * ctx.blockHeight;
|
||||
tileOffset++;
|
||||
|
||||
unsigned blockOffset = 0;
|
||||
for(unsigned blockY = 0; blockY < ctx.blockHeight; blockY++) {
|
||||
for(unsigned blockX = 0; blockX < ctx.blockWidth; blockX++) {
|
||||
if(load) {
|
||||
unsigned palette = 0;
|
||||
for(unsigned n = 0; n < ctx.depth; n++) {
|
||||
unsigned index = blockOffset++;
|
||||
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
|
||||
palette |= stream.read(offset + ctx.block(index, index)) << n;
|
||||
}
|
||||
|
||||
write(
|
||||
objectIX + mosaicIX + tileIX + blockX,
|
||||
objectIY + mosaicIY + tileIY + blockY,
|
||||
ctx.palette(palette, palette)
|
||||
);
|
||||
} else /* save */ {
|
||||
uint32_t palette = read(
|
||||
objectIX + mosaicIX + tileIX + blockX,
|
||||
objectIY + mosaicIY + tileIY + blockY
|
||||
);
|
||||
|
||||
for(unsigned n = 0; n < ctx.depth; n++) {
|
||||
unsigned index = blockOffset++;
|
||||
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
|
||||
stream.write(offset + ctx.block(index, index), palette & 1);
|
||||
palette >>= 1;
|
||||
}
|
||||
}
|
||||
} //blockX
|
||||
} //blockY
|
||||
|
||||
offset += ctx.blockStride;
|
||||
} //tileX
|
||||
|
||||
offset += ctx.blockOffset;
|
||||
} //tileY
|
||||
|
||||
offset += ctx.tileStride;
|
||||
} //mosaicX
|
||||
|
||||
offset += ctx.tileOffset;
|
||||
} //mosaicY
|
||||
|
||||
offset += ctx.mosaicStride;
|
||||
} //objectX
|
||||
|
||||
offset += ctx.mosaicOffset;
|
||||
} //objectY
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,171 @@
|
|||
#ifndef NALL_NES_CARTRIDGE_HPP
|
||||
#define NALL_NES_CARTRIDGE_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct FamicomCartridge {
|
||||
string markup;
|
||||
inline FamicomCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||
if(size < 16) return;
|
||||
if(data[0] != 'N') return;
|
||||
if(data[1] != 'E') return;
|
||||
if(data[2] != 'S') return;
|
||||
if(data[3] != 26) return;
|
||||
|
||||
unsigned mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
|
||||
unsigned mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
|
||||
unsigned prgrom = data[4] * 0x4000;
|
||||
unsigned chrrom = data[5] * 0x2000;
|
||||
unsigned prgram = 0u;
|
||||
unsigned chrram = chrrom == 0u ? 8192u : 0u;
|
||||
|
||||
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
|
||||
|
||||
switch(mapper) {
|
||||
default:
|
||||
markup.append(" <board type='NES-NROM-256'/>\n");
|
||||
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
markup.append(" <board type='NES-SXROM'/>\n");
|
||||
markup.append(" <chip type='MMC1B2'/>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
markup.append(" <board type='NES-UOROM'/>\n");
|
||||
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
markup.append(" <board type='NES-CNROM'/>\n");
|
||||
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
//MMC3
|
||||
markup.append(" <board type='NES-TLROM'/>\n");
|
||||
markup.append(" <chip type='MMC3B'/>\n");
|
||||
prgram = 8192;
|
||||
//MMC6
|
||||
//markup.append(" <board type='NES-HKROM'/>\n");
|
||||
//markup.append(" <chip type='MMC6'/>\n");
|
||||
//prgram = 1024;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
markup.append(" <board type='NES-ELROM'/>\n");
|
||||
markup.append(" <chip type='MMC5'/>\n");
|
||||
prgram = 65536;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
markup.append(" <board type='NES-AOROM'/>\n");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
markup.append(" <board type='NES-PNROM'/>\n");
|
||||
markup.append(" <chip type='MMC2'/>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
markup.append(" <board type='NES-FKROM'/>\n");
|
||||
markup.append(" <chip type='MMC4'/>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
markup.append(" <board type='BANDAI-FCG'/>\n");
|
||||
markup.append(" <chip type='LZ93D50'/>\n");
|
||||
break;
|
||||
|
||||
case 21:
|
||||
case 23:
|
||||
case 25:
|
||||
//VRC4
|
||||
markup.append(" <board type='KONAMI-VRC-4'/>\n");
|
||||
markup.append(" <chip type='VRC4'>\n");
|
||||
markup.append(" <pinout a0='1' a1='0'/>\n");
|
||||
markup.append(" </chip>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 22:
|
||||
//VRC2
|
||||
markup.append(" <board type='KONAMI-VRC-2'/>\n");
|
||||
markup.append(" <chip type='VRC2'>\n");
|
||||
markup.append(" <pinout a0='0' a1='1'/>\n");
|
||||
markup.append(" </chip>\n");
|
||||
break;
|
||||
|
||||
case 24:
|
||||
markup.append(" <board type='KONAMI-VRC-6'/>\n");
|
||||
markup.append(" <chip type='VRC6'/>\n");
|
||||
break;
|
||||
|
||||
case 26:
|
||||
markup.append(" <board type='KONAMI-VRC-6'/>\n");
|
||||
markup.append(" <chip type='VRC6'/>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 34:
|
||||
markup.append(" <board type='NES-BNROM'/>\n");
|
||||
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||
break;
|
||||
|
||||
case 66:
|
||||
markup.append(" <board type='NES-GNROM'/>\n");
|
||||
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||
break;
|
||||
|
||||
case 69:
|
||||
markup.append(" <board type='SUNSOFT-5B'/>\n");
|
||||
markup.append(" <chip type='5B'/>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 73:
|
||||
markup.append(" <board type='KONAMI-VRC-3'/>\n");
|
||||
markup.append(" <chip type='VRC3'/>\n");
|
||||
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 75:
|
||||
markup.append(" <board type='KONAMI-VRC-1'/>\n");
|
||||
markup.append(" <chip type='VRC1'/>\n");
|
||||
break;
|
||||
|
||||
case 85:
|
||||
markup.append(" <board type='KONAMI-VRC-7/'>\n");
|
||||
markup.append(" <chip type='VRC7'/>\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
}
|
||||
|
||||
markup.append(" <prg>\n");
|
||||
if(prgrom) markup.append(" <rom size='", prgrom, "'/>\n");
|
||||
if(prgram) markup.append(" <ram size='", prgram, "' nonvolatile='true'/>\n");
|
||||
markup.append(" </prg>\n");
|
||||
|
||||
markup.append(" <chr>\n");
|
||||
if(chrrom) markup.append(" <rom size='", chrrom, "'/>\n");
|
||||
if(chrram) markup.append(" <ram size='", chrram, "'/>\n");
|
||||
markup.append(" </chr>\n");
|
||||
|
||||
markup.append("</cartridge>\n");
|
||||
markup.transform("'", "\"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -64,8 +64,8 @@
|
|||
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
|
||||
#define putenv _putenv
|
||||
#define rmdir _rmdir
|
||||
#define usleep(n) Sleep(n / 1000)
|
||||
#define vsnprintf _vsnprintf
|
||||
inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 1000); }
|
||||
#endif
|
||||
|
||||
//================
|
||||
|
@ -104,6 +104,8 @@
|
|||
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
|
||||
strcpy(path, nall::utf8_t(fp));
|
||||
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -112,6 +114,8 @@
|
|||
_wgetcwd(fp, _MAX_PATH);
|
||||
strcpy(path, nall::utf8_t(fp));
|
||||
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
|
@ -121,11 +125,16 @@
|
|||
*path = 0;
|
||||
struct passwd *userinfo = getpwuid(getuid());
|
||||
if(userinfo) strcpy(path, userinfo->pw_dir);
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
|
||||
inline char *getcwd(char *path) {
|
||||
return getcwd(path, PATH_MAX);
|
||||
auto unused = getcwd(path, PATH_MAX);
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -10,9 +10,12 @@
|
|||
namespace nall {
|
||||
|
||||
struct png {
|
||||
uint32_t *data;
|
||||
unsigned size;
|
||||
|
||||
//colorType:
|
||||
//0 = L
|
||||
//2 = R,G,B
|
||||
//3 = P
|
||||
//4 = L,A
|
||||
//6 = R,G,B,A
|
||||
struct Info {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
@ -28,13 +31,14 @@ struct png {
|
|||
uint8_t palette[256][3];
|
||||
} info;
|
||||
|
||||
uint8_t *rawData;
|
||||
unsigned rawSize;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
inline bool decode(const string &filename);
|
||||
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
|
||||
inline void transform();
|
||||
inline void alphaTransform(uint32_t rgb = 0xffffff);
|
||||
inline unsigned readbits(const uint8_t *&data);
|
||||
unsigned bitpos;
|
||||
|
||||
inline png();
|
||||
inline ~png();
|
||||
|
||||
|
@ -46,16 +50,11 @@ protected:
|
|||
IEND = 0x49454e44,
|
||||
};
|
||||
|
||||
static const unsigned interlace[7][4];
|
||||
unsigned bitpos;
|
||||
|
||||
inline unsigned interlace(unsigned pass, unsigned index);
|
||||
inline unsigned inflateSize();
|
||||
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
|
||||
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
|
||||
inline unsigned read(const uint8_t *data, unsigned length);
|
||||
inline unsigned decode(const uint8_t *&data);
|
||||
inline unsigned readbits(const uint8_t *&data);
|
||||
inline unsigned scale(unsigned n);
|
||||
};
|
||||
|
||||
bool png::decode(const string &filename) {
|
||||
|
@ -146,14 +145,14 @@ bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
|||
return false;
|
||||
}
|
||||
|
||||
rawSize = info.width * info.height * info.bytesPerPixel;
|
||||
rawData = new uint8_t[rawSize];
|
||||
size = info.width * info.height * info.bytesPerPixel;
|
||||
data = new uint8_t[size];
|
||||
|
||||
if(info.interlaceMethod == 0) {
|
||||
if(filter(rawData, interlacedData, info.width, info.height) == false) {
|
||||
if(filter(data, interlacedData, info.width, info.height) == false) {
|
||||
delete[] interlacedData;
|
||||
delete[] rawData;
|
||||
rawData = 0;
|
||||
delete[] data;
|
||||
data = 0;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -161,8 +160,8 @@ bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
|||
for(unsigned pass = 0; pass < 7; pass++) {
|
||||
if(deinterlace(passData, pass) == false) {
|
||||
delete[] interlacedData;
|
||||
delete[] rawData;
|
||||
rawData = 0;
|
||||
delete[] data;
|
||||
data = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +171,8 @@ bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
|||
return true;
|
||||
}
|
||||
|
||||
const unsigned png::interlace[7][4] = {
|
||||
unsigned png::interlace(unsigned pass, unsigned index) {
|
||||
static const unsigned data[7][4] = {
|
||||
//x-distance, y-distance, x-origin, y-origin
|
||||
{ 8, 8, 0, 0 },
|
||||
{ 8, 8, 4, 0 },
|
||||
|
@ -182,6 +182,8 @@ const unsigned png::interlace[7][4] = {
|
|||
{ 2, 2, 1, 0 },
|
||||
{ 1, 2, 0, 1 },
|
||||
};
|
||||
return data[pass][index];
|
||||
}
|
||||
|
||||
unsigned png::inflateSize() {
|
||||
if(info.interlaceMethod == 0) {
|
||||
|
@ -190,8 +192,8 @@ unsigned png::inflateSize() {
|
|||
|
||||
unsigned size = 0;
|
||||
for(unsigned pass = 0; pass < 7; pass++) {
|
||||
unsigned xd = interlace[pass][0], yd = interlace[pass][1];
|
||||
unsigned xo = interlace[pass][2], yo = interlace[pass][3];
|
||||
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
|
||||
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
|
||||
unsigned width = (info.width + (xd - xo - 1)) / xd;
|
||||
unsigned height = (info.height + (yd - yo - 1)) / yd;
|
||||
if(width == 0 || height == 0) continue;
|
||||
|
@ -201,8 +203,8 @@ unsigned png::inflateSize() {
|
|||
}
|
||||
|
||||
bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
|
||||
unsigned xd = interlace[pass][0], yd = interlace[pass][1];
|
||||
unsigned xo = interlace[pass][2], yo = interlace[pass][3];
|
||||
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
|
||||
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
|
||||
unsigned width = (info.width + (xd - xo - 1)) / xd;
|
||||
unsigned height = (info.height + (yd - yo - 1)) / yd;
|
||||
if(width == 0 || height == 0) return true;
|
||||
|
@ -213,7 +215,7 @@ bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
|
|||
|
||||
const uint8_t *rd = outputData;
|
||||
for(unsigned y = yo; y < info.height; y += yd) {
|
||||
uint8_t *wr = rawData + y * info.pitch;
|
||||
uint8_t *wr = data + y * info.pitch;
|
||||
for(unsigned x = xo; x < info.width; x += xd) {
|
||||
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
|
||||
wr[x * info.bytesPerPixel + b] = *rd++;
|
||||
|
@ -295,42 +297,6 @@ unsigned png::read(const uint8_t *data, unsigned length) {
|
|||
return result;
|
||||
}
|
||||
|
||||
unsigned png::decode(const uint8_t *&data) {
|
||||
unsigned p, r, g, b, a;
|
||||
|
||||
switch(info.colorType) {
|
||||
case 0: //L
|
||||
r = g = b = scale(readbits(data));
|
||||
a = 0xff;
|
||||
break;
|
||||
case 2: //R,G,B
|
||||
r = scale(readbits(data));
|
||||
g = scale(readbits(data));
|
||||
b = scale(readbits(data));
|
||||
a = 0xff;
|
||||
break;
|
||||
case 3: //P
|
||||
p = readbits(data);
|
||||
r = info.palette[p][0];
|
||||
g = info.palette[p][1];
|
||||
b = info.palette[p][2];
|
||||
a = 0xff;
|
||||
break;
|
||||
case 4: //L,A
|
||||
r = g = b = scale(readbits(data));
|
||||
a = scale(readbits(data));
|
||||
break;
|
||||
case 6: //R,G,B,A
|
||||
r = scale(readbits(data));
|
||||
g = scale(readbits(data));
|
||||
b = scale(readbits(data));
|
||||
a = scale(readbits(data));
|
||||
break;
|
||||
}
|
||||
|
||||
return (a << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
|
||||
unsigned png::readbits(const uint8_t *&data) {
|
||||
unsigned result = 0;
|
||||
switch(info.bitDepth) {
|
||||
|
@ -360,62 +326,12 @@ unsigned png::readbits(const uint8_t *&data) {
|
|||
return result;
|
||||
}
|
||||
|
||||
unsigned png::scale(unsigned n) {
|
||||
switch(info.bitDepth) {
|
||||
case 1: return n ? 0xff : 0x00;
|
||||
case 2: return n * 0x55;
|
||||
case 4: return n * 0x11;
|
||||
case 8: return n;
|
||||
case 16: return n >> 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void png::transform() {
|
||||
if(data) delete[] data;
|
||||
data = new uint32_t[info.width * info.height];
|
||||
|
||||
png::png() : data(nullptr) {
|
||||
bitpos = 0;
|
||||
const uint8_t *rd = rawData;
|
||||
for(unsigned y = 0; y < info.height; y++) {
|
||||
uint32_t *wr = data + y * info.width;
|
||||
for(unsigned x = 0; x < info.width; x++) {
|
||||
wr[x] = decode(rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void png::alphaTransform(uint32_t rgb) {
|
||||
transform();
|
||||
|
||||
uint8_t ir = rgb >> 16;
|
||||
uint8_t ig = rgb >> 8;
|
||||
uint8_t ib = rgb >> 0;
|
||||
|
||||
uint32_t *p = data;
|
||||
for(unsigned y = 0; y < info.height; y++) {
|
||||
for(unsigned x = 0; x < info.width; x++) {
|
||||
uint32_t pixel = *p;
|
||||
uint8_t a = pixel >> 24;
|
||||
uint8_t r = pixel >> 16;
|
||||
uint8_t g = pixel >> 8;
|
||||
uint8_t b = pixel >> 0;
|
||||
|
||||
r = (r * a) + (ir * (255 - a)) >> 8;
|
||||
g = (g * a) + (ig * (255 - a)) >> 8;
|
||||
b = (b * a) + (ib * (255 - a)) >> 8;
|
||||
|
||||
*p++ = (255 << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
png::png() : data(0), rawData(0) {
|
||||
}
|
||||
|
||||
png::~png() {
|
||||
if(data) delete[] data;
|
||||
if(rawData) delete[] rawData;
|
||||
}
|
||||
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace nall {
|
|||
//priority queue implementation using binary min-heap array;
|
||||
//does not require normalize() function.
|
||||
//O(1) find (tick)
|
||||
//O(log n) insert (enqueue)
|
||||
//O(log n) append (enqueue)
|
||||
//O(log n) remove (dequeue)
|
||||
template<typename type_t> class priority_queue {
|
||||
public:
|
|
@ -22,7 +22,7 @@
|
|||
// readwrite<int> y;
|
||||
//};
|
||||
|
||||
//return types are const T& (byref) instead fo T (byval) to avoid major speed
|
||||
//return types are const T& (byref) instead of T (byval) to avoid major speed
|
||||
//penalties for objects with expensive copy constructors
|
||||
|
||||
//operator-> provides access to underlying object type:
|
|
@ -0,0 +1,142 @@
|
|||
#ifndef NALL_REFERENCE_ARRAY_HPP
|
||||
#define NALL_REFERENCE_ARRAY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <nall/bit.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<typename T> struct reference_array {
|
||||
struct exception_out_of_bounds{};
|
||||
|
||||
protected:
|
||||
typedef typename std::remove_reference<T>::type type_t;
|
||||
type_t **pool;
|
||||
unsigned poolsize, buffersize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return buffersize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
buffersize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned newsize) {
|
||||
if(newsize == poolsize) return;
|
||||
|
||||
pool = (type_t**)realloc(pool, sizeof(type_t*) * newsize);
|
||||
poolsize = newsize;
|
||||
buffersize = min(buffersize, newsize);
|
||||
}
|
||||
|
||||
void resize(unsigned newsize) {
|
||||
if(newsize > poolsize) reserve(bit::round(newsize));
|
||||
buffersize = newsize;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool append(type_t& data, Args&&... args) {
|
||||
bool result = append(data);
|
||||
append(std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool append(type_t& data) {
|
||||
for(unsigned index = 0; index < buffersize; index++) {
|
||||
if(pool[index] == &data) return false;
|
||||
}
|
||||
|
||||
unsigned index = buffersize++;
|
||||
if(index >= poolsize) resize(index + 1);
|
||||
pool[index] = &data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(type_t& data) {
|
||||
for(unsigned index = 0; index < buffersize; index++) {
|
||||
if(pool[index] == &data) {
|
||||
for(unsigned i = index; i < buffersize - 1; i++) pool[i] = pool[i + 1];
|
||||
resize(buffersize - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Args> reference_array(Args&... args) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
construct(args...);
|
||||
}
|
||||
|
||||
~reference_array() {
|
||||
reset();
|
||||
}
|
||||
|
||||
reference_array& operator=(const reference_array &source) {
|
||||
if(pool) free(pool);
|
||||
buffersize = source.buffersize;
|
||||
poolsize = source.poolsize;
|
||||
pool = (type_t**)malloc(sizeof(type_t*) * poolsize);
|
||||
memcpy(pool, source.pool, sizeof(type_t*) * buffersize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference_array& operator=(const reference_array &&source) {
|
||||
if(pool) free(pool);
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
buffersize = source.buffersize;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline type_t& operator[](unsigned index) {
|
||||
if(index >= buffersize) throw exception_out_of_bounds();
|
||||
return *pool[index];
|
||||
}
|
||||
|
||||
inline type_t& operator[](unsigned index) const {
|
||||
if(index >= buffersize) throw exception_out_of_bounds();
|
||||
return *pool[index];
|
||||
}
|
||||
|
||||
//iteration
|
||||
struct iterator {
|
||||
bool operator!=(const iterator &source) const { return index != source.index; }
|
||||
type_t& operator*() { return array.operator[](index); }
|
||||
iterator& operator++() { index++; return *this; }
|
||||
iterator(const reference_array &array, unsigned index) : array(array), index(index) {}
|
||||
private:
|
||||
const reference_array &array;
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(*this, 0); }
|
||||
iterator end() { return iterator(*this, buffersize); }
|
||||
const iterator begin() const { return iterator(*this, 0); }
|
||||
const iterator end() const { return iterator(*this, buffersize); }
|
||||
|
||||
private:
|
||||
void construct() {
|
||||
}
|
||||
|
||||
void construct(const reference_array &source) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
void construct(const reference_array &&source) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
template<typename... Args> void construct(T data, Args&... args) {
|
||||
append(data);
|
||||
construct(args...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -9,14 +9,39 @@
|
|||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class serial {
|
||||
public:
|
||||
struct serial {
|
||||
bool readable() {
|
||||
if(port_open == false) return false;
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(port, &fdset);
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
int result = select(FD_SETSIZE, &fdset, nullptr, nullptr, &timeout);
|
||||
if(result < 1) return false;
|
||||
return FD_ISSET(port, &fdset);
|
||||
}
|
||||
|
||||
//-1 on error, otherwise return bytes read
|
||||
int read(uint8_t *data, unsigned length) {
|
||||
if(port_open == false) return -1;
|
||||
return ::read(port, (void*)data, length);
|
||||
}
|
||||
|
||||
bool writable() {
|
||||
if(port_open == false) return false;
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(port, &fdset);
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
int result = select(FD_SETSIZE, nullptr, &fdset, nullptr, &timeout);
|
||||
if(result < 1) return false;
|
||||
return FD_ISSET(port, &fdset);
|
||||
}
|
||||
|
||||
//-1 on error, otherwise return bytes written
|
||||
int write(const uint8_t *data, unsigned length) {
|
||||
if(port_open == false) return -1;
|
|
@ -55,10 +55,10 @@ namespace nall {
|
|||
template<typename T> void integer(T &value) {
|
||||
enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
|
||||
if(imode == Save) {
|
||||
for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3);
|
||||
for(unsigned n = 0; n < size; n++) idata[isize++] = (uintmax_t)value >> (n << 3);
|
||||
} else if(imode == Load) {
|
||||
value = 0;
|
||||
for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3);
|
||||
for(unsigned n = 0; n < size; n++) value |= (uintmax_t)idata[isize++] << (n << 3);
|
||||
} else if(imode == Size) {
|
||||
isize += size;
|
||||
}
|
|
@ -0,0 +1,885 @@
|
|||
#ifndef NALL_SNES_CARTRIDGE_HPP
|
||||
#define NALL_SNES_CARTRIDGE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SuperFamicomCartridge {
|
||||
string markup;
|
||||
inline SuperFamicomCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
inline void read_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned find_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||
inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size);
|
||||
inline bool gameboy_has_rtc(const uint8_t *data, unsigned size);
|
||||
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
RomType = 0x16,
|
||||
RomSize = 0x17,
|
||||
RamSize = 0x18,
|
||||
CartRegion = 0x19,
|
||||
Company = 0x1a,
|
||||
Version = 0x1b,
|
||||
Complement = 0x1c, //inverse checksum
|
||||
Checksum = 0x1e,
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
ModeNormal,
|
||||
ModeBsxSlotted,
|
||||
ModeBsx,
|
||||
ModeSufamiTurbo,
|
||||
ModeSuperGameBoy,
|
||||
};
|
||||
|
||||
enum Type {
|
||||
TypeNormal,
|
||||
TypeBsxSlotted,
|
||||
TypeBsxBios,
|
||||
TypeBsx,
|
||||
TypeSufamiTurboBios,
|
||||
TypeSufamiTurbo,
|
||||
TypeSuperGameBoy1Bios,
|
||||
TypeSuperGameBoy2Bios,
|
||||
TypeGameBoy,
|
||||
TypeUnknown,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
};
|
||||
|
||||
enum MemoryMapper {
|
||||
LoROM,
|
||||
HiROM,
|
||||
ExLoROM,
|
||||
ExHiROM,
|
||||
SuperFXROM,
|
||||
SA1ROM,
|
||||
SPC7110ROM,
|
||||
BSCLoROM,
|
||||
BSCHiROM,
|
||||
BSXROM,
|
||||
STROM,
|
||||
};
|
||||
|
||||
enum DSP1MemoryMapper {
|
||||
DSP1Unmapped,
|
||||
DSP1LoROM1MB,
|
||||
DSP1LoROM2MB,
|
||||
DSP1HiROM,
|
||||
};
|
||||
|
||||
bool loaded; //is a base cartridge inserted?
|
||||
unsigned crc32; //crc32 of all cartridges (base+slot(s))
|
||||
unsigned rom_size;
|
||||
unsigned ram_size;
|
||||
|
||||
Mode mode;
|
||||
Type type;
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
|
||||
bool has_bsx_slot;
|
||||
bool has_superfx;
|
||||
bool has_sa1;
|
||||
bool has_srtc;
|
||||
bool has_sdd1;
|
||||
bool has_spc7110;
|
||||
bool has_spc7110rtc;
|
||||
bool has_cx4;
|
||||
bool has_dsp1;
|
||||
bool has_dsp2;
|
||||
bool has_dsp3;
|
||||
bool has_dsp4;
|
||||
bool has_obc1;
|
||||
bool has_st010;
|
||||
bool has_st011;
|
||||
bool has_st018;
|
||||
};
|
||||
|
||||
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
read_header(data, size);
|
||||
|
||||
string xml;
|
||||
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||
|
||||
if(type == TypeBsx) {
|
||||
markup.append("<cartridge/>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == TypeSufamiTurbo) {
|
||||
markup.append("<cartridge/>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == TypeGameBoy) {
|
||||
markup.append("<cartridge rtc='", gameboy_has_rtc(data, size), "'\n");
|
||||
if(gameboy_ram_size(data, size) > 0) {
|
||||
markup.append(" <ram size='0x", hex(gameboy_ram_size(data, size)), "'>\n");
|
||||
}
|
||||
markup.append("</cartridge>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
|
||||
markup.append("<cartridge region='", region == NTSC ? "NTSC" : "PAL", "'>\n");
|
||||
|
||||
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <icd2 revision='1'>\n"
|
||||
" <map address='00-3f:6000-7fff'/>\n"
|
||||
" <map address='80-bf:6000-7fff'/>\n"
|
||||
" </icd2>\n"
|
||||
);
|
||||
|
||||
else if(has_cx4) markup.append(
|
||||
" <hitachidsp model='HG51B169' frequency='20000000' firmware='cx4.rom' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n"
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <mmio>\n"
|
||||
" <map address='00-3f:6000-7fff'/>\n"
|
||||
" <map address='80-bf:6000-7fff'/>\n"
|
||||
" </mmio>\n"
|
||||
" </hitachidsp>\n"
|
||||
);
|
||||
|
||||
else if(has_spc7110) {
|
||||
markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='shadow' address='00-0f:8000-ffff'/>\n"
|
||||
" <map mode='shadow' address='80-bf:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='c0-cf:0000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <spc7110>\n"
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='00:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='30:6000-7fff'/>\n"
|
||||
" </ram>\n"
|
||||
" <mmio>\n"
|
||||
" <map address='00-3f:4800-483f'/>\n"
|
||||
" <map address='80-bf:4800-483f'/>\n"
|
||||
" </mmio>\n"
|
||||
" <mcu>\n"
|
||||
" <map address='d0-ff:0000-ffff' offset='0x100000' size='0x", hex(size - 0x100000), "'/>\n"
|
||||
" </mcu>\n"
|
||||
" <dcu>\n"
|
||||
" <map address='50:0000-ffff'/>\n"
|
||||
" </dcu>\n"
|
||||
);
|
||||
if(has_spc7110rtc) markup.append(
|
||||
" <rtc>\n"
|
||||
" <map address='00-3f:4840-4842'/>\n"
|
||||
" <map address='80-bf:4840-4842'/>\n"
|
||||
" </rtc>\n"
|
||||
);
|
||||
markup.append(
|
||||
" </spc7110>\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == LoROM) {
|
||||
markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='70-7f:", range, "'/>\n"
|
||||
" <map mode='linear' address='f0-ff:", range, "'/>\n"
|
||||
" </ram>\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == HiROM) {
|
||||
markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='shadow' address='00-3f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='40-7f:0000-ffff'/>\n"
|
||||
" <map mode='shadow' address='80-bf:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='c0-ff:0000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='70-7f:", range, "'/>\n"
|
||||
" </ram>\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExLoROM) {
|
||||
markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-3f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='40-7f:0000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-bf:8000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='70-7f:0000-7fff'/>\n"
|
||||
" </ram>\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExHiROM) {
|
||||
markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='shadow' address='00-3f:8000-ffff' offset='0x400000'/>\n"
|
||||
" <map mode='linear' address='40-7f:0000-ffff' offset='0x400000'/>\n"
|
||||
" <map mode='shadow' address='80-bf:8000-ffff' offset='0x000000'/>\n"
|
||||
" <map mode='linear' address='c0-ff:0000-ffff' offset='0x000000'/>\n"
|
||||
" </rom>\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='70-7f:", range, "'/>\n"
|
||||
" </ram>\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == SuperFXROM) markup.append(
|
||||
" <superfx revision='2'>\n"
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-3f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='40-5f:0000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-bf:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='c0-df:0000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='00-3f:6000-7fff' size='0x2000'/>\n"
|
||||
" <map mode='linear' address='60-7f:0000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-bf:6000-7fff' size='0x2000'/>\n"
|
||||
" <map mode='linear' address='e0-ff:0000-ffff'/>\n"
|
||||
" </ram>\n"
|
||||
" <mmio>\n"
|
||||
" <map address='00-3f:3000-32ff'/>\n"
|
||||
" <map address='80-bf:3000-32ff'/>\n"
|
||||
" </mmio>\n"
|
||||
" </superfx>\n"
|
||||
);
|
||||
|
||||
else if(mapper == SA1ROM) markup.append(
|
||||
" <sa1>\n"
|
||||
" <mcu>\n"
|
||||
" <rom>\n"
|
||||
" <map mode='direct' address='00-3f:8000-ffff'/>\n"
|
||||
" <map mode='direct' address='80-bf:8000-ffff'/>\n"
|
||||
" <map mode='direct' address='c0-ff:0000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <ram>\n"
|
||||
" <map mode='direct' address='00-3f:6000-7fff'/>\n"
|
||||
" <map mode='direct' address='80-bf:6000-7fff'/>\n"
|
||||
" </ram>\n"
|
||||
" </mcu>\n"
|
||||
" <iram size='0x800'>\n"
|
||||
" <map mode='linear' address='00-3f:3000-37ff'/>\n"
|
||||
" <map mode='linear' address='80-bf:3000-37ff'/>\n"
|
||||
" </iram>\n"
|
||||
" <bwram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='40-4f:0000-ffff'/>\n"
|
||||
" </bwram>\n"
|
||||
" <mmio>\n"
|
||||
" <map address='00-3f:2200-23ff'/>\n"
|
||||
" <map address='80-bf:2200-23ff'/>\n"
|
||||
" </mmio>\n"
|
||||
" </sa1>\n"
|
||||
);
|
||||
|
||||
else if(mapper == BSCLoROM) markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-1f:8000-ffff' offset='0x000000'/>\n"
|
||||
" <map mode='linear' address='20-3f:8000-ffff' offset='0x100000'/>\n"
|
||||
" <map mode='linear' address='80-9f:8000-ffff' offset='0x200000'/>\n"
|
||||
" <map mode='linear' address='a0-bf:8000-ffff' offset='0x100000'/>\n"
|
||||
" </rom>\n"
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='70-7f:0000-7fff'/>\n"
|
||||
" <map mode='linear' address='f0-ff:0000-7fff'/>\n"
|
||||
" </ram>\n"
|
||||
" <bsx>\n"
|
||||
" <slot>\n"
|
||||
" <map mode='linear' address='c0-ef:0000-ffff'/>\n"
|
||||
" </slot>\n"
|
||||
" </bsx>\n"
|
||||
);
|
||||
|
||||
else if(mapper == BSCHiROM) markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='shadow' address='00-1f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='40-5f:0000-ffff'/>\n"
|
||||
" <map mode='shadow' address='80-9f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='c0-df:0000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <ram size='0x", hex(ram_size), "'>\n"
|
||||
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
|
||||
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
|
||||
" </ram>\n"
|
||||
" <bsx>\n"
|
||||
" <slot>\n"
|
||||
" <map mode='shadow' address='20-3f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='60-7f:0000-ffff'/>\n"
|
||||
" <map mode='shadow' address='a0-bf:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='e0-ff:0000-ffff'/>\n"
|
||||
" </slot>\n"
|
||||
" </bsx>\n"
|
||||
);
|
||||
|
||||
else if(mapper == BSXROM) markup.append(
|
||||
" <bsx>\n"
|
||||
" <mcu>\n"
|
||||
" <map address='00-3f:8000-ffff'/>\n"
|
||||
" <map address='80-bf:8000-ffff'/>\n"
|
||||
" <map address='40-7f:0000-ffff'/>\n"
|
||||
" <map address='c0-ff:0000-ffff'/>\n"
|
||||
" <map address='20-3f:6000-7fff'/>\n"
|
||||
" </mcu>\n"
|
||||
" <mmio>\n"
|
||||
" <map address='00-3f:5000-5fff'/>\n"
|
||||
" <map address='80-bf:5000-5fff'/>\n"
|
||||
" </mmio>\n"
|
||||
" </bsx>\n"
|
||||
);
|
||||
|
||||
else if(mapper == STROM) markup.append(
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-1f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-9f:8000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <sufamiturbo>\n"
|
||||
" <slot id='A'>\n"
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='20-3f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='a0-bf:8000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <ram size='0x20000'>\n"
|
||||
" <map mode='linear' address='60-63:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='e0-e3:8000-ffff'/>\n"
|
||||
" </ram>\n"
|
||||
" </slot>\n"
|
||||
" <slot id='B'>\n"
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='40-5f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='c0-df:8000-ffff'/>\n"
|
||||
" </rom>\n"
|
||||
" <ram size='0x20000'>\n"
|
||||
" <map mode='linear' address='70-73:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='f0-f3:8000-ffff'/>\n"
|
||||
" </ram>\n"
|
||||
" </slot>\n"
|
||||
" </sufamiturbo>\n"
|
||||
);
|
||||
|
||||
if(has_srtc) markup.append(
|
||||
" <srtc>\n"
|
||||
" <map address='00-3f:2800-2801'/>\n"
|
||||
" <map address='80-bf:2800-2801'/>\n"
|
||||
" </srtc>\n"
|
||||
);
|
||||
|
||||
if(has_sdd1) markup.append(
|
||||
" <sdd1>\n"
|
||||
" <mcu>\n"
|
||||
" <map address='c0-ff:0000-ffff'/>\n"
|
||||
" </mcu>\n"
|
||||
" <mmio>\n"
|
||||
" <map address='00-3f:4800-4807'/>\n"
|
||||
" <map address='80-bf:4800-4807'/>\n"
|
||||
" </mmio>\n"
|
||||
" </sdd1>\n"
|
||||
);
|
||||
|
||||
if(has_obc1) markup.append(
|
||||
" <obc1>\n"
|
||||
" <map address='00-3f:6000-7fff'/>\n"
|
||||
" <map address='80-bf:6000-7fff'/>\n"
|
||||
" </obc1>\n"
|
||||
);
|
||||
|
||||
if(has_dsp1) {
|
||||
//91e87d11e1c30d172556bed2211cce2efa94ba595f58c5d264809ef4d363a97b dsp1.rom
|
||||
markup.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp1b.rom' sha256='d789cb3c36b05c0b23b6c6f23be7aa37c6e78b6ee9ceac8d2d2aa9d8c4d35fa9'>\n");
|
||||
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
|
||||
" <dr>\n"
|
||||
" <map address='20-3f:8000-bfff'/>\n"
|
||||
" <map address='a0-bf:8000-bfff'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='20-3f:c000-ffff'/>\n"
|
||||
" <map address='a0-bf:c000-ffff'/>\n"
|
||||
" </sr>\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
|
||||
" <dr>\n"
|
||||
" <map address='60-6f:0000-3fff'/>\n"
|
||||
" <map address='e0-ef:0000-3fff'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='60-6f:4000-7fff'/>\n"
|
||||
" <map address='e0-ef:4000-7fff'/>\n"
|
||||
" </sr>\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1HiROM) markup.append(
|
||||
" <dr>\n"
|
||||
" <map address='00-1f:6000-6fff'/>\n"
|
||||
" <map address='80-9f:6000-6fff'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='00-1f:7000-7fff'/>\n"
|
||||
" <map address='80-9f:7000-7fff'/>\n"
|
||||
" </sr>\n"
|
||||
);
|
||||
markup.append(" </necdsp>\n");
|
||||
}
|
||||
|
||||
if(has_dsp2) markup.append(
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp2.rom' sha256='03ef4ef26c9f701346708cb5d07847b5203cf1b0818bf2930acd34510ffdd717'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='20-3f:8000-bfff'/>\n"
|
||||
" <map address='a0-bf:8000-bfff'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='20-3f:c000-ffff'/>\n"
|
||||
" <map address='a0-bf:c000-ffff'/>\n"
|
||||
" </sr>\n"
|
||||
" </necdsp>\n"
|
||||
);
|
||||
|
||||
if(has_dsp3) markup.append(
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp3.rom' sha256='0971b08f396c32e61989d1067dddf8e4b14649d548b2188f7c541b03d7c69e4e'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='20-3f:8000-bfff'/>\n"
|
||||
" <map address='a0-bf:8000-bfff'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='20-3f:c000-ffff'/>\n"
|
||||
" <map address='a0-bf:c000-ffff'/>\n"
|
||||
" </sr>\n"
|
||||
" </necdsp>\n"
|
||||
);
|
||||
|
||||
if(has_dsp4) markup.append(
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp4.rom' sha256='752d03b2d74441e430b7f713001fa241f8bbcfc1a0d890ed4143f174dbe031da'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='30-3f:8000-bfff'/>\n"
|
||||
" <map address='b0-bf:8000-bfff'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='30-3f:c000-ffff'/>\n"
|
||||
" <map address='b0-bf:c000-ffff'/>\n"
|
||||
" </sr>\n"
|
||||
" </necdsp>\n"
|
||||
);
|
||||
|
||||
if(has_st010) markup.append(
|
||||
" <necdsp model='uPD96050' frequency='10000000' firmware='st010.rom' sha256='fa9bced838fedea11c6f6ace33d1878024bdd0d02cc9485899d0bdd4015ec24c'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='60:0000'/>\n"
|
||||
" <map address='e0:0000'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='60:0001'/>\n"
|
||||
" <map address='e0:0001'/>\n"
|
||||
" </sr>\n"
|
||||
" <dp>\n"
|
||||
" <map address='68-6f:0000-0fff'/>\n"
|
||||
" <map address='e8-ef:0000-0fff'/>\n"
|
||||
" </dp>\n"
|
||||
" </necdsp>\n"
|
||||
);
|
||||
|
||||
if(has_st011) markup.append(
|
||||
" <necdsp model='uPD96050' frequency='15000000' firmware='st011.rom' sha256='8b2b3f3f3e6e29f4d21d8bc736b400bc988b7d2214ebee15643f01c1fee2f364'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='60:0000'/>\n"
|
||||
" <map address='e0:0000'/>\n"
|
||||
" </dr>\n"
|
||||
" <sr>\n"
|
||||
" <map address='60:0001'/>\n"
|
||||
" <map address='e0:0001'/>\n"
|
||||
" </sr>\n"
|
||||
" <dp>\n"
|
||||
" <map address='68-6f:0000-0fff'/>\n"
|
||||
" <map address='e8-ef:0000-0fff'/>\n"
|
||||
" </dp>\n"
|
||||
" </necdsp>\n"
|
||||
);
|
||||
|
||||
if(has_st018) markup.append(
|
||||
" <armdsp firmware='st018.rom' frequency='21477272' sha256='6df209ab5d2524d1839c038be400ae5eb20dafc14a3771a3239cd9e8acd53806'>\n"
|
||||
" <map address='00-3f:3800-38ff'/>\n"
|
||||
" <map address='80-bf:3800-38ff'/>\n"
|
||||
" </armdsp>\n"
|
||||
);
|
||||
|
||||
markup.append("</cartridge>\n");
|
||||
}
|
||||
|
||||
void SuperFamicomCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
rom_size = size;
|
||||
ram_size = 0;
|
||||
|
||||
has_bsx_slot = false;
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
has_srtc = false;
|
||||
has_sdd1 = false;
|
||||
has_spc7110 = false;
|
||||
has_spc7110rtc = false;
|
||||
has_cx4 = false;
|
||||
has_dsp1 = false;
|
||||
has_dsp2 = false;
|
||||
has_dsp3 = false;
|
||||
has_dsp4 = false;
|
||||
has_obc1 = false;
|
||||
has_st010 = false;
|
||||
has_st011 = false;
|
||||
has_st018 = false;
|
||||
|
||||
//=====================
|
||||
//detect Game Boy carts
|
||||
//=====================
|
||||
|
||||
if(size >= 0x0140) {
|
||||
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
|
||||
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
|
||||
type = TypeGameBoy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(size < 32768) {
|
||||
type = TypeUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned index = find_header(data, size);
|
||||
const uint8_t mapperid = data[index + Mapper];
|
||||
const uint8_t rom_type = data[index + RomType];
|
||||
const uint8_t rom_size = data[index + RomSize];
|
||||
const uint8_t company = data[index + Company];
|
||||
const uint8_t regionid = data[index + CartRegion] & 0x7f;
|
||||
|
||||
ram_size = 1024 << (data[index + RamSize] & 7);
|
||||
if(ram_size == 1024) ram_size = 0; //no RAM present
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
|
||||
|
||||
//=======================
|
||||
//detect BS-X flash carts
|
||||
//=======================
|
||||
|
||||
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
||||
if(data[index + 0x14] == 0x00) {
|
||||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
type = TypeBsx;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================
|
||||
//detect Sufami Turbo carts
|
||||
//=========================
|
||||
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
type = TypeSufamiTurboBios;
|
||||
} else {
|
||||
type = TypeSufamiTurbo;
|
||||
}
|
||||
mapper = STROM;
|
||||
region = NTSC; //Sufami Turbo only released in Japan
|
||||
return; //RAM size handled outside this routine
|
||||
}
|
||||
|
||||
//==========================
|
||||
//detect Super Game Boy BIOS
|
||||
//==========================
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
|
||||
type = TypeSuperGameBoy2Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
|
||||
type = TypeSuperGameBoy1Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
//=====================
|
||||
//detect standard carts
|
||||
//=====================
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
if(data[index - 14] == 'Z') {
|
||||
if(data[index - 11] == 'J') {
|
||||
uint8_t n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
has_bsx_slot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsx_slot) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
type = TypeBsxBios;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
} else {
|
||||
type = TypeBsxSlotted;
|
||||
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
region = NTSC; //BS-X slotted cartridges only released in Japan
|
||||
}
|
||||
} else {
|
||||
//standard cart
|
||||
type = TypeNormal;
|
||||
|
||||
if(index == 0x7fc0 && size >= 0x401000) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapperid == 0x32) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
mapper = ExHiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
has_superfx = true;
|
||||
mapper = SuperFXROM;
|
||||
ram_size = 1024 << (data[index - 3] & 7);
|
||||
if(ram_size == 1024) ram_size = 0;
|
||||
}
|
||||
|
||||
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
||||
has_sa1 = true;
|
||||
mapper = SA1ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x35 && rom_type == 0x55) {
|
||||
has_srtc = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
has_sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
has_spc7110 = true;
|
||||
has_spc7110rtc = (rom_type == 0xf9);
|
||||
mapper = SPC7110ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0xf3) {
|
||||
has_cx4 = true;
|
||||
}
|
||||
|
||||
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(has_dsp1 == true) {
|
||||
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
|
||||
dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapperid & 0x2f) == 0x20) {
|
||||
dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapperid & 0x2f) == 0x21) {
|
||||
dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0x05) {
|
||||
has_dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
has_dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x03) {
|
||||
has_dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x25) {
|
||||
has_obc1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
has_st010 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
has_st011 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf5) {
|
||||
has_st018 = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
return 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
return 0x00ffc0;
|
||||
} else {
|
||||
return 0x40ffc0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
|
||||
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
|
||||
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
|
||||
|
||||
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
||||
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
|
||||
|
||||
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
||||
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
||||
if(resetvector < 0x8000) return 0;
|
||||
|
||||
//some images duplicate the header in multiple locations, and others have completely
|
||||
//invalid header information that cannot be relied upon.
|
||||
//below code will analyze the first opcode executed at the specified reset vector to
|
||||
//determine the probability that this is the correct header.
|
||||
|
||||
//most likely opcodes
|
||||
if(resetop == 0x78 //sei
|
||||
|| resetop == 0x18 //clc (clc; xce)
|
||||
|| resetop == 0x38 //sec (sec; xce)
|
||||
|| resetop == 0x9c //stz $nnnn (stz $4200)
|
||||
|| resetop == 0x4c //jmp $nnnn
|
||||
|| resetop == 0x5c //jml $nnnnnn
|
||||
) score += 8;
|
||||
|
||||
//plausible opcodes
|
||||
if(resetop == 0xc2 //rep #$nn
|
||||
|| resetop == 0xe2 //sep #$nn
|
||||
|| resetop == 0xad //lda $nnnn
|
||||
|| resetop == 0xae //ldx $nnnn
|
||||
|| resetop == 0xac //ldy $nnnn
|
||||
|| resetop == 0xaf //lda $nnnnnn
|
||||
|| resetop == 0xa9 //lda #$nn
|
||||
|| resetop == 0xa2 //ldx #$nn
|
||||
|| resetop == 0xa0 //ldy #$nn
|
||||
|| resetop == 0x20 //jsr $nnnn
|
||||
|| resetop == 0x22 //jsl $nnnnnn
|
||||
) score += 4;
|
||||
|
||||
//implausible opcodes
|
||||
if(resetop == 0x40 //rti
|
||||
|| resetop == 0x60 //rts
|
||||
|| resetop == 0x6b //rtl
|
||||
|| resetop == 0xcd //cmp $nnnn
|
||||
|| resetop == 0xec //cpx $nnnn
|
||||
|| resetop == 0xcc //cpy $nnnn
|
||||
) score -= 4;
|
||||
|
||||
//least likely opcodes
|
||||
if(resetop == 0x00 //brk #$nn
|
||||
|| resetop == 0x02 //cop #$nn
|
||||
|| resetop == 0xdb //stp
|
||||
|| resetop == 0x42 //wdm
|
||||
|| resetop == 0xff //sbc $nnnnnn,x
|
||||
) score -= 8;
|
||||
|
||||
//at times, both the header and reset vector's first opcode will match ...
|
||||
//fallback and rely on info validity in these cases to determine more likely header.
|
||||
|
||||
//a valid checksum is the biggest indicator of a valid header.
|
||||
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
|
||||
|
||||
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
|
||||
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
|
||||
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
||||
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
||||
|
||||
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
|
||||
if(data[addr + RomType] < 0x08) score++;
|
||||
if(data[addr + RomSize] < 0x10) score++;
|
||||
if(data[addr + RamSize] < 0x08) score++;
|
||||
if(data[addr + CartRegion] < 14) score++;
|
||||
|
||||
if(score < 0) score = 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
|
||||
if(size < 512) return 0;
|
||||
switch(data[0x0149]) {
|
||||
case 0x00: return 0 * 1024;
|
||||
case 0x01: return 8 * 1024;
|
||||
case 0x02: return 8 * 1024;
|
||||
case 0x03: return 32 * 1024;
|
||||
case 0x04: return 128 * 1024;
|
||||
case 0x05: return 128 * 1024;
|
||||
default: return 128 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
bool SuperFamicomCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
|
||||
if(size < 512) return false;
|
||||
if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,105 @@
|
|||
#ifndef NALL_SNES_USART_HPP
|
||||
#define NALL_SNES_USART_HPP
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/serial.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#define usartproc dllexport
|
||||
|
||||
static nall::function<bool ()> usart_quit;
|
||||
static nall::function<void (unsigned milliseconds)> usart_usleep;
|
||||
static nall::function<bool ()> usart_readable;
|
||||
static nall::function<uint8_t ()> usart_read;
|
||||
static nall::function<bool ()> usart_writable;
|
||||
static nall::function<void (uint8_t data)> usart_write;
|
||||
|
||||
extern "C" usartproc void usart_init(
|
||||
nall::function<bool ()> quit,
|
||||
nall::function<void (unsigned milliseconds)> usleep,
|
||||
nall::function<bool ()> readable,
|
||||
nall::function<uint8_t ()> read,
|
||||
nall::function<bool ()> writable,
|
||||
nall::function<void (uint8_t data)> write
|
||||
) {
|
||||
usart_quit = quit;
|
||||
usart_usleep = usleep;
|
||||
usart_readable = readable;
|
||||
usart_read = read;
|
||||
usart_writable = writable;
|
||||
usart_write = write;
|
||||
}
|
||||
|
||||
extern "C" usartproc void usart_main();
|
||||
|
||||
//
|
||||
|
||||
static nall::serial usart;
|
||||
static bool usart_is_virtual = true;
|
||||
static bool usart_sigint = false;
|
||||
|
||||
static bool usart_virtual() {
|
||||
return usart_is_virtual;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
static bool usarthw_quit() {
|
||||
return usart_sigint;
|
||||
}
|
||||
|
||||
static void usarthw_usleep(unsigned milliseconds) {
|
||||
usleep(milliseconds);
|
||||
}
|
||||
|
||||
static bool usarthw_readable() {
|
||||
return usart.readable();
|
||||
}
|
||||
|
||||
static uint8_t usarthw_read() {
|
||||
while(true) {
|
||||
uint8_t buffer[1];
|
||||
signed length = usart.read((uint8_t*)&buffer, 1);
|
||||
if(length > 0) return buffer[0];
|
||||
}
|
||||
}
|
||||
|
||||
static bool usarthw_writable() {
|
||||
return usart.writable();
|
||||
}
|
||||
|
||||
static void usarthw_write(uint8_t data) {
|
||||
uint8_t buffer[1] = { data };
|
||||
usart.write((uint8_t*)&buffer, 1);
|
||||
}
|
||||
|
||||
static void sigint(int) {
|
||||
signal(SIGINT, SIG_DFL);
|
||||
usart_sigint = true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
//requires superuser privileges; otherwise priority = +0
|
||||
setpriority(PRIO_PROCESS, 0, -20);
|
||||
signal(SIGINT, sigint);
|
||||
|
||||
bool result = false;
|
||||
if(argc == 1) result = usart.open("/dev/ttyACM0", 57600, true);
|
||||
if(argc == 2) result = usart.open(argv[1], 57600, true);
|
||||
if(result == false) {
|
||||
printf("error: unable to open USART hardware device\n");
|
||||
return 0;
|
||||
}
|
||||
usart_is_virtual = false;
|
||||
usart_init(usarthw_quit, usarthw_usleep, usarthw_readable, usarthw_read, usarthw_writable, usarthw_write);
|
||||
usart_main();
|
||||
usart.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef NALL_SORT_HPP
|
||||
#define NALL_SORT_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
//class: merge sort
|
||||
//average: O(n log n)
|
||||
//worst: O(n log n)
|
||||
//memory: O(n)
|
||||
//stack: O(log n)
|
||||
//stable?: yes
|
||||
|
||||
//note: merge sort was chosen over quick sort, because:
|
||||
//* it is a stable sort
|
||||
//* it lacks O(n^2) worst-case overhead
|
||||
|
||||
#define NALL_SORT_INSERTION
|
||||
//#define NALL_SORT_SELECTION
|
||||
|
||||
namespace nall {
|
||||
template<typename T, typename Comparator>
|
||||
void sort(T list[], unsigned size, const Comparator &lessthan) {
|
||||
if(size <= 1) return; //nothing to sort
|
||||
|
||||
//use insertion sort to quickly sort smaller blocks
|
||||
if(size < 64) {
|
||||
#if defined(NALL_SORT_INSERTION)
|
||||
for(signed i = 1, j; i < size; i++) {
|
||||
T copy = std::move(list[i]);
|
||||
for(j = i - 1; j >= 0; j--) {
|
||||
if(lessthan(list[j], copy)) break;
|
||||
list[j + 1] = std::move(list[j]);
|
||||
}
|
||||
list[j + 1] = std::move(copy);
|
||||
}
|
||||
#elif defined(NALL_SORT_SELECTION)
|
||||
for(unsigned i = 0; i < size; i++) {
|
||||
unsigned min = i;
|
||||
for(unsigned j = i + 1; j < size; j++) {
|
||||
if(lessthan(list[j], list[min])) min = j;
|
||||
}
|
||||
if(min != i) std::swap(list[i], list[min]);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
//split list in half and recursively sort both
|
||||
unsigned middle = size / 2;
|
||||
sort(list, middle, lessthan);
|
||||
sort(list + middle, size - middle, lessthan);
|
||||
|
||||
//left and right are sorted here; perform merge sort
|
||||
T *buffer = new T[size];
|
||||
unsigned offset = 0, left = 0, right = middle;
|
||||
while(left < middle && right < size) {
|
||||
if(lessthan(list[left], list[right])) {
|
||||
buffer[offset++] = std::move(list[left++]);
|
||||
} else {
|
||||
buffer[offset++] = std::move(list[right++]);
|
||||
}
|
||||
}
|
||||
while(left < middle) buffer[offset++] = std::move(list[left++]);
|
||||
while(right < size) buffer[offset++] = std::move(list[right++]);
|
||||
|
||||
for(unsigned i = 0; i < size; i++) list[i] = std::move(buffer[i]);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void sort(T list[], unsigned size) {
|
||||
return sort(list, size, [](const T &l, const T &r) { return l < r; });
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef NALL_STDINT_HPP
|
||||
#define NALL_STDINT_HPP
|
||||
|
||||
#include <nall/static.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef NALL_STREAM_HPP
|
||||
#define NALL_STREAM_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/gzip.hpp>
|
||||
#include <nall/http.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/zip.hpp>
|
||||
|
||||
#define NALL_STREAM_INTERNAL_HPP
|
||||
#include <nall/stream/stream.hpp>
|
||||
#include <nall/stream/memory.hpp>
|
||||
#include <nall/stream/mmap.hpp>
|
||||
#include <nall/stream/file.hpp>
|
||||
#include <nall/stream/http.hpp>
|
||||
#include <nall/stream/gzip.hpp>
|
||||
#include <nall/stream/zip.hpp>
|
||||
#include <nall/stream/auto.hpp>
|
||||
#undef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
#define autostream(...) (*makestream(__VA_ARGS__))
|
||||
|
||||
inline std::unique_ptr<stream> makestream(const string &path) {
|
||||
if(path.ibeginswith("http://")) return std::unique_ptr<stream>(new httpstream(path, 80));
|
||||
if(path.iendswith(".gz")) return std::unique_ptr<stream>(new gzipstream(filestream{path}));
|
||||
if(path.iendswith(".zip")) return std::unique_ptr<stream>(new zipstream(filestream{path}));
|
||||
return std::unique_ptr<stream>(new mmapstream(path));
|
||||
}
|
||||
|
||||
inline std::unique_ptr<stream> makestream(uint8_t *data, unsigned size) {
|
||||
return std::unique_ptr<stream>(new memorystream(data, size));
|
||||
}
|
||||
|
||||
inline std::unique_ptr<stream> makestream(const uint8_t *data, unsigned size) {
|
||||
return std::unique_ptr<stream>(new memorystream(data, size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct filestream : stream {
|
||||
inline bool seekable() const { return true; }
|
||||
inline bool readable() const { return true; }
|
||||
inline bool writable() const { return pwritable; }
|
||||
inline bool randomaccess() const { return false; }
|
||||
|
||||
inline unsigned size() const { return pfile.size(); }
|
||||
inline unsigned offset() const { return pfile.offset(); }
|
||||
inline void seek(unsigned offset) const { pfile.seek(offset); }
|
||||
|
||||
inline uint8_t read() const { return pfile.read(); }
|
||||
inline void write(uint8_t data) const { pfile.write(data); }
|
||||
|
||||
inline filestream(const string &filename) {
|
||||
pfile.open(filename, file::mode::readwrite);
|
||||
pwritable = pfile.open();
|
||||
if(!pwritable) pfile.open(filename, file::mode::read);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable file pfile;
|
||||
bool pwritable;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct gzipstream : memorystream {
|
||||
inline gzipstream(const stream &stream) {
|
||||
unsigned size = stream.size();
|
||||
uint8_t *data = new uint8_t[size];
|
||||
stream.read(data, size);
|
||||
|
||||
gzip archive;
|
||||
bool result = archive.decompress(data, size);
|
||||
delete[] data;
|
||||
if(result == false) return;
|
||||
|
||||
psize = archive.size;
|
||||
pdata = new uint8_t[psize];
|
||||
memcpy(pdata, archive.data, psize);
|
||||
}
|
||||
|
||||
inline ~gzipstream() {
|
||||
if(pdata) delete[] pdata;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct httpstream : stream {
|
||||
inline bool seekable() const { return true; }
|
||||
inline bool readable() const { return true; }
|
||||
inline bool writable() const { return true; }
|
||||
inline bool randomaccess() const { return true; }
|
||||
|
||||
inline unsigned size() const { return psize; }
|
||||
inline unsigned offset() const { return poffset; }
|
||||
inline void seek(unsigned offset) const { poffset = offset; }
|
||||
|
||||
inline uint8_t read() const { return pdata[poffset++]; }
|
||||
inline void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||
|
||||
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||
|
||||
inline httpstream(const string &url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
|
||||
string uri = url;
|
||||
uri.ltrim<1>("http://");
|
||||
lstring part = uri.split<1>("/");
|
||||
part[1] = { "/", part[1] };
|
||||
|
||||
http connection;
|
||||
if(connection.connect(part[0], port) == false) return;
|
||||
connection.download(part[1], pdata, psize);
|
||||
}
|
||||
|
||||
inline ~httpstream() {
|
||||
if(pdata) delete[] pdata;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable uint8_t *pdata;
|
||||
mutable unsigned psize, poffset;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct memorystream : stream {
|
||||
inline bool seekable() const { return true; }
|
||||
inline bool readable() const { return true; }
|
||||
inline bool writable() const { return pwritable; }
|
||||
inline bool randomaccess() const { return true; }
|
||||
|
||||
inline unsigned size() const { return psize; }
|
||||
inline unsigned offset() const { return poffset; }
|
||||
inline void seek(unsigned offset) const { poffset = offset; }
|
||||
|
||||
inline uint8_t read() const { return pdata[poffset++]; }
|
||||
inline void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||
|
||||
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||
|
||||
inline memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {}
|
||||
|
||||
inline memorystream(uint8_t *data, unsigned size) {
|
||||
pdata = data, psize = size, poffset = 0;
|
||||
pwritable = true;
|
||||
}
|
||||
|
||||
inline memorystream(const uint8_t *data, unsigned size) {
|
||||
pdata = (uint8_t*)data, psize = size, poffset = 0;
|
||||
pwritable = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable uint8_t *pdata;
|
||||
mutable unsigned psize, poffset, pwritable;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct mmapstream : stream {
|
||||
inline bool seekable() const { return true; }
|
||||
inline bool readable() const { return true; }
|
||||
inline bool writable() const { return pwritable; }
|
||||
inline bool randomaccess() const { return false; }
|
||||
|
||||
inline unsigned size() const { return pmmap.size(); }
|
||||
inline unsigned offset() const { return poffset; }
|
||||
inline void seek(unsigned offset) const { poffset = offset; }
|
||||
|
||||
inline uint8_t read() const { return pdata[poffset++]; }
|
||||
inline void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||
|
||||
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||
|
||||
inline mmapstream(const string &filename) {
|
||||
pmmap.open(filename, filemap::mode::readwrite);
|
||||
pwritable = pmmap.open();
|
||||
if(!pwritable) pmmap.open(filename, filemap::mode::read);
|
||||
pdata = pmmap.data(), poffset = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable filemap pmmap;
|
||||
mutable uint8_t *pdata;
|
||||
mutable unsigned pwritable, poffset;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef NALL_STREAM_STREAM_HPP
|
||||
#define NALL_STREAM_STREAM_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct stream {
|
||||
virtual bool seekable() const = 0;
|
||||
virtual bool readable() const = 0;
|
||||
virtual bool writable() const = 0;
|
||||
virtual bool randomaccess() const = 0;
|
||||
|
||||
virtual unsigned size() const = 0;
|
||||
virtual unsigned offset() const = 0;
|
||||
virtual void seek(unsigned offset) const = 0;
|
||||
|
||||
virtual uint8_t read() const = 0;
|
||||
virtual void write(uint8_t data) const = 0;
|
||||
|
||||
inline virtual uint8_t read(unsigned) const { return 0; }
|
||||
inline virtual void write(unsigned, uint8_t) const {}
|
||||
|
||||
inline bool end() const {
|
||||
return offset() >= size();
|
||||
}
|
||||
|
||||
inline void copy(uint8_t *&data, unsigned &length) const {
|
||||
seek(0);
|
||||
length = size();
|
||||
data = new uint8_t[length];
|
||||
for(unsigned n = 0; n < length; n++) data[n] = read();
|
||||
}
|
||||
|
||||
inline uintmax_t readl(unsigned length = 1) const {
|
||||
uintmax_t data = 0, shift = 0;
|
||||
while(length--) { data |= read() << shift; shift += 8; }
|
||||
return data;
|
||||
}
|
||||
|
||||
inline uintmax_t readm(unsigned length = 1) const {
|
||||
uintmax_t data = 0;
|
||||
while(length--) data = (data << 8) | read();
|
||||
return data;
|
||||
}
|
||||
|
||||
inline void read(uint8_t *data, unsigned length) const {
|
||||
while(length--) *data++ = read();
|
||||
}
|
||||
|
||||
inline void writel(uintmax_t data, unsigned length = 1) const {
|
||||
while(length--) {
|
||||
write(data);
|
||||
data >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
inline void writem(uintmax_t data, unsigned length = 1) const {
|
||||
uintmax_t shift = 8 * length;
|
||||
while(length--) {
|
||||
shift -= 8;
|
||||
write(data >> shift);
|
||||
}
|
||||
}
|
||||
|
||||
inline void write(const uint8_t *data, unsigned length) const {
|
||||
while(length--) write(*data++);
|
||||
}
|
||||
|
||||
struct byte {
|
||||
inline operator uint8_t() const { return s.read(offset); }
|
||||
inline byte& operator=(uint8_t data) { s.write(offset, data); }
|
||||
inline byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
|
||||
|
||||
private:
|
||||
const stream &s;
|
||||
const unsigned offset;
|
||||
};
|
||||
|
||||
inline byte operator[](unsigned offset) const {
|
||||
return byte(*this, offset);
|
||||
}
|
||||
|
||||
inline stream() {}
|
||||
inline virtual ~stream() {}
|
||||
stream(const stream&) = delete;
|
||||
stream& operator=(const stream&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct zipstream : memorystream {
|
||||
inline zipstream(const stream &stream, const string &filter = "*") {
|
||||
unsigned size = stream.size();
|
||||
uint8_t *data = new uint8_t[size];
|
||||
stream.read(data, size);
|
||||
|
||||
zip archive;
|
||||
if(archive.open(data, size) == false) return;
|
||||
delete[] data;
|
||||
|
||||
for(auto &file : archive.file) {
|
||||
if(file.name.wildcard(filter)) {
|
||||
archive.extract(file, pdata, psize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline ~zipstream() {
|
||||
if(pdata) delete[] pdata;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,34 +1,50 @@
|
|||
#ifndef NALL_STRING_HPP
|
||||
#define NALL_STRING_HPP
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/atoi.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <nall/varint.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
#include <nall/windows/utf8.hpp>
|
||||
|
||||
#define NALL_STRING_INTERNAL_HPP
|
||||
#include <nall/string/base.hpp>
|
||||
#include <nall/string/bml.hpp>
|
||||
#include <nall/string/bsv.hpp>
|
||||
#include <nall/string/core.hpp>
|
||||
#include <nall/string/cast.hpp>
|
||||
#include <nall/string/compare.hpp>
|
||||
#include <nall/string/convert.hpp>
|
||||
#include <nall/string/core.hpp>
|
||||
#include <nall/string/cstring.hpp>
|
||||
#include <nall/string/filename.hpp>
|
||||
#include <nall/string/math.hpp>
|
||||
#include <nall/string/math-fixed-point.hpp>
|
||||
#include <nall/string/math-floating-point.hpp>
|
||||
#include <nall/string/platform.hpp>
|
||||
#include <nall/string/strl.hpp>
|
||||
#include <nall/string/strm.hpp>
|
||||
#include <nall/string/strpos.hpp>
|
||||
#include <nall/string/trim.hpp>
|
||||
#include <nall/string/replace.hpp>
|
||||
#include <nall/string/split.hpp>
|
||||
#include <nall/string/utf8.hpp>
|
||||
#include <nall/string/utility.hpp>
|
||||
#include <nall/string/variadic.hpp>
|
||||
#include <nall/string/wildcard.hpp>
|
||||
#include <nall/string/wrapper.hpp>
|
||||
#include <nall/string/xml.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<> struct has_length<string> { enum { value = true }; };
|
||||
template<> struct has_size<lstring> { enum { value = true }; };
|
||||
}
|
||||
#undef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
#endif
|
|
@ -0,0 +1,202 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
struct cstring;
|
||||
struct string;
|
||||
struct lstring;
|
||||
template<typename T> inline const char* to_string(T);
|
||||
|
||||
struct cstring {
|
||||
inline operator const char*() const;
|
||||
inline unsigned length() const;
|
||||
inline bool operator==(const char*) const;
|
||||
inline bool operator!=(const char*) const;
|
||||
inline optional<unsigned> position(const char *key) const;
|
||||
inline optional<unsigned> iposition(const char *key) const;
|
||||
inline cstring& operator=(const char *data);
|
||||
inline cstring(const char *data);
|
||||
inline cstring();
|
||||
|
||||
protected:
|
||||
const char *data;
|
||||
};
|
||||
|
||||
struct string {
|
||||
inline void reserve(unsigned);
|
||||
inline bool empty() const;
|
||||
|
||||
template<typename... Args> inline string& assign(Args&&... args);
|
||||
template<typename... Args> inline string& append(Args&&... args);
|
||||
|
||||
inline bool readfile(const string&);
|
||||
|
||||
template<unsigned Limit = 0> inline string& replace(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline string& ireplace(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline string& qreplace(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline string& iqreplace(const char*, const char*);
|
||||
|
||||
inline unsigned length() const;
|
||||
inline unsigned capacity() const;
|
||||
|
||||
template<unsigned Limit = 0> inline lstring split(const char*) const;
|
||||
template<unsigned Limit = 0> inline lstring isplit(const char*) const;
|
||||
template<unsigned Limit = 0> inline lstring qsplit(const char*) const;
|
||||
template<unsigned Limit = 0> inline lstring iqsplit(const char*) const;
|
||||
|
||||
inline bool equals(const char*) const;
|
||||
inline bool iequals(const char*) const;
|
||||
|
||||
inline bool wildcard(const char*) const;
|
||||
inline bool iwildcard(const char*) const;
|
||||
|
||||
inline bool beginswith(const char*) const;
|
||||
inline bool ibeginswith(const char*) const;
|
||||
inline bool endswith(const char*) const;
|
||||
inline bool iendswith(const char*) const;
|
||||
|
||||
inline string& lower();
|
||||
inline string& upper();
|
||||
inline string& qlower();
|
||||
inline string& qupper();
|
||||
inline string& transform(const char *before, const char *after);
|
||||
|
||||
template<unsigned limit = 0> inline string& ltrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& rtrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& trim(const char *key = " ", const char *rkey = 0);
|
||||
|
||||
inline optional<unsigned> position(const char *key) const;
|
||||
inline optional<unsigned> iposition(const char *key) const;
|
||||
inline optional<unsigned> qposition(const char *key) const;
|
||||
inline optional<unsigned> iqposition(const char *key) const;
|
||||
|
||||
inline operator const char*() const;
|
||||
inline char* operator()();
|
||||
inline char& operator[](int);
|
||||
|
||||
inline bool operator==(const char*) const;
|
||||
inline bool operator!=(const char*) const;
|
||||
inline bool operator< (const char*) const;
|
||||
inline bool operator<=(const char*) const;
|
||||
inline bool operator> (const char*) const;
|
||||
inline bool operator>=(const char*) const;
|
||||
|
||||
inline string& operator=(const string&);
|
||||
inline string& operator=(string&&);
|
||||
|
||||
template<typename... Args> inline string(Args&&... args);
|
||||
inline string(const string&);
|
||||
inline string(string&&);
|
||||
inline ~string();
|
||||
|
||||
inline char* begin() { return &data[0]; }
|
||||
inline char* end() { return &data[length()]; }
|
||||
inline const char* begin() const { return &data[0]; }
|
||||
inline const char* end() const { return &data[length()]; }
|
||||
|
||||
//internal functions
|
||||
inline string& assign_(const char*);
|
||||
inline string& append_(const char*);
|
||||
|
||||
protected:
|
||||
char *data;
|
||||
unsigned size;
|
||||
|
||||
template<unsigned Limit, bool Insensitive, bool Quoted> inline string& ureplace(const char*, const char*);
|
||||
|
||||
#if defined(QSTRING_H)
|
||||
public:
|
||||
inline operator QString() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lstring : vector<string> {
|
||||
inline optional<unsigned> find(const char*) const;
|
||||
inline string concatenate(const char*) const;
|
||||
|
||||
template<unsigned Limit = 0> inline lstring& split(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline lstring& isplit(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline lstring& qsplit(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline lstring& iqsplit(const char*, const char*);
|
||||
|
||||
inline bool operator==(const lstring&) const;
|
||||
inline bool operator!=(const lstring&) const;
|
||||
|
||||
inline lstring();
|
||||
inline lstring(std::initializer_list<string>);
|
||||
|
||||
protected:
|
||||
template<unsigned Limit, bool Insensitive, bool Quoted> inline lstring& usplit(const char*, const char*);
|
||||
};
|
||||
|
||||
//compare.hpp
|
||||
inline char chrlower(char c);
|
||||
inline char chrupper(char c);
|
||||
inline int istrcmp(const char *str1, const char *str2);
|
||||
inline bool strbegin(const char *str, const char *key);
|
||||
inline bool istrbegin(const char *str, const char *key);
|
||||
inline bool strend(const char *str, const char *key);
|
||||
inline bool istrend(const char *str, const char *key);
|
||||
|
||||
//convert.hpp
|
||||
inline char* strlower(char *str);
|
||||
inline char* strupper(char *str);
|
||||
inline char* qstrlower(char *str);
|
||||
inline char* qstrupper(char *str);
|
||||
inline char* strtr(char *dest, const char *before, const char *after);
|
||||
|
||||
//math.hpp
|
||||
inline bool strint(const char *str, int &result);
|
||||
inline bool strmath(const char *str, int &result);
|
||||
|
||||
//platform.hpp
|
||||
inline string realpath(const char *name);
|
||||
inline string userpath();
|
||||
inline string currentpath();
|
||||
|
||||
//strm.hpp
|
||||
inline unsigned strmcpy(char *target, const char *source, unsigned length);
|
||||
inline unsigned strmcat(char *target, const char *source, unsigned length);
|
||||
inline bool strccpy(char *target, const char *source, unsigned length);
|
||||
inline bool strccat(char *target, const char *source, unsigned length);
|
||||
inline void strpcpy(char *&target, const char *source, unsigned &length);
|
||||
|
||||
//strpos.hpp
|
||||
inline optional<unsigned> strpos(const char *str, const char *key);
|
||||
inline optional<unsigned> istrpos(const char *str, const char *key);
|
||||
inline optional<unsigned> qstrpos(const char *str, const char *key);
|
||||
inline optional<unsigned> iqstrpos(const char *str, const char *key);
|
||||
template<bool Insensitive = false, bool Quoted = false> inline optional<unsigned> ustrpos(const char *str, const char *key);
|
||||
|
||||
//trim.hpp
|
||||
template<unsigned limit = 0> inline char* ltrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* rtrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* trim(char *str, const char *key = " ", const char *rkey = 0);
|
||||
|
||||
//utility.hpp
|
||||
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
|
||||
template<bool Quoted, typename T> alwaysinline bool quoteskip(T *&p);
|
||||
template<bool Quoted, typename T> alwaysinline bool quotecopy(char *&t, T *&p);
|
||||
inline string substr(const char *src, unsigned start = 0, unsigned length = ~0u);
|
||||
inline string sha256(const uint8_t *data, unsigned size);
|
||||
|
||||
inline char* integer(char *result, intmax_t value);
|
||||
inline char* decimal(char *result, uintmax_t value);
|
||||
|
||||
template<unsigned length = 0, char padding = ' '> inline string integer(intmax_t value);
|
||||
template<unsigned length = 0, char padding = ' '> inline string linteger(intmax_t value);
|
||||
template<unsigned length = 0, char padding = ' '> inline string decimal(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
|
||||
inline unsigned fp(char *str, long double value);
|
||||
inline string fp(long double value);
|
||||
|
||||
//variadic.hpp
|
||||
template<typename... Args> inline void print(Args&&... args);
|
||||
|
||||
//wildcard.hpp
|
||||
inline bool wildcard(const char *str, const char *pattern);
|
||||
inline bool iwildcard(const char *str, const char *pattern);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,151 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//BML v1.0 parser
|
||||
//revision 0.05
|
||||
|
||||
namespace nall {
|
||||
namespace BML {
|
||||
|
||||
inline static string indent(const char *s, unsigned depth) {
|
||||
array<char> output;
|
||||
do {
|
||||
for(unsigned n = 0; n < depth; n++) output.append('\t');
|
||||
do output.append(*s); while(*s && *s++ != '\n');
|
||||
} while(*s);
|
||||
return output.get();
|
||||
}
|
||||
|
||||
struct Node {
|
||||
cstring name;
|
||||
cstring value;
|
||||
|
||||
private:
|
||||
vector<Node> children;
|
||||
|
||||
inline bool valid(char p) const { //A-Za-z0-9-.
|
||||
return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u;
|
||||
}
|
||||
|
||||
inline unsigned parseDepth(char *&p) {
|
||||
while(*p == '\n' || *p == '#') {
|
||||
while(*p != '\n') *p++ = 0;
|
||||
*p++ = 0; //'\n'
|
||||
}
|
||||
unsigned depth = 0;
|
||||
while(p[depth] == '\t') depth++;
|
||||
return depth;
|
||||
}
|
||||
|
||||
inline void parseName(char *&p) {
|
||||
if(valid(*p) == false) throw "Missing node name";
|
||||
name = p;
|
||||
while(valid(*p)) p++;
|
||||
}
|
||||
|
||||
inline void parseValue(char *&p) {
|
||||
char terminal = *p == ':' ? '\n' : ' '; //':' or '='
|
||||
*p++ = 0;
|
||||
value = p;
|
||||
while(*p && *p != terminal && *p != '\n') p++;
|
||||
}
|
||||
|
||||
inline void parseBlock(char *&p, unsigned depth) {
|
||||
value = p;
|
||||
char *w = p;
|
||||
while(parseDepth(p) > depth) {
|
||||
p += depth + 1;
|
||||
while(*p && *p != '\n') *w++ = *p++;
|
||||
if(*p && *p != '\n') throw "Multi-line value missing line feed";
|
||||
*w++ = *p;
|
||||
}
|
||||
*(w - 1) = 0; //'\n'
|
||||
}
|
||||
|
||||
inline void parseLine(char *&p) {
|
||||
unsigned depth = parseDepth(p);
|
||||
while(*p == '\t') p++;
|
||||
|
||||
parseName(p);
|
||||
bool multiLine = *p == '~';
|
||||
if(multiLine) *p++ = 0;
|
||||
else if(*p == ':' || *p == '=') parseValue(p);
|
||||
if(*p && *p != ' ' && *p != '\n') throw "Invalid character encountered";
|
||||
|
||||
while(*p == ' ') {
|
||||
*p++ = 0;
|
||||
Node node;
|
||||
node.parseName(p);
|
||||
if(*p == ':' || *p == '=') node.parseValue(p);
|
||||
if(*p && *p != ' ' && *p != '\n') throw "Invalid character after node";
|
||||
if(*p == '\n') *p++ = 0;
|
||||
children.append(node);
|
||||
}
|
||||
|
||||
if(multiLine) return parseBlock(p, depth);
|
||||
|
||||
while(parseDepth(p) > depth) {
|
||||
Node node;
|
||||
node.parseLine(p);
|
||||
children.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
inline void parse(char *&p) {
|
||||
while(*p) {
|
||||
Node node;
|
||||
node.parseLine(p);
|
||||
children.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
inline Node& operator[](const char *name) {
|
||||
for(auto &node : children) {
|
||||
if(node.name == name) return node;
|
||||
}
|
||||
static Node node;
|
||||
node.name = nullptr;
|
||||
return node;
|
||||
}
|
||||
|
||||
inline bool exists() const { return name; }
|
||||
unsigned size() const { return children.size(); }
|
||||
Node* begin() { return children.begin(); }
|
||||
Node* end() { return children.end(); }
|
||||
const Node* begin() const { return children.begin(); }
|
||||
const Node* end() const { return children.end(); }
|
||||
inline Node() : name(""), value("") {}
|
||||
friend class Document;
|
||||
};
|
||||
|
||||
struct Document : Node {
|
||||
cstring error;
|
||||
|
||||
inline bool load(const char *document) {
|
||||
if(document == nullptr) return false;
|
||||
this->document = strdup(document);
|
||||
char *p = this->document;
|
||||
try {
|
||||
this->error = nullptr;
|
||||
parse(p);
|
||||
} catch(const char *error) {
|
||||
this->error = error;
|
||||
free(this->document);
|
||||
this->document = nullptr;
|
||||
children.reset();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Document(const char *document = "") : document(nullptr), error(nullptr) { if(*document) load(document); }
|
||||
inline ~Document() { if(document) free(document); }
|
||||
|
||||
private:
|
||||
char *document;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,76 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//BSV v1.0 parser
|
||||
//revision 0.02
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct BSV {
|
||||
static inline string decode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '}' ) return "";
|
||||
if(*input == '\r') return "";
|
||||
if(*input == '\n') return "";
|
||||
|
||||
//normal characters
|
||||
if(*input != '{') { output[offset++] = *input++; continue; }
|
||||
|
||||
//entities
|
||||
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
|
||||
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
|
||||
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
|
||||
|
||||
//illegal entities
|
||||
return "";
|
||||
}
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
static inline string encode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '\r') return "";
|
||||
|
||||
if(*input == '\n') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'f';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*input == '{') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*input == '}') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'r';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
output[offset++] = *input++;
|
||||
}
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,185 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//convert any (supported) type to a const char* without constructing a new nall::string
|
||||
//this is used inside istring(...) to build nall::string values
|
||||
template<typename T> struct stringify;
|
||||
|
||||
// base types
|
||||
|
||||
template<> struct stringify<bool> {
|
||||
bool value;
|
||||
operator const char*() const { return value ? "true" : "false"; }
|
||||
stringify(bool value) : value(value) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<char> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(char value) { integer(data, value); }
|
||||
};
|
||||
|
||||
// signed integers
|
||||
|
||||
template<> struct stringify<signed char> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(signed char value) { integer(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<signed short> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(signed short value) { integer(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<signed int> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(signed int value) { integer(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<signed long> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(signed long value) { integer(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<signed long long> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(signed long long value) { integer(data, value); }
|
||||
};
|
||||
|
||||
template<unsigned bits> struct stringify<int_t<bits>> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(int_t<bits> value) { integer(data, value); }
|
||||
};
|
||||
|
||||
// unsigned integers
|
||||
|
||||
template<> struct stringify<unsigned char> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(unsigned char value) { decimal(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<unsigned short> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(unsigned short value) { decimal(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<unsigned int> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(unsigned int value) { decimal(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<unsigned long> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(unsigned long value) { decimal(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<unsigned long long> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(unsigned long long value) { decimal(data, value); }
|
||||
};
|
||||
|
||||
template<unsigned bits> struct stringify<uint_t<bits>> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(uint_t<bits> value) { decimal(data, value); }
|
||||
};
|
||||
|
||||
// floating-point
|
||||
|
||||
template<> struct stringify<float> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(float value) { fp(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<double> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(double value) { fp(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<long double> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(long double value) { fp(data, value); }
|
||||
};
|
||||
|
||||
// strings
|
||||
|
||||
template<> struct stringify<char*> {
|
||||
const char *value;
|
||||
operator const char*() const { return value; }
|
||||
stringify(char *value) : value(value) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<const char*> {
|
||||
const char *value;
|
||||
operator const char*() const { return value; }
|
||||
stringify(const char *value) : value(value) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<string> {
|
||||
const string &value;
|
||||
operator const char*() const { return value; }
|
||||
stringify(const string &value) : value(value) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<const string&> {
|
||||
const string &value;
|
||||
operator const char*() const { return value; }
|
||||
stringify(const string &value) : value(value) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<cstring> {
|
||||
const char *value;
|
||||
operator const char*() const { return value; }
|
||||
stringify(const cstring &value) : value(value) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<const cstring&> {
|
||||
const char *value;
|
||||
operator const char*() const { return value; }
|
||||
stringify(const cstring &value) : value(value) {}
|
||||
};
|
||||
|
||||
#if defined(QSTRING_H)
|
||||
|
||||
template<> struct stringify<QString> {
|
||||
const QString &value;
|
||||
operator const char*() const { return value.toUtf8().constData(); }
|
||||
stringify(const QString &value) : value(value) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<const QString&> {
|
||||
const QString &value;
|
||||
operator const char*() const { return value.toUtf8().constData(); }
|
||||
stringify(const QString &value) : value(value) {}
|
||||
};
|
||||
|
||||
string::operator QString() const {
|
||||
return QString::fromUtf8(*this);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
template<typename T> stringify<T> make_string(T value) {
|
||||
return stringify<T>(std::forward<T>(value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef NALL_STRING_COMPARE_HPP
|
||||
#define NALL_STRING_COMPARE_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
|
@ -19,46 +18,6 @@ int istrcmp(const char *str1, const char *str2) {
|
|||
return (int)chrlower(*str1) - (int)chrlower(*str2);
|
||||
}
|
||||
|
||||
bool wildcard(const char *s, const char *p) {
|
||||
const char *cp = 0, *mp = 0;
|
||||
while(*s && *p != '*') {
|
||||
if(*p != '?' && *s != *p) return false;
|
||||
p++, s++;
|
||||
}
|
||||
while(*s) {
|
||||
if(*p == '*') {
|
||||
if(!*++p) return true;
|
||||
mp = p, cp = s + 1;
|
||||
} else if(*p == '?' || *p == *s) {
|
||||
p++, s++;
|
||||
} else {
|
||||
p = mp, s = cp++;
|
||||
}
|
||||
}
|
||||
while(*p == '*') p++;
|
||||
return !*p;
|
||||
}
|
||||
|
||||
bool iwildcard(const char *s, const char *p) {
|
||||
const char *cp = 0, *mp = 0;
|
||||
while(*s && *p != '*') {
|
||||
if(*p != '?' && chrlower(*s) != chrlower(*p)) return false;
|
||||
p++, s++;
|
||||
}
|
||||
while(*s) {
|
||||
if(*p == '*') {
|
||||
if(!*++p) return true;
|
||||
mp = p, cp = s + 1;
|
||||
} else if(*p == '?' || chrlower(*p) == chrlower(*s)) {
|
||||
p++, s++;
|
||||
} else {
|
||||
p = mp, s = cp++;
|
||||
}
|
||||
}
|
||||
while(*p == '*') p++;
|
||||
return !*p;
|
||||
}
|
||||
|
||||
bool strbegin(const char *str, const char *key) {
|
||||
int i, ssl = strlen(str), ksl = strlen(key);
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
char* strlower(char *str) {
|
||||
if(!str) return 0;
|
||||
int i = 0;
|
||||
while(str[i]) {
|
||||
str[i] = chrlower(str[i]);
|
||||
i++;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
char* strupper(char *str) {
|
||||
if(!str) return 0;
|
||||
int i = 0;
|
||||
while(str[i]) {
|
||||
str[i] = chrupper(str[i]);
|
||||
i++;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
char* qstrlower(char *s) {
|
||||
if(!s) return 0;
|
||||
bool quoted = false;
|
||||
while(*s) {
|
||||
if(*s == '\"' || *s == '\'') quoted ^= 1;
|
||||
if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
char* qstrupper(char *s) {
|
||||
if(!s) return 0;
|
||||
bool quoted = false;
|
||||
while(*s) {
|
||||
if(*s == '\"' || *s == '\'') quoted ^= 1;
|
||||
if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
char* strtr(char *dest, const char *before, const char *after) {
|
||||
if(!dest || !before || !after) return dest;
|
||||
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
|
||||
|
||||
if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace
|
||||
for(unsigned i = 0; i < sl; i++) {
|
||||
for(unsigned l = 0; l < bsl; l++) {
|
||||
if(dest[i] == before[l]) {
|
||||
dest[i] = after[l];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,171 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
static void istring(string &output) {
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static void istring(string &output, const T &value, Args&&... args) {
|
||||
output.append_(make_string(value));
|
||||
istring(output, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void string::reserve(unsigned size_) {
|
||||
if(size_ > size) {
|
||||
size = size_;
|
||||
data = (char*)realloc(data, size + 1);
|
||||
data[size] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool string::empty() const {
|
||||
return !*data;
|
||||
}
|
||||
|
||||
template<typename... Args> string& string::assign(Args&&... args) {
|
||||
*data = 0;
|
||||
istring(*this, std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Args> string& string::append(Args&&... args) {
|
||||
istring(*this, std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::assign_(const char *s) {
|
||||
unsigned length = strlen(s);
|
||||
reserve(length);
|
||||
strcpy(data, s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append_(const char *s) {
|
||||
unsigned length = strlen(data) + strlen(s);
|
||||
reserve(length);
|
||||
strcat(data, s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string::operator const char*() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
char* string::operator()() {
|
||||
return data;
|
||||
}
|
||||
|
||||
char& string::operator[](int index) {
|
||||
reserve(index);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
bool string::operator==(const char *str) const { return strcmp(data, str) == 0; }
|
||||
bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; }
|
||||
bool string::operator< (const char *str) const { return strcmp(data, str) < 0; }
|
||||
bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; }
|
||||
bool string::operator> (const char *str) const { return strcmp(data, str) > 0; }
|
||||
bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; }
|
||||
|
||||
string& string::operator=(const string &value) {
|
||||
if(&value == this) return *this;
|
||||
assign(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator=(string &&source) {
|
||||
if(&source == this) return *this;
|
||||
if(data) free(data);
|
||||
size = source.size;
|
||||
data = source.data;
|
||||
source.data = nullptr;
|
||||
source.size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Args> string::string(Args&&... args) {
|
||||
size = 64;
|
||||
data = (char*)malloc(size + 1);
|
||||
*data = 0;
|
||||
istring(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
string::string(const string &value) {
|
||||
if(&value == this) return;
|
||||
size = strlen(value);
|
||||
data = strdup(value);
|
||||
}
|
||||
|
||||
string::string(string &&source) {
|
||||
if(&source == this) return;
|
||||
size = source.size;
|
||||
data = source.data;
|
||||
source.data = nullptr;
|
||||
}
|
||||
|
||||
string::~string() {
|
||||
if(data) free(data);
|
||||
}
|
||||
|
||||
bool string::readfile(const string &filename) {
|
||||
assign("");
|
||||
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
#else
|
||||
FILE *fp = _wfopen(utf16_t(filename), L"rb");
|
||||
#endif
|
||||
if(!fp) return false;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
unsigned size = ftell(fp);
|
||||
rewind(fp);
|
||||
char *fdata = new char[size + 1];
|
||||
unsigned unused = fread(fdata, 1, size, fp);
|
||||
fclose(fp);
|
||||
fdata[size] = 0;
|
||||
assign(fdata);
|
||||
delete[] fdata;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<unsigned> lstring::find(const char *key) const {
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
if(operator[](i) == key) return { true, i };
|
||||
}
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
string lstring::concatenate(const char *separator) const {
|
||||
string output;
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
output.append(operator[](i), i < size() - 1 ? separator : "");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
bool lstring::operator==(const lstring &source) const {
|
||||
if(this == &source) return true;
|
||||
if(size() != source.size()) return false;
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(operator[](n) != source[n]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lstring::operator!=(const lstring &source) const {
|
||||
return !operator==(source);
|
||||
}
|
||||
|
||||
inline lstring::lstring() {
|
||||
}
|
||||
|
||||
inline lstring::lstring(std::initializer_list<string> list) {
|
||||
for(auto &data : list) append(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//const string:
|
||||
//bind a const char* pointer to an object that has various testing functionality;
|
||||
//yet lacks the memory allocation and modification functionality of the string class
|
||||
|
||||
namespace nall {
|
||||
|
||||
cstring::operator const char*() const { return data; }
|
||||
unsigned cstring::length() const { return strlen(data); }
|
||||
bool cstring::operator==(const char *s) const { return !strcmp(data, s); }
|
||||
bool cstring::operator!=(const char *s) const { return strcmp(data, s); }
|
||||
optional<unsigned> cstring::position (const char *key) const { return strpos(data, key); }
|
||||
optional<unsigned> cstring::iposition(const char *key) const { return istrpos(data, key); }
|
||||
cstring& cstring::operator=(const char *data) { this->data = data; return *this; }
|
||||
cstring::cstring(const char *data) : data(data) {}
|
||||
cstring::cstring() : data("") {}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef NALL_FILENAME_HPP
|
||||
#define NALL_FILENAME_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
#ifndef NALL_STRING_MATH_HPP
|
||||
#define NALL_STRING_MATH_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
namespace fixedpoint {
|
||||
|
||||
static function<int64_t (const char *&)> eval_fallback;
|
||||
static nall::function<intmax_t (const char *&)> eval_fallback;
|
||||
|
||||
static int eval_integer(const char *&s) {
|
||||
if(!*s) throw "unrecognized_integer";
|
||||
int value = 0, x = *s, y = *(s + 1);
|
||||
static intmax_t eval_integer(const char *& s) {
|
||||
if(!*s) throw "unrecognized integer";
|
||||
intmax_t value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
//hexadecimal
|
||||
if(x == '0' && (y == 'X' || y == 'x')) {
|
||||
|
@ -52,21 +51,21 @@ static int eval_integer(const char *&s) {
|
|||
while(true) {
|
||||
value = value * 256 + *s++;
|
||||
if(*s == '\'') { s += 1; return value; }
|
||||
if(!*s) throw "mismatched_char";
|
||||
if(!*s) throw "mismatched char";
|
||||
}
|
||||
}
|
||||
|
||||
throw "unrecognized_integer";
|
||||
throw "unrecognized integer";
|
||||
}
|
||||
|
||||
static int eval(const char *&s, int depth = 0) {
|
||||
static intmax_t eval(const char *&s, int depth = 0) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) throw "unrecognized_token";
|
||||
int value = 0, x = *s, y = *(s + 1);
|
||||
if(!*s) throw "unrecognized token";
|
||||
intmax_t value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
if(*s == '(') {
|
||||
value = eval(++s, 1);
|
||||
if(*s++ != ')') throw "mismatched_group";
|
||||
if(*s++ != ')') throw "mismatched group";
|
||||
}
|
||||
|
||||
else if(x == '!') value = !eval(++s, 13);
|
||||
|
@ -78,7 +77,7 @@ static int eval(const char *&s, int depth = 0) {
|
|||
|
||||
else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
|
||||
|
||||
else throw "unrecognized_token";
|
||||
else throw "unrecognized token";
|
||||
|
||||
while(true) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
|
@ -87,8 +86,8 @@ static int eval(const char *&s, int depth = 0) {
|
|||
|
||||
if(depth >= 13) break;
|
||||
if(x == '*') { value *= eval(++s, 13); continue; }
|
||||
if(x == '/') { value /= eval(++s, 13); continue; }
|
||||
if(x == '%') { value %= eval(++s, 13); continue; }
|
||||
if(x == '/') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value /= result; continue; }
|
||||
if(x == '%') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value %= result; continue; }
|
||||
|
||||
if(depth >= 12) break;
|
||||
if(x == '+') { value += eval(++s, 12); continue; }
|
||||
|
@ -127,9 +126,9 @@ static int eval(const char *&s, int depth = 0) {
|
|||
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
|
||||
|
||||
if(x == '?') {
|
||||
int lhs = eval(++s, 2);
|
||||
if(*s != ':') throw "mismatched_ternary";
|
||||
int rhs = eval(++s, 2);
|
||||
intmax_t lhs = eval(++s, 2);
|
||||
if(*s != ':') throw "mismatched ternary";
|
||||
intmax_t rhs = eval(++s, 2);
|
||||
value = value ? lhs : rhs;
|
||||
continue;
|
||||
}
|
||||
|
@ -137,23 +136,13 @@ static int eval(const char *&s, int depth = 0) {
|
|||
|
||||
if(depth > 0 && x == ')') break;
|
||||
|
||||
throw "unrecognized_token";
|
||||
throw "unrecognized token";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool strint(const char *s, int &result) {
|
||||
try {
|
||||
result = eval_integer(s);
|
||||
return true;
|
||||
} catch(const char*) {
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool strmath(const char *s, int &result) {
|
||||
static bool eval(const char *s, intmax_t &result) {
|
||||
try {
|
||||
result = eval(s);
|
||||
return true;
|
||||
|
@ -163,6 +152,15 @@ bool strmath(const char *s, int &result) {
|
|||
}
|
||||
}
|
||||
|
||||
static intmax_t parse(const char *s) {
|
||||
try {
|
||||
intmax_t result = eval(s);
|
||||
return result;
|
||||
} catch(const char *) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,157 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace floatingpoint {
|
||||
|
||||
static nall::function<double (const char *&)> eval_fallback;
|
||||
|
||||
static double eval_integer(const char *&s) {
|
||||
if(!*s) throw "unrecognized integer";
|
||||
intmax_t value = 0, radix = 0, x = *s, y = *(s + 1);
|
||||
|
||||
//hexadecimal
|
||||
if(x == '0' && (y == 'X' || y == 'x')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
|
||||
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
|
||||
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//binary
|
||||
if(x == '0' && (y == 'B' || y == 'b')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//octal (or decimal '0')
|
||||
if(x == '0' && y != '.') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//decimal
|
||||
if(x >= '0' && x <= '9') {
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
|
||||
if(*s == '.') { s++; break; }
|
||||
return value;
|
||||
}
|
||||
//floating-point
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { radix = radix * 10 + (*s++ - '0'); continue; }
|
||||
return atof(nall::string{ nall::decimal(value), ".", nall::decimal(radix) });
|
||||
}
|
||||
}
|
||||
|
||||
//char
|
||||
if(x == '\'' && y != '\'') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
value = value * 256 + *s++;
|
||||
if(*s == '\'') { s += 1; return value; }
|
||||
if(!*s) throw "mismatched char";
|
||||
}
|
||||
}
|
||||
|
||||
throw "unrecognized integer";
|
||||
}
|
||||
|
||||
static double eval(const char *&s, int depth = 0) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) throw "unrecognized token";
|
||||
double value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
if(*s == '(') {
|
||||
value = eval(++s, 1);
|
||||
if(*s++ != ')') throw "mismatched group";
|
||||
}
|
||||
|
||||
else if(x == '!') value = !eval(++s, 9);
|
||||
else if(x == '+') value = +eval(++s, 9);
|
||||
else if(x == '-') value = -eval(++s, 9);
|
||||
|
||||
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
|
||||
|
||||
else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
|
||||
|
||||
else throw "unrecognized token";
|
||||
|
||||
while(true) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) break;
|
||||
x = *s, y = *(s + 1);
|
||||
|
||||
if(depth >= 9) break;
|
||||
if(x == '*') { value *= eval(++s, 9); continue; }
|
||||
if(x == '/') { double result = eval(++s, 9); if(result == 0.0) throw "division by zero"; value /= result; continue; }
|
||||
|
||||
if(depth >= 8) break;
|
||||
if(x == '+') { value += eval(++s, 8); continue; }
|
||||
if(x == '-') { value -= eval(++s, 8); continue; }
|
||||
|
||||
if(depth >= 7) break;
|
||||
if(x == '<' && y == '=') { value = value <= eval(++++s, 7); continue; }
|
||||
if(x == '>' && y == '=') { value = value >= eval(++++s, 7); continue; }
|
||||
if(x == '<') { value = value < eval(++s, 7); continue; }
|
||||
if(x == '>') { value = value > eval(++s, 7); continue; }
|
||||
|
||||
if(depth >= 6) break;
|
||||
if(x == '=' && y == '=') { value = value == eval(++++s, 6); continue; }
|
||||
if(x == '!' && y == '=') { value = value != eval(++++s, 6); continue; }
|
||||
|
||||
if(depth >= 5) break;
|
||||
if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; }
|
||||
|
||||
if(depth >= 4) break;
|
||||
if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; }
|
||||
|
||||
if(depth >= 3) break;
|
||||
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
|
||||
|
||||
if(x == '?') {
|
||||
double lhs = eval(++s, 2);
|
||||
if(*s != ':') throw "mismatched ternary";
|
||||
double rhs = eval(++s, 2);
|
||||
value = value ? lhs : rhs;
|
||||
continue;
|
||||
}
|
||||
if(depth >= 2) break;
|
||||
|
||||
if(depth > 0 && x == ')') break;
|
||||
|
||||
throw "unrecognized token";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool eval(const char *s, double &result) {
|
||||
try {
|
||||
result = eval(s);
|
||||
return true;
|
||||
} catch(const char*e) {
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static double parse(const char *s) {
|
||||
try {
|
||||
double result = eval(s);
|
||||
return result;
|
||||
} catch(const char *) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue