mirror of https://github.com/bsnes-emu/bsnes.git
Update to v094r44 release.
byuu says: Changelog: - return open bus instead of mirroring addresses on the bus (fixes Mario&Luigi, Minish Cap, etc) [Jonas Quinn] - add boolean flag to load requests for slotted game carts (fixes slot load prompts) - rename BS-X Town cart from psram to ram - icarus: add support for game database Note: I didn't rename "bsx" to "mcc" in the database for icarus before uploading that. But I just fixed it locally, so it'll be in the next WIP. For now, make it create the manifest for you and then rename it yourself. I did fix the PSRAM size to 256kbit.
This commit is contained in:
parent
0c87bdabed
commit
483fc81356
|
@ -8,7 +8,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "094.43";
|
||||
static const string Version = "094.44";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -3,11 +3,12 @@ auto CPU::bus_idle() -> void {
|
|||
}
|
||||
|
||||
auto CPU::bus_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
addr &= 0x0fff'ffff;
|
||||
unsigned wait = bus_wait(mode, addr);
|
||||
unsigned word = pipeline.fetch.instruction;
|
||||
|
||||
if(addr & 0x0800'0000) {
|
||||
if(addr >= 0x1000'0000) {
|
||||
prefetch_step(wait);
|
||||
} else if(addr & 0x0800'0000) {
|
||||
if(mode & Prefetch && regs.wait.control.prefetch) {
|
||||
prefetch_sync(addr);
|
||||
word = prefetch_read();
|
||||
|
@ -35,10 +36,11 @@ auto CPU::bus_read(unsigned mode, uint32 addr) -> uint32 {
|
|||
}
|
||||
|
||||
auto CPU::bus_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
addr &= 0x0fff'ffff;
|
||||
unsigned wait = bus_wait(mode, addr);
|
||||
|
||||
if(addr & 0x0800'0000) {
|
||||
if(addr >= 0x1000'0000) {
|
||||
prefetch_step(wait);
|
||||
} else if(addr & 0x0800'0000) {
|
||||
if(!active.dma) prefetch_wait();
|
||||
step(wait);
|
||||
addr < 0x0e00'0000 ? cartridge.rom_write(mode, addr, word) : cartridge.ram_write(mode, addr, word);
|
||||
|
@ -56,11 +58,12 @@ auto CPU::bus_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
|||
}
|
||||
|
||||
auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
|
||||
if(addr < 0x0200'0000) return 1;
|
||||
if(addr < 0x0300'0000) return (16 - regs.memory.control.ewramwait) * (mode & Word ? 2 : 1);
|
||||
if(addr < 0x0500'0000) return 1;
|
||||
if(addr < 0x0700'0000) return mode & Word ? 2 : 1;
|
||||
if(addr < 0x0800'0000) return 1;
|
||||
if(addr >= 0x1000'0000) return 1; //unmapped
|
||||
if(addr < 0x0200'0000) return 1;
|
||||
if(addr < 0x0300'0000) return (16 - regs.memory.control.ewramwait) * (mode & Word ? 2 : 1);
|
||||
if(addr < 0x0500'0000) return 1;
|
||||
if(addr < 0x0700'0000) return mode & Word ? 2 : 1;
|
||||
if(addr < 0x0800'0000) return 1;
|
||||
|
||||
static unsigned timings[] = {5, 4, 3, 9};
|
||||
unsigned n = timings[regs.wait.control.nwait[addr >> 25 & 3]];
|
||||
|
|
|
@ -8,14 +8,17 @@ BIOS::~BIOS() {
|
|||
}
|
||||
|
||||
auto BIOS::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
//unmapped memory
|
||||
if(addr >= 0x0000'4000) return cpu.pipeline.fetch.instruction; //0000'4000-01ff'ffff
|
||||
|
||||
//GBA BIOS is read-protected; only the BIOS itself can read its own memory
|
||||
//when accessed elsewhere; this returns the last value read by the BIOS program
|
||||
if(cpu.r(15) >= 0x02000000) return mdr;
|
||||
//when accessed elsewhere; this should return the last value read by the BIOS program
|
||||
if(cpu.r(15) >= 0x0000'4000) return mdr;
|
||||
|
||||
if(mode & Word) return mdr = read(Half, addr &~ 2) << 0 | read(Half, addr | 2) << 16;
|
||||
if(mode & Half) return mdr = read(Byte, addr &~ 1) << 0 | read(Byte, addr | 1) << 8;
|
||||
|
||||
return mdr = data[addr & 0x3fff];
|
||||
return mdr = data[addr];
|
||||
}
|
||||
|
||||
auto BIOS::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
|
|
|
@ -255,7 +255,6 @@ struct Geometry {
|
|||
Geometry();
|
||||
Geometry(Position position, Size size);
|
||||
Geometry(signed x, signed y, signed width, signed height);
|
||||
Geometry(const string& text);
|
||||
|
||||
explicit operator bool() const;
|
||||
auto operator==(const Geometry& source) const -> bool;
|
||||
|
@ -276,7 +275,6 @@ struct Geometry {
|
|||
auto setX(signed x) -> type&;
|
||||
auto setY(signed y) -> type&;
|
||||
auto size() const -> Size;
|
||||
auto text() const -> string;
|
||||
auto width() const -> signed;
|
||||
auto x() const -> signed;
|
||||
auto y() const -> signed;
|
||||
|
|
|
@ -12,14 +12,6 @@ Geometry::Geometry(signed x, signed y, signed width, signed height) {
|
|||
setGeometry(x, y, width, height);
|
||||
}
|
||||
|
||||
Geometry::Geometry(const string& text) {
|
||||
lstring part = text.split(",").strip();
|
||||
state.x = integer(part(0));
|
||||
state.y = integer(part(1));
|
||||
state.width = integer(part(2));
|
||||
state.height = integer(part(3));
|
||||
}
|
||||
|
||||
Geometry::operator bool() const {
|
||||
return state.x || state.y || state.width || state.height;
|
||||
}
|
||||
|
@ -105,10 +97,6 @@ auto Geometry::size() const -> Size {
|
|||
return {state.width, state.height};
|
||||
}
|
||||
|
||||
auto Geometry::text() const -> string {
|
||||
return {state.x, ",", state.y, ",", state.width, ",", state.height};
|
||||
}
|
||||
|
||||
auto Geometry::width() const -> signed {
|
||||
return state.width;
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ auto BrowserDialogWindow::run() -> lstring {
|
|||
pathName.onActivate([&] { setPath(pathName.text()); });
|
||||
pathRefresh.setBordered(false).setImage(Icon::Action::Refresh).onActivate([&] { setPath(state.path); });
|
||||
pathHome.setBordered(false).setImage(Icon::Go::Home).onActivate([&] { setPath(userpath()); });
|
||||
pathUp.setBordered(false).setImage(Icon::Go::Up).onActivate([&] { setPath(state.path.dirname()); });
|
||||
pathUp.setBordered(false).setImage(Icon::Go::Up).onActivate([&] { setPath(dirname(state.path)); });
|
||||
view.setBatchable(state.action == "openFiles").onActivate([&] { activate(); }).onChange([&] { change(); });
|
||||
filterList.setVisible(state.action != "selectFolder").onChange([&] { setPath(state.path); });
|
||||
for(auto& filter : state.filters) {
|
||||
|
|
|
@ -32,7 +32,7 @@ auto pMenuItem::setText(const string& text) -> void {
|
|||
|
||||
auto pMenuItem::_setState() -> void {
|
||||
qtMenuItem->setIcon(CreateImage(state().image));
|
||||
qtMenuItem->setText(state().text);
|
||||
qtMenuItem->setText(QString::fromUtf8(state().text));
|
||||
}
|
||||
|
||||
auto QtMenuItem::onActivate() -> void {
|
||||
|
|
|
@ -11,16 +11,16 @@ auto pFont::size(const QFont& qtFont, const string& text) -> Size {
|
|||
signed maxWidth = 0;
|
||||
auto lines = text.split("\n");
|
||||
for(auto& line : lines) {
|
||||
maxWidth = max(maxWidth, metrics.width(line));
|
||||
maxWidth = max(maxWidth, metrics.width(QString::fromUtf8(line)));
|
||||
}
|
||||
return {maxWidth, metrics.height() * (signed)lines.size()};
|
||||
}
|
||||
|
||||
auto pFont::family(const string& family) -> string {
|
||||
auto pFont::family(const string& family) -> QString {
|
||||
if(family == Font::Sans ) return "Sans";
|
||||
if(family == Font::Serif) return "Serif";
|
||||
if(family == Font::Mono ) return "Liberation Mono";
|
||||
return family ? family : "Sans";
|
||||
return family ? QString::fromUtf8(family) : "Sans";
|
||||
}
|
||||
|
||||
auto pFont::create(const Font& font) -> QFont {
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace hiro {
|
|||
struct pFont {
|
||||
static auto size(const Font& font, const string& text) -> Size;
|
||||
static auto size(const QFont& qtFont, const string& text) -> Size;
|
||||
static auto family(const string& family) -> string;
|
||||
static auto family(const string& family) -> QString;
|
||||
static auto create(const Font& font) -> QFont;
|
||||
};
|
||||
|
||||
|
|
|
@ -9,11 +9,11 @@ auto pWindow::construct() -> void {
|
|||
//if program was given a name, try and set the window taskbar icon to a matching pixmap image
|
||||
if(auto name = Application::state.name) {
|
||||
if(file::exists({userpath(), ".local/share/icons/", name, ".png"})) {
|
||||
qtWindow->setWindowIcon(QIcon(string{userpath(), ".local/share/icons/", name, ".png"}));
|
||||
qtWindow->setWindowIcon(QIcon(QString::fromUtf8(string{userpath(), ".local/share/icons/", name, ".png"})));
|
||||
} else if(file::exists({"/usr/local/share/pixmaps/", name, ".png"})) {
|
||||
qtWindow->setWindowIcon(QIcon(string{"/usr/local/share/pixmaps/", name, ".png"}));
|
||||
qtWindow->setWindowIcon(QIcon(QString::fromUtf8(string{"/usr/local/share/pixmaps/", name, ".png"})));
|
||||
} else if(file::exists({"/usr/share/pixmaps/", name, ".png"})) {
|
||||
qtWindow->setWindowIcon(QIcon(string{"/usr/share/pixmaps/", name, ".png"}));
|
||||
qtWindow->setWindowIcon(QIcon(QString::fromUtf8(string{"/usr/share/pixmaps/", name, ".png"})));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ auto Icarus::bsxSatellaviewManifest(vector<uint8_t>& buffer, const string& locat
|
|||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" sha256: ", Hash::SHA256(buffer.data(), buffer.size()).digest(), "\n");
|
||||
markup.append(" title: ", location.prefixname(), "\n");
|
||||
markup.append(" title: ", prefixname(location), "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
return markup;
|
||||
}
|
||||
|
@ -18,20 +18,36 @@ auto Icarus::bsxSatellaviewManifest(vector<uint8_t>& buffer, const string& locat
|
|||
}
|
||||
|
||||
auto Icarus::bsxSatellaviewImport(vector<uint8_t>& buffer, const string& location) -> bool {
|
||||
auto name = location.prefixname();
|
||||
auto source = location.pathname();
|
||||
auto name = prefixname(location);
|
||||
auto source = pathname(location);
|
||||
string target{settings.libraryPath, "BS-X Satellaview/", name, ".bs/"};
|
||||
//if(directory::exists(target)) return failure("game already exists");
|
||||
|
||||
BsxSatellaviewCartridge cartridge{buffer.data(), buffer.size()};
|
||||
auto markup = cartridge.markup;
|
||||
if(!markup) return failure("does not appear to be a valid image");
|
||||
string markup;
|
||||
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
if(settings.useDatabase && !markup) {
|
||||
auto digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
|
||||
for(auto node : database.bsxSatellaview) {
|
||||
if(node.name() != "release") continue;
|
||||
if(node["information/sha256"].text() == digest) {
|
||||
markup.append(BML::serialize(node["cartridge"]), "\n");
|
||||
markup.append(BML::serialize(node["information"]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(settings.useHeuristics && !markup) {
|
||||
BsxSatellaviewCartridge cartridge{buffer.data(), buffer.size()};
|
||||
if(markup = cartridge.markup) {
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
|
||||
if(settings.createManifests) file::write({target, "manifest.bml"}, markup);
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
namespace Database {
|
||||
#include "../database/super-famicom.hpp"
|
||||
#include "../database/bsx-satellaview.hpp"
|
||||
#include "../database/sufami-turbo.hpp"
|
||||
}
|
||||
|
||||
Icarus::Icarus() {
|
||||
database.superFamicom = BML::unserialize(Database::SuperFamicom);
|
||||
database.bsxSatellaview = BML::unserialize(Database::BsxSatellaview);
|
||||
database.sufamiTurbo = BML::unserialize(Database::SufamiTurbo);
|
||||
}
|
||||
|
||||
auto Icarus::error() const -> string {
|
||||
return errorMessage;
|
||||
}
|
||||
|
@ -16,7 +28,7 @@ auto Icarus::manifest(string location) -> string {
|
|||
location.transform("\\", "/").rtrim("/").append("/");
|
||||
if(!directory::exists(location)) return "";
|
||||
|
||||
auto type = location.suffixname().downcase();
|
||||
auto type = suffixname(location).downcase();
|
||||
if(type == ".fc") return famicomManifest(location);
|
||||
if(type == ".sfc") return superFamicomManifest(location);
|
||||
if(type == ".gb") return gameBoyManifest(location);
|
||||
|
@ -33,8 +45,8 @@ auto Icarus::import(string location) -> bool {
|
|||
if(!file::exists(location)) return failure("file does not exist");
|
||||
if(!file::readable(location)) return failure("file is unreadable");
|
||||
|
||||
auto name = location.prefixname();
|
||||
auto type = location.suffixname().downcase();
|
||||
auto name = prefixname(location);
|
||||
auto type = suffixname(location).downcase();
|
||||
if(!name || !type) return failure("invalid file name");
|
||||
|
||||
auto buffer = file::read(location);
|
||||
|
@ -45,8 +57,8 @@ auto Icarus::import(string location) -> bool {
|
|||
if(!zip.open(location)) return failure("ZIP archive is invalid");
|
||||
if(!zip.file) return failure("ZIP archive is empty");
|
||||
|
||||
name = zip.file[0].name.prefixname();
|
||||
type = zip.file[0].name.suffixname().downcase();
|
||||
name = prefixname(zip.file[0].name);
|
||||
type = suffixname(zip.file[0].name).downcase();
|
||||
buffer = zip.extract(zip.file[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
struct Icarus {
|
||||
//core.cpp
|
||||
Icarus();
|
||||
|
||||
auto error() const -> string;
|
||||
auto success() -> bool;
|
||||
auto failure(const string& message) -> bool;
|
||||
|
@ -47,4 +49,10 @@ struct Icarus {
|
|||
|
||||
private:
|
||||
string errorMessage;
|
||||
|
||||
struct {
|
||||
Markup::Node superFamicom;
|
||||
Markup::Node bsxSatellaview;
|
||||
Markup::Node sufamiTurbo;
|
||||
} database;
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ auto Icarus::famicomManifest(vector<uint8_t>& buffer, const string& location) ->
|
|||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" sha256: ", Hash::SHA256(buffer.data(), buffer.size()).digest(), "\n");
|
||||
markup.append(" title: ", location.prefixname(), "\n");
|
||||
markup.append(" title: ", prefixname(location), "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
return markup;
|
||||
}
|
||||
|
@ -20,20 +20,24 @@ auto Icarus::famicomManifest(vector<uint8_t>& buffer, const string& location) ->
|
|||
}
|
||||
|
||||
auto Icarus::famicomImport(vector<uint8_t>& buffer, const string& location) -> bool {
|
||||
auto name = location.prefixname();
|
||||
auto source = location.pathname();
|
||||
auto name = prefixname(location);
|
||||
auto source = pathname(location);
|
||||
string target{settings.libraryPath, "Famicom/", name, ".fc/"};
|
||||
//if(directory::exists(target)) return failure("game already exists");
|
||||
|
||||
FamicomCartridge cartridge{buffer.data(), buffer.size()};
|
||||
auto markup = cartridge.markup;
|
||||
if(!markup) return failure("does not appear to be a valid image");
|
||||
string markup;
|
||||
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
//if(settings.useHeuristics && !markup) {
|
||||
FamicomCartridge cartridge{buffer.data(), buffer.size()};
|
||||
if(markup = cartridge.markup) {
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
}
|
||||
//}
|
||||
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
|
||||
if(settings.createManifests) file::write({target, "manifest.bml"}, markup);
|
||||
|
|
|
@ -10,7 +10,7 @@ auto Icarus::gameBoyAdvanceManifest(vector<uint8_t>& buffer, const string& locat
|
|||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" sha256: ", Hash::SHA256(buffer.data(), buffer.size()).digest(), "\n");
|
||||
markup.append(" title: ", location.prefixname(), "\n");
|
||||
markup.append(" title: ", prefixname(location), "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
return markup;
|
||||
}
|
||||
|
@ -18,20 +18,24 @@ auto Icarus::gameBoyAdvanceManifest(vector<uint8_t>& buffer, const string& locat
|
|||
}
|
||||
|
||||
auto Icarus::gameBoyAdvanceImport(vector<uint8_t>& buffer, const string& location) -> bool {
|
||||
auto name = location.prefixname();
|
||||
auto source = location.pathname();
|
||||
auto name = prefixname(location);
|
||||
auto source = pathname(location);
|
||||
string target{settings.libraryPath, "Game Boy Advance/", name, ".gba/"};
|
||||
//if(directory::exists(target)) return failure("game already exists");
|
||||
|
||||
GameBoyAdvanceCartridge cartridge{buffer.data(), buffer.size()};
|
||||
auto markup = cartridge.markup;
|
||||
if(!markup) return failure("does not appear to be a valid image");
|
||||
string markup;
|
||||
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
if(settings.useHeuristics && !markup) {
|
||||
GameBoyAdvanceCartridge cartridge{buffer.data(), buffer.size()};
|
||||
if(markup = cartridge.markup) {
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
|
||||
if(settings.createManifests) file::write({target, "manifest.bml"}, markup);
|
||||
|
|
|
@ -10,7 +10,7 @@ auto Icarus::gameBoyColorManifest(vector<uint8_t>& buffer, const string& locatio
|
|||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" sha256: ", Hash::SHA256(buffer.data(), buffer.size()).digest(), "\n");
|
||||
markup.append(" title: ", location.prefixname(), "\n");
|
||||
markup.append(" title: ", prefixname(location), "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
return markup;
|
||||
}
|
||||
|
@ -18,20 +18,24 @@ auto Icarus::gameBoyColorManifest(vector<uint8_t>& buffer, const string& locatio
|
|||
}
|
||||
|
||||
auto Icarus::gameBoyColorImport(vector<uint8_t>& buffer, const string& location) -> bool {
|
||||
auto name = location.prefixname();
|
||||
auto source = location.pathname();
|
||||
auto name = prefixname(location);
|
||||
auto source = pathname(location);
|
||||
string target{settings.libraryPath, "Game Boy Color/", name, ".gbc/"};
|
||||
//if(directory::exists(target)) return failure("game already exists");
|
||||
|
||||
GameBoyCartridge cartridge{buffer.data(), buffer.size()};
|
||||
auto markup = cartridge.markup;
|
||||
if(!markup) return failure("does not appear to be a valid image");
|
||||
string markup;
|
||||
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
if(settings.useHeuristics && !markup) {
|
||||
GameBoyCartridge cartridge{buffer.data(), buffer.size()};
|
||||
if(markup = cartridge.markup) {
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
|
||||
if(settings.createManifests) file::write({target, "manifest.bml"}, markup);
|
||||
|
|
|
@ -10,7 +10,7 @@ auto Icarus::gameBoyManifest(vector<uint8_t>& buffer, const string& location) ->
|
|||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" sha256: ", Hash::SHA256(buffer.data(), buffer.size()).digest(), "\n");
|
||||
markup.append(" title: ", location.prefixname(), "\n");
|
||||
markup.append(" title: ", prefixname(location), "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
return markup;
|
||||
}
|
||||
|
@ -18,20 +18,24 @@ auto Icarus::gameBoyManifest(vector<uint8_t>& buffer, const string& location) ->
|
|||
}
|
||||
|
||||
auto Icarus::gameBoyImport(vector<uint8_t>& buffer, const string& location) -> bool {
|
||||
auto name = location.prefixname();
|
||||
auto source = location.pathname();
|
||||
auto name = prefixname(location);
|
||||
auto source = pathname(location);
|
||||
string target{settings.libraryPath, "Game Boy/", name, ".gb/"};
|
||||
//if(directory::exists(target)) return failure("game already exists");
|
||||
|
||||
GameBoyCartridge cartridge{buffer.data(), buffer.size()};
|
||||
auto markup = cartridge.markup;
|
||||
if(!markup) return failure("does not appear to be a valid image");
|
||||
string markup;
|
||||
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
if(settings.useHeuristics && !markup) {
|
||||
GameBoyCartridge cartridge{buffer.data(), buffer.size()};
|
||||
if(markup = cartridge.markup) {
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
|
||||
if(settings.createManifests) file::write({target, "manifest.bml"}, markup);
|
||||
|
|
|
@ -10,7 +10,7 @@ auto Icarus::sufamiTurboManifest(vector<uint8_t>& buffer, const string& location
|
|||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" sha256: ", Hash::SHA256(buffer.data(), buffer.size()).digest(), "\n");
|
||||
markup.append(" title: ", location.prefixname(), "\n");
|
||||
markup.append(" title: ", prefixname(location), "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
return markup;
|
||||
}
|
||||
|
@ -18,20 +18,36 @@ auto Icarus::sufamiTurboManifest(vector<uint8_t>& buffer, const string& location
|
|||
}
|
||||
|
||||
auto Icarus::sufamiTurboImport(vector<uint8_t>& buffer, const string& location) -> bool {
|
||||
auto name = location.prefixname();
|
||||
auto source = location.pathname();
|
||||
auto name = prefixname(location);
|
||||
auto source = pathname(location);
|
||||
string target{settings.libraryPath, "Sufami Turbo/", name, ".st/"};
|
||||
//if(directory::exists(target)) return failure("game already exists");
|
||||
|
||||
SufamiTurboCartridge cartridge{buffer.data(), buffer.size()};
|
||||
auto markup = cartridge.markup;
|
||||
if(!markup) return failure("does not appear to be a valid image");
|
||||
string markup;
|
||||
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
if(settings.useDatabase && !markup) {
|
||||
auto digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
|
||||
for(auto node : database.sufamiTurbo) {
|
||||
if(node.name() != "release") continue;
|
||||
if(node["information/sha256"].text() == digest) {
|
||||
markup.append(BML::serialize(node["cartridge"]), "\n");
|
||||
markup.append(BML::serialize(node["information"]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(settings.useHeuristics && !markup) {
|
||||
SufamiTurboCartridge cartridge{buffer.data(), buffer.size()};
|
||||
if(markup = cartridge.markup) {
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: heuristically generated by icarus\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
|
||||
if(settings.createManifests) file::write({target, "manifest.bml"}, markup);
|
||||
|
|
|
@ -15,7 +15,7 @@ auto Icarus::superFamicomManifest(vector<uint8_t>& buffer, const string& locatio
|
|||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" sha256: ", Hash::SHA256(buffer.data(), buffer.size()).digest(), "\n");
|
||||
markup.append(" title: ", location.prefixname(), "\n");
|
||||
markup.append(" title: ", prefixname(location), "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
return markup;
|
||||
}
|
||||
|
@ -23,31 +23,48 @@ auto Icarus::superFamicomManifest(vector<uint8_t>& buffer, const string& locatio
|
|||
}
|
||||
|
||||
auto Icarus::superFamicomImport(vector<uint8_t>& buffer, const string& location) -> bool {
|
||||
auto name = location.prefixname();
|
||||
auto source = location.pathname();
|
||||
auto name = prefixname(location);
|
||||
auto source = pathname(location);
|
||||
string target{settings.libraryPath, "Super Famicom/", name, ".sfc/"};
|
||||
//if(directory::exists(target)) return failure("game already exists");
|
||||
|
||||
SuperFamicomCartridge cartridge{buffer.data(), buffer.size()};
|
||||
auto markup = cartridge.markup;
|
||||
if(!markup) return failure("does not appear to be a valid image");
|
||||
|
||||
string markup;
|
||||
vector<Markup::Node> roms;
|
||||
auto document = BML::unserialize(markup);
|
||||
superFamicomImportScanManifest(roms, document);
|
||||
bool firmwareAppended = true;
|
||||
|
||||
if(settings.useDatabase && !markup) {
|
||||
auto digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
|
||||
for(auto node : database.superFamicom) {
|
||||
if(node.name() != "release") continue;
|
||||
if(node["information/sha256"].text() == digest) {
|
||||
markup.append(BML::serialize(node["cartridge"]), "\n");
|
||||
markup.append(BML::serialize(node["information"]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(settings.useHeuristics && !markup) {
|
||||
SuperFamicomCartridge cartridge{buffer.data(), buffer.size()};
|
||||
if(markup = cartridge.markup) {
|
||||
firmwareAppended = cartridge.firmware_appended;
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
}
|
||||
}
|
||||
|
||||
auto document = BML::unserialize(markup);
|
||||
superFamicomImportScanManifest(roms, document["cartridge"]);
|
||||
for(auto rom : roms) {
|
||||
auto name = rom["name"].text();
|
||||
auto size = rom["size"].decimal();
|
||||
if(name == "program.rom" || name == "data.rom" || cartridge.firmware_appended) continue;
|
||||
if(name == "program.rom" || name == "data.rom" || firmwareAppended) continue;
|
||||
if(file::size({source, name}) != size) return failure({"firmware (", name, ") missing or invalid"});
|
||||
}
|
||||
|
||||
markup.append("\n");
|
||||
markup.append("information\n");
|
||||
markup.append(" title: ", name, "\n");
|
||||
markup.append(" note: ", "heuristically generated by icarus\n");
|
||||
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
|
||||
if(settings.createManifests) file::write({target, "manifest.bml"}, markup);
|
||||
|
@ -55,7 +72,7 @@ auto Icarus::superFamicomImport(vector<uint8_t>& buffer, const string& location)
|
|||
for(auto rom : roms) {
|
||||
auto name = rom["name"].text();
|
||||
auto size = rom["size"].decimal();
|
||||
if(name == "program.rom" || name == "data.rom" || cartridge.firmware_appended) {
|
||||
if(name == "program.rom" || name == "data.rom" || firmwareAppended) {
|
||||
file::write({target, name}, buffer.data() + offset, size);
|
||||
offset += size;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
string BsxSatellaview = R"(
|
||||
|
||||
database revision=2013-01-22
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
information
|
||||
title: 鮫亀 キャラカセット
|
||||
name: Same Game - Character Cassette
|
||||
region: JP
|
||||
revision: 1.0
|
||||
board: BSMC-CR-01
|
||||
serial: BSMC-ZS5J-JPN
|
||||
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
|
||||
configuration
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
|
||||
)";
|
|
@ -0,0 +1,162 @@
|
|||
string SufamiTurbo = R"(
|
||||
|
||||
database revision=2013-01-22
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル ウルトラマン伝説
|
||||
name: SD Ultra Battle - Ultraman Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0101-JPN
|
||||
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル セブン伝説
|
||||
name: SD Ultra Battle - Seven Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0102-JPN
|
||||
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: ポイポイ忍者ワールド
|
||||
name: Poi Poi Ninja World
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0103-JPN
|
||||
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション 一年戦争記
|
||||
name: SD Gundam Generation - Ichinen Sensouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0104-JPN
|
||||
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション グリプス戦記
|
||||
name: SD Gundam Generation - Grips Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0105-JPN
|
||||
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000
|
||||
information
|
||||
title: ゲゲゲの鬼太郎 妖怪ドンジャラ
|
||||
name: Gegege no Kitarou - Youkai Donjara
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0106-JPN
|
||||
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション アクシズ戦記
|
||||
name: SD Gundam Generation - Axis Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0107-JPN
|
||||
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション バビロニア建国戦記
|
||||
name: SD Gundam Generation - Babylonia Kenkoku Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0108-JPN
|
||||
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション ザンスカール戦記
|
||||
name: SD Gundam Generation - Zanscar Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0110-JPN
|
||||
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDガンダムジェネレーション コロニー格闘記
|
||||
name: SD Gundam Generation - Colony Kakutouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0111-JPN
|
||||
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
)";
|
File diff suppressed because it is too large
Load Diff
|
@ -390,7 +390,7 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
|
|||
" mcc\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram id=save name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" ram id=download name=bsx.ram size=0x80000\n"
|
||||
" ram id=download name=download.ram size=0x80000\n"
|
||||
" map id=io address=00-3f,80-bf:5000-5fff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
|
||||
|
|
|
@ -6,12 +6,16 @@ struct Settings : Configuration::Document {
|
|||
string activePath;
|
||||
string libraryPath;
|
||||
bool createManifests = false;
|
||||
bool useDatabase = true;
|
||||
bool useHeuristics = true;
|
||||
};
|
||||
|
||||
Settings::Settings() {
|
||||
root.append(activePath, "ActivePath");
|
||||
root.append(libraryPath, "LibraryPath");
|
||||
root.append(createManifests, "CreateManifests");
|
||||
root.append(useDatabase, "UseDatabase");
|
||||
root.append(useHeuristics, "UseHeuristics");
|
||||
append(root, "Settings");
|
||||
|
||||
directory::create({configpath(), "icarus/"});
|
||||
|
|
|
@ -20,7 +20,7 @@ auto ImportDialog::run(lstring locations) -> void {
|
|||
|
||||
setVisible(true);
|
||||
for(auto& location : locations) {
|
||||
auto name = location.basename();
|
||||
auto name = basename(location);
|
||||
|
||||
if(abort) {
|
||||
errors.append(string{"[", name, "] aborted"});
|
||||
|
|
|
@ -4,16 +4,16 @@ ScanDialog::ScanDialog() {
|
|||
onClose(&Application::quit);
|
||||
layout.setMargin(5);
|
||||
pathEdit.onActivate([&] { refresh(); });
|
||||
refreshButton.setIcon(Icon::Action::Refresh).setBordered(false).onActivate([&] {
|
||||
refreshButton.setImage(Icon::Action::Refresh).setBordered(false).onActivate([&] {
|
||||
pathEdit.setText(settings.activePath);
|
||||
refresh();
|
||||
});
|
||||
homeButton.setIcon(Icon::Go::Home).setBordered(false).onActivate([&] {
|
||||
homeButton.setImage(Icon::Go::Home).setBordered(false).onActivate([&] {
|
||||
pathEdit.setText(userpath());
|
||||
refresh();
|
||||
});
|
||||
upButton.setIcon(Icon::Go::Up).setBordered(false).onActivate([&] {
|
||||
pathEdit.setText(settings.activePath.dirname());
|
||||
upButton.setImage(Icon::Go::Up).setBordered(false).onActivate([&] {
|
||||
pathEdit.setText(dirname(settings.activePath));
|
||||
refresh();
|
||||
});
|
||||
scanList.onActivate([&] { activate(); });
|
||||
|
@ -51,14 +51,14 @@ auto ScanDialog::refresh() -> void {
|
|||
|
||||
for(auto& name : contents) {
|
||||
if(!name.endsWith("/")) continue;
|
||||
if(gamePakType(name.suffixname())) continue;
|
||||
scanList.append(ListViewItem().append(ListViewCell().setIcon(Icon::Emblem::Folder).setText(name.rtrim("/"))));
|
||||
if(gamePakType(suffixname(name))) continue;
|
||||
scanList.append(ListViewItem().append(ListViewCell().setImage(Icon::Emblem::Folder).setText(name.rtrim("/"))));
|
||||
}
|
||||
|
||||
for(auto& name : contents) {
|
||||
if(name.endsWith("/")) continue;
|
||||
if(!gameRomType(name.suffixname().downcase())) continue;
|
||||
scanList.append(ListViewItem().append(ListViewCell().setCheckable().setIcon(Icon::Emblem::File).setText(name)));
|
||||
if(!gameRomType(suffixname(name).downcase())) continue;
|
||||
scanList.append(ListViewItem().append(ListViewCell().setCheckable().setImage(Icon::Emblem::File).setText(name)));
|
||||
}
|
||||
|
||||
Application::processEvents();
|
||||
|
@ -69,7 +69,7 @@ auto ScanDialog::refresh() -> void {
|
|||
auto ScanDialog::activate() -> void {
|
||||
if(auto item = scanList.selected()) {
|
||||
string location{settings.activePath, item.cell(0).text()};
|
||||
if(directory::exists(location) && !gamePakType(location.suffixname())) {
|
||||
if(directory::exists(location) && !gamePakType(suffixname(location))) {
|
||||
pathEdit.setText(location);
|
||||
refresh();
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ auto Response::findContentLength() const -> unsigned {
|
|||
auto Response::findContentType() const -> string {
|
||||
if(auto contentType = header("Content-Type")) return contentType;
|
||||
if(hasData()) return "application/octet-stream";
|
||||
if(hasFile()) return findContentType(file().suffixname());
|
||||
if(hasFile()) return findContentType(suffixname(file()));
|
||||
return "text/html; charset=utf-8";
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ struct zipstream : memorystream {
|
|||
if(file.name.match(filter)) {
|
||||
auto buffer = archive.extract(file);
|
||||
psize = buffer.size();
|
||||
pdata = buffer.move();
|
||||
pdata = buffer.release();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
#define NALL_STRING_INTERNAL_HPP
|
||||
#include <nall/string/base.hpp>
|
||||
#include <nall/string/ref.hpp>
|
||||
#include <nall/string/view.hpp>
|
||||
#include <nall/string/atoi.hpp>
|
||||
#include <nall/string/cast.hpp>
|
||||
#include <nall/string/compare.hpp>
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
struct string_view;
|
||||
struct string;
|
||||
struct format;
|
||||
struct stringref;
|
||||
struct lstring;
|
||||
|
||||
using rstring = const string_view&;
|
||||
using cstring = const string&;
|
||||
using rstring = const stringref&;
|
||||
|
||||
#define NALL_STRING_ALLOCATOR_ADAPTIVE
|
||||
//#define NALL_STRING_ALLOCATOR_COPY_ON_WRITE
|
||||
|
@ -17,46 +18,6 @@ using rstring = const stringref&;
|
|||
//cast.hpp
|
||||
template<typename T> struct stringify;
|
||||
|
||||
//compare.hpp
|
||||
inline auto compare(const string& self, rstring source) -> signed;
|
||||
inline auto icompare(const string& self, rstring source) -> signed;
|
||||
|
||||
inline auto equals(const string& self, rstring source) -> bool;
|
||||
inline auto iequals(const string& self, rstring source) -> bool;
|
||||
|
||||
inline auto beginsWith(const string& self, rstring source) -> bool;
|
||||
inline auto ibeginsWith(const string& self, rstring source) -> bool;
|
||||
|
||||
inline auto endsWith(const string& self, rstring source) -> bool;
|
||||
inline auto iendsWith(const string& self, rstring source) -> bool;
|
||||
|
||||
//convert.hpp
|
||||
inline auto downcase(string& self) -> string&;
|
||||
inline auto qdowncase(string& self) -> string&;
|
||||
|
||||
inline auto upcase(string& self) -> string&;
|
||||
inline auto qupcase(string& self) -> string&;
|
||||
|
||||
inline auto transform(string& self, rstring from, rstring to) -> string&;
|
||||
|
||||
//core.hpp
|
||||
template<typename... P> inline auto assign(string& self, P&&... p) -> string&;
|
||||
template<typename T, typename... P> inline auto append(string& self, const T& value, P&&... p) -> string&;
|
||||
template<typename... P> inline auto append(string& self, const format& value, P&&... p) -> string&;
|
||||
inline auto append(string& self) -> string&;
|
||||
template<typename T> inline auto _append(string& self, const stringify<T>& source) -> string&;
|
||||
inline auto empty(const string& self) -> bool;
|
||||
inline auto length(const string& self) -> unsigned;
|
||||
|
||||
//find.hpp
|
||||
template<bool I, bool Q> inline auto _find(const string& self, signed offset, rstring source) -> maybe<unsigned>;
|
||||
inline auto find(const string& self, rstring source) -> maybe<unsigned>;
|
||||
inline auto ifind(const string& self, rstring source) -> maybe<unsigned>;
|
||||
inline auto qfind(const string& self, rstring source) -> maybe<unsigned>;
|
||||
inline auto iqfind(const string& self, rstring source) -> maybe<unsigned>;
|
||||
inline auto findFrom(const string& self, signed offset, rstring source) -> maybe<unsigned>;
|
||||
inline auto ifindFrom(const string& self, signed offset, rstring source) -> maybe<unsigned>;
|
||||
|
||||
//format.hpp
|
||||
template<typename... P> inline auto print(P&&...) -> void;
|
||||
inline auto integer(intmax_t value, long precision = 0, char padchar = '0') -> string;
|
||||
|
@ -69,23 +30,22 @@ inline auto pointer(uintptr_t value, long precision = 0) -> string;
|
|||
inline auto real(long double value) -> string;
|
||||
|
||||
//hash.hpp
|
||||
inline auto crc16(const string& self) -> string;
|
||||
inline auto crc32(const string& self) -> string;
|
||||
inline auto sha256(const string& self) -> string;
|
||||
inline auto crc16(rstring self) -> string;
|
||||
inline auto crc32(rstring self) -> string;
|
||||
inline auto sha256(rstring self) -> string;
|
||||
|
||||
//match.hpp
|
||||
inline auto match(const string& self, rstring source) -> bool;
|
||||
inline auto imatch(const string& self, rstring source) -> bool;
|
||||
inline auto tokenize(const char* s, const char* p) -> bool;
|
||||
inline auto tokenize(lstring& list, const char* s, const char* p) -> bool;
|
||||
|
||||
//path.hpp
|
||||
inline auto pathname(const string& self) -> string;
|
||||
inline auto filename(const string& self) -> string;
|
||||
inline auto pathname(rstring self) -> string;
|
||||
inline auto filename(rstring self) -> string;
|
||||
|
||||
inline auto dirname(const string& self) -> string;
|
||||
inline auto basename(const string& self) -> string;
|
||||
inline auto prefixname(const string& self) -> string;
|
||||
inline auto suffixname(const string& self) -> string;
|
||||
inline auto dirname(rstring self) -> string;
|
||||
inline auto basename(rstring self) -> string;
|
||||
inline auto prefixname(rstring self) -> string;
|
||||
inline auto suffixname(rstring self) -> string;
|
||||
|
||||
//platform.hpp
|
||||
inline auto activepath() -> string;
|
||||
|
@ -97,41 +57,8 @@ inline auto localpath() -> string;
|
|||
inline auto sharedpath() -> string;
|
||||
inline auto temppath() -> string;
|
||||
|
||||
//replace.hpp
|
||||
template<bool I, bool Q> inline auto _replace(string& self, rstring from, rstring to, long limit = LONG_MAX) -> string&;
|
||||
inline auto replace(string& self, rstring from, rstring to, long limit = LONG_MAX) -> string&;
|
||||
inline auto ireplace(string& self, rstring from, rstring to, long limit = LONG_MAX) -> string&;
|
||||
inline auto qreplace(string& self, rstring from, rstring to, long limit = LONG_MAX) -> string&;
|
||||
inline auto iqreplace(string& self, rstring from, rstring to, long limit = LONG_MAX) -> string&;
|
||||
|
||||
//split.hpp
|
||||
template<bool I, bool Q> inline auto _split(lstring& self, rstring source, rstring find, long limit = LONG_MAX) -> lstring&;
|
||||
inline auto split(string& self, rstring key, long limit = LONG_MAX) -> lstring;
|
||||
inline auto isplit(string& self, rstring key, long limit = LONG_MAX) -> lstring;
|
||||
inline auto qsplit(string& self, rstring key, long limit = LONG_MAX) -> lstring;
|
||||
inline auto iqsplit(string& self, rstring key, long limit = LONG_MAX) -> lstring;
|
||||
|
||||
//trim.hpp
|
||||
inline auto trim(string& self, rstring lhs, rstring rhs, long limit = LONG_MAX) -> string&;
|
||||
inline auto ltrim(string& self, rstring lhs, long limit = LONG_MAX) -> string&;
|
||||
inline auto rtrim(string& self, rstring rhs, long limit = LONG_MAX) -> string&;
|
||||
|
||||
inline auto itrim(string& self, rstring lhs, rstring rhs, long limit = LONG_MAX) -> string&;
|
||||
inline auto iltrim(string& self, rstring lhs, long limit = LONG_MAX) -> string&;
|
||||
inline auto irtrim(string& self, rstring rhs, long limit = LONG_MAX) -> string&;
|
||||
|
||||
inline auto strip(string& self) -> string&;
|
||||
inline auto lstrip(string& self) -> string&;
|
||||
inline auto rstrip(string& self) -> string&;
|
||||
|
||||
//utility.hpp
|
||||
inline auto fill(string& self, char fill = ' ') -> string&;
|
||||
inline auto hash(const string& self) -> unsigned;
|
||||
inline auto remove(string& self, unsigned offset, unsigned length) -> string&;
|
||||
inline auto reverse(string& self) -> string&;
|
||||
inline auto size(string& self, signed length, char fill = ' ') -> string&;
|
||||
inline auto slice(const string& self, signed offset = 0, signed length = -1) -> string;
|
||||
inline auto substr(rstring source, signed offset = 0, signed length = -1) -> string;
|
||||
inline auto slice(rstring self, signed offset = 0, signed length = -1) -> string;
|
||||
|
||||
inline auto integer(char* result, intmax_t value) -> char*;
|
||||
inline auto decimal(char* result, uintmax_t value) -> char*;
|
||||
|
@ -212,7 +139,7 @@ public:
|
|||
auto operator>=(const char* s) const -> bool { return strcmp(data(), s) >= 0; }
|
||||
|
||||
string(const string& source) : string() { operator=(source); }
|
||||
string(string&& source) : string() { operator=(std::move(source)); }
|
||||
string(string&& source) : string() { operator=(move(source)); }
|
||||
|
||||
auto begin() -> char* { return &get()[0]; }
|
||||
auto end() -> char* { return &get()[size()]; }
|
||||
|
@ -226,82 +153,67 @@ public:
|
|||
|
||||
//core.hpp
|
||||
inline auto operator[](signed) const -> const char&;
|
||||
template<typename... P> inline auto assign(P&&...) -> type&;
|
||||
template<typename T, typename... P> inline auto append(const T&, P&&...) -> type&;
|
||||
template<typename... P> inline auto append(const nall::format&, P&&...) -> type&;
|
||||
inline auto append() -> type&;
|
||||
template<typename T> inline auto _append(const stringify<T>&) -> string&;
|
||||
inline auto empty() const -> bool;
|
||||
inline auto length() const -> unsigned;
|
||||
|
||||
//datetime.hpp
|
||||
inline static auto date(time_t = 0) -> string;
|
||||
inline static auto time(time_t = 0) -> string;
|
||||
inline static auto datetime(time_t = 0) -> string;
|
||||
|
||||
//find.hpp
|
||||
template<bool, bool> inline auto _find(signed, rstring) const -> maybe<unsigned>;
|
||||
|
||||
inline auto find(rstring source) const -> maybe<unsigned>;
|
||||
inline auto ifind(rstring source) const -> maybe<unsigned>;
|
||||
inline auto qfind(rstring source) const -> maybe<unsigned>;
|
||||
inline auto iqfind(rstring source) const -> maybe<unsigned>;
|
||||
|
||||
inline auto findFrom(signed offset, rstring source) const -> maybe<unsigned>;
|
||||
inline auto ifindFrom(signed offset, rstring source) const -> maybe<unsigned>;
|
||||
|
||||
//format.hpp
|
||||
inline auto format(const nall::format& params) -> type&;
|
||||
|
||||
//utility.hpp
|
||||
inline static auto read(const string& filename) -> string;
|
||||
inline static auto repeat(const string& pattern, unsigned times) -> string;
|
||||
|
||||
//extension methods
|
||||
//=================
|
||||
|
||||
//compare.hpp
|
||||
auto compare(rstring source) const -> signed { return nall::compare(*this, source); }
|
||||
auto icompare(rstring source) const -> signed { return nall::icompare(*this, source); }
|
||||
template<bool> inline static auto _compare(const char*, unsigned, const char*, unsigned) -> signed;
|
||||
|
||||
auto equals(rstring source) const -> bool { return nall::equals(*this, source); }
|
||||
auto iequals(rstring source) const -> bool { return nall::iequals(*this, source); }
|
||||
inline auto compare(rstring source) const -> signed;
|
||||
inline auto icompare(rstring source) const -> signed;
|
||||
|
||||
auto beginsWith(rstring source) const -> bool { return nall::beginsWith(*this, source); }
|
||||
auto ibeginsWith(rstring source) const -> bool { return nall::ibeginsWith(*this, source); }
|
||||
inline auto equals(rstring source) const -> bool;
|
||||
inline auto iequals(rstring source) const -> bool;
|
||||
|
||||
auto endsWith(rstring source) const -> bool { return nall::endsWith(*this, source); }
|
||||
auto iendsWith(rstring source) const -> bool { return nall::iendsWith(*this, source); }
|
||||
inline auto beginsWith(rstring source) const -> bool;
|
||||
inline auto ibeginsWith(rstring source) const -> bool;
|
||||
|
||||
inline auto endsWith(rstring source) const -> bool;
|
||||
inline auto iendsWith(rstring source) const -> bool;
|
||||
|
||||
//convert.hpp
|
||||
auto downcase() -> type& { return nall::downcase(*this); }
|
||||
auto upcase() -> type& { return nall::upcase(*this); }
|
||||
inline auto downcase() -> type&;
|
||||
inline auto upcase() -> type&;
|
||||
|
||||
auto qdowncase() -> type& { return nall::qdowncase(*this); }
|
||||
auto qupcase() -> type& { return nall::qupcase(*this); }
|
||||
inline auto qdowncase() -> type&;
|
||||
inline auto qupcase() -> type&;
|
||||
|
||||
auto transform(rstring from, rstring to) -> type& { return nall::transform(*this, from, to); }
|
||||
|
||||
//core.hpp
|
||||
template<typename... P> auto assign(P&&... p) -> type& { return nall::assign(*this, forward<P>(p)...); }
|
||||
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, forward<P>(p)...); }
|
||||
auto empty() const -> bool { return nall::empty(*this); }
|
||||
auto length() const -> unsigned { return nall::length(*this); }
|
||||
|
||||
//find.hpp
|
||||
auto find(rstring source) const -> maybe<unsigned> { return nall::find(*this, source); }
|
||||
auto ifind(rstring source) const -> maybe<unsigned> { return nall::ifind(*this, source); }
|
||||
auto qfind(rstring source) const -> maybe<unsigned> { return nall::qfind(*this, source); }
|
||||
auto iqfind(rstring source) const -> maybe<unsigned> { return nall::iqfind(*this, source); }
|
||||
|
||||
auto findFrom(signed offset, rstring source) const -> maybe<unsigned> { return nall::findFrom(*this, offset, source); }
|
||||
auto ifindFrom(signed offset, rstring source) const -> maybe<unsigned> { return nall::ifindFrom(*this, offset, source); }
|
||||
|
||||
//hash.hpp
|
||||
auto crc16() const -> string { return nall::crc16(*this); }
|
||||
auto crc32() const -> string { return nall::crc32(*this); }
|
||||
auto sha256() const -> string { return nall::sha256(*this); }
|
||||
inline auto transform(rstring from, rstring to) -> type&;
|
||||
|
||||
//match.hpp
|
||||
auto match(rstring source) const -> bool { return nall::match(*this, source); }
|
||||
auto imatch(rstring source) const -> bool { return nall::imatch(*this, source); }
|
||||
|
||||
//path.hpp
|
||||
auto pathname() const -> string { return nall::pathname(*this); }
|
||||
auto filename() const -> string { return nall::filename(*this); }
|
||||
|
||||
auto dirname() const -> string { return nall::dirname(*this); }
|
||||
auto basename() const -> string { return nall::basename(*this); }
|
||||
auto prefixname() const -> string { return nall::prefixname(*this); }
|
||||
auto suffixname() const -> string { return nall::suffixname(*this); }
|
||||
inline auto match(rstring source) const -> bool;
|
||||
inline auto imatch(rstring source) const -> bool;
|
||||
|
||||
//replace.hpp
|
||||
auto replace(rstring from, rstring to, long limit = LONG_MAX) -> type& { return nall::_replace<0, 0>(*this, from, to, limit); }
|
||||
auto ireplace(rstring from, rstring to, long limit = LONG_MAX) -> type& { return nall::_replace<1, 0>(*this, from, to, limit); }
|
||||
auto qreplace(rstring from, rstring to, long limit = LONG_MAX) -> type& { return nall::_replace<0, 1>(*this, from, to, limit); }
|
||||
auto iqreplace(rstring from, rstring to, long limit = LONG_MAX) -> type& { return nall::_replace<1, 1>(*this, from, to, limit); }
|
||||
template<bool, bool> inline auto _replace(rstring, rstring, long) -> type&;
|
||||
inline auto replace(rstring from, rstring to, long limit = LONG_MAX) -> type&;
|
||||
inline auto ireplace(rstring from, rstring to, long limit = LONG_MAX) -> type&;
|
||||
inline auto qreplace(rstring from, rstring to, long limit = LONG_MAX) -> type&;
|
||||
inline auto iqreplace(rstring from, rstring to, long limit = LONG_MAX) -> type&;
|
||||
|
||||
//split.hpp
|
||||
inline auto split(rstring key, long limit = LONG_MAX) const -> lstring;
|
||||
|
@ -310,40 +222,28 @@ public:
|
|||
inline auto iqsplit(rstring key, long limit = LONG_MAX) const -> lstring;
|
||||
|
||||
//trim.hpp
|
||||
auto trim(rstring lhs, rstring rhs, long limit = LONG_MAX) -> type& { return nall::trim(*this, lhs, rhs, limit); }
|
||||
auto ltrim(rstring lhs, long limit = LONG_MAX) -> type& { return nall::ltrim(*this, lhs, limit); }
|
||||
auto rtrim(rstring rhs, long limit = LONG_MAX) -> type& { return nall::rtrim(*this, rhs, limit); }
|
||||
inline auto trim(rstring lhs, rstring rhs, long limit = LONG_MAX) -> type&;
|
||||
inline auto ltrim(rstring lhs, long limit = LONG_MAX) -> type&;
|
||||
inline auto rtrim(rstring rhs, long limit = LONG_MAX) -> type&;
|
||||
|
||||
auto itrim(rstring lhs, rstring rhs, long limit = LONG_MAX) -> type& { return nall::itrim(*this, lhs, rhs, limit); }
|
||||
auto iltrim(rstring lhs, long limit = LONG_MAX) -> type& { return nall::iltrim(*this, lhs, limit); }
|
||||
auto irtrim(rstring rhs, long limit = LONG_MAX) -> type& { return nall::irtrim(*this, rhs, limit); }
|
||||
inline auto itrim(rstring lhs, rstring rhs, long limit = LONG_MAX) -> type&;
|
||||
inline auto iltrim(rstring lhs, long limit = LONG_MAX) -> type&;
|
||||
inline auto irtrim(rstring rhs, long limit = LONG_MAX) -> type&;
|
||||
|
||||
auto strip() -> type& { return nall::strip(*this); }
|
||||
auto lstrip() -> type& { return nall::lstrip(*this); }
|
||||
auto rstrip() -> type& { return nall::rstrip(*this); }
|
||||
inline auto strip() -> type&;
|
||||
inline auto lstrip() -> type&;
|
||||
inline auto rstrip() -> type&;
|
||||
|
||||
//utility.hpp
|
||||
auto fill(char fill = ' ') -> type& { return nall::fill(*this, fill); }
|
||||
auto hash() const -> const type& { return nall::hash(*this), *this; }
|
||||
auto remove(unsigned offset, unsigned length) -> type& { return nall::remove(*this, offset, length); }
|
||||
auto reverse() -> type& { return nall::reverse(*this); }
|
||||
auto size(signed length, char fill = ' ') -> type& { return nall::size(*this, length, fill); }
|
||||
auto slice(signed offset = 0, signed length = -1) const -> string { return nall::slice(*this, offset, length); }
|
||||
|
||||
#if defined(QSTRING_H)
|
||||
inline operator QString() const;
|
||||
#endif
|
||||
inline static auto read(rstring filename) -> string;
|
||||
inline static auto repeat(rstring pattern, unsigned times) -> string;
|
||||
inline auto fill(char fill = ' ') -> type&;
|
||||
inline auto hash() const -> unsigned;
|
||||
inline auto remove(unsigned offset, unsigned length) -> type&;
|
||||
inline auto reverse() -> type&;
|
||||
inline auto size(signed length, char fill = ' ') -> type&;
|
||||
};
|
||||
|
||||
//list.hpp
|
||||
template<typename... P> auto append(lstring& self, const string& value, P&&... p) -> lstring&;
|
||||
inline auto append(lstring& self) -> lstring&;
|
||||
inline auto find(const lstring& self, const string& source) -> maybe<unsigned>;
|
||||
inline auto ifind(const lstring& self, const string& source) -> maybe<unsigned>;
|
||||
inline auto match(const lstring& self, const string& pattern) -> lstring;
|
||||
inline auto merge(const lstring& self, const string& separator) -> string;
|
||||
inline auto strip(lstring& self) -> lstring&;
|
||||
|
||||
struct lstring : vector<string> {
|
||||
using type = lstring;
|
||||
|
||||
|
@ -362,33 +262,25 @@ struct lstring : vector<string> {
|
|||
|
||||
inline auto isort() -> type&;
|
||||
|
||||
//extension methods
|
||||
//=================
|
||||
template<typename... P> inline auto append(const string&, P&&...) -> type&;
|
||||
inline auto append() -> type&;
|
||||
|
||||
//list.hpp
|
||||
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, forward<P>(p)...); }
|
||||
auto find(const string& source) const -> maybe<unsigned> { return nall::find(*this, source); }
|
||||
auto ifind(const string& source) const -> maybe<unsigned> { return nall::ifind(*this, source); }
|
||||
auto match(const string& pattern) const -> lstring { return nall::match(*this, pattern); }
|
||||
auto merge(const string& separator) const -> string { return nall::merge(*this, separator); }
|
||||
auto strip() -> type& { return nall::strip(*this); }
|
||||
inline auto find(rstring source) const -> maybe<unsigned>;
|
||||
inline auto ifind(rstring source) const -> maybe<unsigned>;
|
||||
inline auto match(rstring pattern) const -> lstring;
|
||||
inline auto merge(rstring separator) const -> string;
|
||||
inline auto strip() -> type&;
|
||||
|
||||
//split.hpp
|
||||
auto split(rstring source, rstring on, long limit = LONG_MAX) -> type& { return nall::_split<0, 0>(*this, source, on, limit); }
|
||||
auto isplit(rstring source, rstring on, long limit = LONG_MAX) -> type& { return nall::_split<1, 0>(*this, source, on, limit); }
|
||||
auto qsplit(rstring source, rstring on, long limit = LONG_MAX) -> type& { return nall::_split<0, 1>(*this, source, on, limit); }
|
||||
auto iqsplit(rstring source, rstring on, long limit = LONG_MAX) -> type& { return nall::_split<1, 1>(*this, source, on, limit); }
|
||||
template<bool, bool> inline auto _split(rstring, rstring, long) -> lstring&;
|
||||
};
|
||||
|
||||
//format.hpp
|
||||
template<typename T, typename... P> inline auto append(format& self, const T& value, P&&... p) -> format&;
|
||||
inline auto append(format& self) -> format&;
|
||||
|
||||
struct format : vector<string> {
|
||||
using type = format;
|
||||
|
||||
template<typename... P> format(P&&... p) { reserve(sizeof...(p)); append(forward<P>(p)...); }
|
||||
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, forward<P>(p)...); }
|
||||
template<typename T, typename... P> inline auto append(const T&, P&&... p) -> type&;
|
||||
inline auto append() -> type&;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -186,34 +186,24 @@ template<> struct stringify<const string&> {
|
|||
stringify(const string& source) : _text(source) {}
|
||||
};
|
||||
|
||||
#if defined(QSTRING_H)
|
||||
|
||||
//Qt
|
||||
|
||||
template<> struct stringify<QString> {
|
||||
const QString& _text;
|
||||
auto data() const -> const char* { return _text.toUtf8().constData(); }
|
||||
auto size() const -> unsigned { return _text.size(); }
|
||||
stringify(const QString& source) : _text(source) {}
|
||||
template<> struct stringify<string_view> {
|
||||
const string_view& _view;
|
||||
auto data() const -> const char* { return _view.data(); }
|
||||
auto size() const -> unsigned { return _view.size(); }
|
||||
stringify(const string_view& source) : _view(source) {}
|
||||
};
|
||||
|
||||
template<> struct stringify<const QString&> {
|
||||
const QString& _text;
|
||||
auto data() const -> const char* { return _text.toUtf8().constData(); }
|
||||
auto size() const -> unsigned { return _text.size(); }
|
||||
stringify(const QString& source) : _text(source) {}
|
||||
template<> struct stringify<const string_view&> {
|
||||
const string_view& _view;
|
||||
auto data() const -> const char* { return _view.data(); }
|
||||
auto size() const -> unsigned { return _view.size(); }
|
||||
stringify(const string_view& source) : _view(source) {}
|
||||
};
|
||||
|
||||
string::operator QString() const {
|
||||
return QString::fromUtf8(*this);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
template<typename T> auto make_string(T value) -> stringify<T> {
|
||||
return stringify<T>(std::forward<T>(value));
|
||||
return stringify<T>(forward<T>(value));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,47 +2,48 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
template<bool Insensitive> inline auto _compare(const char* target, unsigned capacity, const char* source, unsigned size) -> signed {
|
||||
template<bool Insensitive>
|
||||
auto string::_compare(const char* target, unsigned capacity, const char* source, unsigned size) -> signed {
|
||||
if(Insensitive) return memory::icompare(target, capacity, source, size);
|
||||
return memory::compare(target, capacity, source, size);
|
||||
}
|
||||
|
||||
auto compare(const string& self, rstring source) -> signed {
|
||||
return memory::compare(self.data(), self.size(), source.data(), source.size());
|
||||
auto string::compare(rstring source) const -> signed {
|
||||
return memory::compare(data(), size(), source.data(), source.size());
|
||||
}
|
||||
|
||||
auto icompare(const string& self, rstring source) -> signed {
|
||||
return memory::icompare(self.data(), self.size(), source.data(), source.size());
|
||||
auto string::icompare(rstring source) const -> signed {
|
||||
return memory::icompare(data(), size(), source.data(), source.size());
|
||||
}
|
||||
|
||||
auto equals(const string& self, rstring source) -> bool {
|
||||
if(self.size() != source.size()) return false;
|
||||
return memory::compare(self.data(), source.data(), source.size()) == 0;
|
||||
auto string::equals(rstring source) const -> bool {
|
||||
if(size() != source.size()) return false;
|
||||
return memory::compare(data(), source.data(), source.size()) == 0;
|
||||
}
|
||||
|
||||
auto iequals(const string& self, rstring source) -> bool {
|
||||
if(self.size() != source.size()) return false;
|
||||
return memory::icompare(self.data(), source.data(), source.size()) == 0;
|
||||
auto string::iequals(rstring source) const -> bool {
|
||||
if(size() != source.size()) return false;
|
||||
return memory::icompare(data(), source.data(), source.size()) == 0;
|
||||
}
|
||||
|
||||
auto beginsWith(const string& self, rstring source) -> bool {
|
||||
if(source.size() > self.size()) return false;
|
||||
return memory::compare(self.data(), source.data(), source.size()) == 0;
|
||||
auto string::beginsWith(rstring source) const -> bool {
|
||||
if(source.size() > size()) return false;
|
||||
return memory::compare(data(), source.data(), source.size()) == 0;
|
||||
}
|
||||
|
||||
auto ibeginsWith(const string& self, rstring source) -> bool {
|
||||
if(source.size() > self.size()) return false;
|
||||
return memory::icompare(self.data(), source.data(), source.size()) == 0;
|
||||
auto string::ibeginsWith(rstring source) const -> bool {
|
||||
if(source.size() > size()) return false;
|
||||
return memory::icompare(data(), source.data(), source.size()) == 0;
|
||||
}
|
||||
|
||||
auto endsWith(const string& self, rstring source) -> bool {
|
||||
if(source.size() > self.size()) return false;
|
||||
return memory::compare(self.data() + self.size() - source.size(), source.data(), source.size()) == 0;
|
||||
auto string::endsWith(rstring source) const -> bool {
|
||||
if(source.size() > size()) return false;
|
||||
return memory::compare(data() + size() - source.size(), source.data(), source.size()) == 0;
|
||||
}
|
||||
|
||||
auto iendsWith(const string& self, rstring source) -> bool {
|
||||
if(source.size() > self.size()) return false;
|
||||
return memory::icompare(self.data() + self.size() - source.size(), source.data(), source.size()) == 0;
|
||||
auto string::iendsWith(rstring source) const -> bool {
|
||||
if(source.size() > size()) return false;
|
||||
return memory::icompare(data() + size() - source.size(), source.data(), source.size()) == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,44 +2,44 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
auto downcase(string& self) -> string& {
|
||||
char* p = self.get();
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
auto string::downcase() -> string& {
|
||||
char* p = get();
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(p[n] >= 'A' && p[n] <= 'Z') p[n] += 0x20;
|
||||
}
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto qdowncase(string& self) -> string& {
|
||||
char* p = self.get();
|
||||
for(unsigned n = 0, quoted = 0; n < self.size(); n++) {
|
||||
auto string::qdowncase() -> string& {
|
||||
char* p = get();
|
||||
for(unsigned n = 0, quoted = 0; n < size(); n++) {
|
||||
if(p[n] == '\"') quoted ^= 1;
|
||||
if(!quoted && p[n] >= 'A' && p[n] <= 'Z') p[n] += 0x20;
|
||||
}
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto upcase(string& self) -> string& {
|
||||
char* p = self.get();
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
auto string::upcase() -> string& {
|
||||
char* p = get();
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(p[n] >= 'a' && p[n] <= 'z') p[n] -= 0x20;
|
||||
}
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto qupcase(string& self) -> string& {
|
||||
char* p = self.get();
|
||||
for(unsigned n = 0, quoted = 0; n < self.size(); n++) {
|
||||
auto string::qupcase() -> string& {
|
||||
char* p = get();
|
||||
for(unsigned n = 0, quoted = 0; n < size(); n++) {
|
||||
if(p[n] == '\"') quoted ^= 1;
|
||||
if(!quoted && p[n] >= 'a' && p[n] <= 'z') p[n] -= 0x20;
|
||||
}
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto transform(string& self, rstring from, rstring to) -> string& {
|
||||
if(from.size() != to.size() || from.size() == 0) return self; //patterns must be the same length
|
||||
char* p = self.get();
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
auto string::transform(rstring from, rstring to) -> string& {
|
||||
if(from.size() != to.size() || from.size() == 0) return *this; //patterns must be the same length
|
||||
char* p = get();
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
for(unsigned s = 0; s < from.size(); s++) {
|
||||
if(p[n] == from[s]) {
|
||||
p[n] = to[s];
|
||||
|
@ -47,7 +47,7 @@ auto transform(string& self, rstring from, rstring to) -> string& {
|
|||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,39 +20,37 @@ auto string::operator[](signed position) const -> const char& {
|
|||
return data()[position];
|
||||
}
|
||||
|
||||
template<typename... P> auto assign(string& self, P&&... p) -> string& {
|
||||
self.resize(0);
|
||||
return self.append(std::forward<P>(p)...);
|
||||
template<typename... P> auto string::assign(P&&... p) -> string& {
|
||||
resize(0);
|
||||
return append(forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<typename T, typename... P> auto append(string& self, const T& value, P&&... p) -> string& {
|
||||
_append(self, make_string(value));
|
||||
return self.append(std::forward<P>(p)...);
|
||||
template<typename T, typename... P> auto string::append(const T& value, P&&... p) -> string& {
|
||||
_append(make_string(value));
|
||||
return append(forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<typename... P> auto append(string& self, const format& value, P&&... p) -> string& {
|
||||
self.format(value);
|
||||
return self.append(std::forward<P>(p)...);
|
||||
template<typename... P> auto string::append(const nall::format& value, P&&... p) -> string& {
|
||||
format(value);
|
||||
return append(forward<P>(p)...);
|
||||
}
|
||||
|
||||
auto append(string& self) -> string& {
|
||||
return self;
|
||||
auto string::append() -> string& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> auto _append(string& self, const stringify<T>& source) -> string& {
|
||||
unsigned size = self.size();
|
||||
unsigned length = source.size();
|
||||
self.resize(size + length);
|
||||
memory::copy(self.get() + size, source.data(), length);
|
||||
return self;
|
||||
template<typename T> auto string::_append(const stringify<T>& source) -> string& {
|
||||
resize(size() + source.size());
|
||||
memory::copy(get() + size() - source.size(), source.data(), source.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto empty(const string& self) -> bool {
|
||||
return self.size() == 0;
|
||||
auto string::empty() const -> bool {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
auto length(const string& self) -> unsigned {
|
||||
return strlen(self.data());
|
||||
auto string::length() const -> unsigned {
|
||||
return strlen(data());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ inline string literalNumber(const char*& s) {
|
|||
p += prefix;
|
||||
while(p[0] == '\'' || p[0] == '0' || p[0] == '1') p++;
|
||||
if(p - s <= prefix) throw "invalid binary literal";
|
||||
string result = substr(s, 0, p - s);
|
||||
string result = slice(s, 0, p - s);
|
||||
s = p;
|
||||
return result;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ inline string literalNumber(const char*& s) {
|
|||
p += prefix;
|
||||
while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '7')) p++;
|
||||
if(p - s <= prefix) throw "invalid octal literal";
|
||||
string result = substr(s, 0, p - s);
|
||||
string result = slice(s, 0, p - s);
|
||||
s = p;
|
||||
return result;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ inline string literalNumber(const char*& s) {
|
|||
p += prefix;
|
||||
while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '9') || (p[0] >= 'A' && p[0] <= 'F') || (p[0] >= 'a' && p[0] <= 'f')) p++;
|
||||
if(p - s <= prefix) throw "invalid hex literal";
|
||||
string result = substr(s, 0, p - s);
|
||||
string result = slice(s, 0, p - s);
|
||||
s = p;
|
||||
return result;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ inline string literalNumber(const char*& s) {
|
|||
//decimal
|
||||
while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '9')) p++;
|
||||
if(p[0] != '.') {
|
||||
string result = substr(s, 0, p - s);
|
||||
string result = slice(s, 0, p - s);
|
||||
s = p;
|
||||
return result;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ inline string literalNumber(const char*& s) {
|
|||
//floating-point
|
||||
p++;
|
||||
while(p[0] == '\'' || (p[0] >= '0' && p[0] <= '9')) p++;
|
||||
string result = substr(s, 0, p - s);
|
||||
string result = slice(s, 0, p - s);
|
||||
s = p;
|
||||
return result;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ inline string literalString(const char*& s) {
|
|||
while(p[0] && p[0] != escape) p++;
|
||||
if(*p++ != escape) throw "unclosed string literal";
|
||||
|
||||
string result = substr(s, 0, p - s);
|
||||
string result = slice(s, 0, p - s);
|
||||
s = p;
|
||||
return result;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ inline string literalVariable(const char*& s) {
|
|||
|
||||
while(p[0] == '_' || p[0] == '.' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z') || (p[0] >= '0' && p[0] <= '9')) p++;
|
||||
|
||||
string result = substr(s, 0, p - s);
|
||||
string result = slice(s, 0, p - s);
|
||||
s = p;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -2,28 +2,26 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
template<bool Insensitive, bool Quoted> auto _find(const string& self, signed offset, rstring source) -> maybe<unsigned> {
|
||||
template<bool Insensitive, bool Quoted> auto string::_find(signed offset, rstring source) const -> maybe<unsigned> {
|
||||
if(source.size() == 0) return nothing;
|
||||
|
||||
const char* p = self.data();
|
||||
unsigned size = self.size();
|
||||
|
||||
for(unsigned n = offset, quoted = 0; n < size;) {
|
||||
auto p = data();
|
||||
for(unsigned n = offset, quoted = 0; n < size();) {
|
||||
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
|
||||
if(_compare<Insensitive>(p + n, size - n, source.data(), source.size())) { n++; continue; }
|
||||
if(_compare<Insensitive>(p + n, size() - n, source.data(), source.size())) { n++; continue; }
|
||||
return n - offset;
|
||||
}
|
||||
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto find(const string& self, rstring source) -> maybe<unsigned> { return _find<0, 0>(self, 0, source); }
|
||||
auto ifind(const string& self, rstring source) -> maybe<unsigned> { return _find<1, 0>(self, 0, source); }
|
||||
auto qfind(const string& self, rstring source) -> maybe<unsigned> { return _find<0, 1>(self, 0, source); }
|
||||
auto iqfind(const string& self, rstring source) -> maybe<unsigned> { return _find<1, 1>(self, 0, source); }
|
||||
auto string::find(rstring source) const -> maybe<unsigned> { return _find<0, 0>(0, source); }
|
||||
auto string::ifind(rstring source) const -> maybe<unsigned> { return _find<1, 0>(0, source); }
|
||||
auto string::qfind(rstring source) const -> maybe<unsigned> { return _find<0, 1>(0, source); }
|
||||
auto string::iqfind(rstring source) const -> maybe<unsigned> { return _find<1, 1>(0, source); }
|
||||
|
||||
auto findFrom(const string& self, signed offset, rstring source) -> maybe<unsigned> { return _find<0, 0>(self, offset, source); }
|
||||
auto ifindFrom(const string& self, signed offset, rstring source) -> maybe<unsigned> { return _find<1, 0>(self, offset, source); }
|
||||
auto string::findFrom(signed offset, rstring source) const -> maybe<unsigned> { return _find<0, 0>(offset, source); }
|
||||
auto string::ifindFrom(signed offset, rstring source) const -> maybe<unsigned> { return _find<1, 0>(offset, source); }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -59,17 +59,17 @@ auto string::format(const nall::format& params) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename... P> auto append(format& self, const T& value, P&&... p) -> format& {
|
||||
self.vector<string>::append(value);
|
||||
append(self, std::forward<P>(p)...);
|
||||
template<typename T, typename... P> auto format::append(const T& value, P&&... p) -> format& {
|
||||
vector<string>::append(value);
|
||||
return append(forward<P>(p)...);
|
||||
}
|
||||
|
||||
auto append(format& self) -> format& {
|
||||
return self;
|
||||
auto format::append() -> format& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... P> auto print(P&&... p) -> void {
|
||||
string s{std::forward<P>(p)...};
|
||||
string s{forward<P>(p)...};
|
||||
fputs(s.data(), stdout);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,15 +18,15 @@ namespace Hash {
|
|||
}
|
||||
}
|
||||
|
||||
auto crc16(const string& self) -> string {
|
||||
auto crc16(rstring self) -> string {
|
||||
return Hash::CRC16(self.data(), self.size()).digest();
|
||||
}
|
||||
|
||||
auto crc32(const string& self) -> string {
|
||||
auto crc32(rstring self) -> string {
|
||||
return Hash::CRC32(self.data(), self.size()).digest();
|
||||
}
|
||||
|
||||
auto sha256(const string& self) -> string {
|
||||
auto sha256(rstring self) -> string {
|
||||
return Hash::SHA256(self.data(), self.size()).digest();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,52 +22,52 @@ auto lstring::isort() -> lstring& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename... P> auto append(lstring& self, const string& data, P&&... p) -> lstring& {
|
||||
self.vector::append(data);
|
||||
append(self, std::forward<P>(p)...);
|
||||
return self;
|
||||
template<typename... P> auto lstring::append(const string& data, P&&... p) -> lstring& {
|
||||
vector::append(data);
|
||||
append(forward<P>(p)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto append(lstring& self) -> lstring& {
|
||||
return self;
|
||||
auto lstring::append() -> lstring& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto find(const lstring& self, const string& source) -> maybe<unsigned> {
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
if(self[n].equals(source)) return n;
|
||||
auto lstring::find(rstring source) const -> maybe<unsigned> {
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(operator[](n).equals(source)) return n;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto ifind(const lstring& self, const string& source) -> maybe<unsigned> {
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
if(self[n].iequals(source)) return n;
|
||||
auto lstring::ifind(rstring source) const -> maybe<unsigned> {
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(operator[](n).iequals(source)) return n;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto match(const lstring& self, const string& pattern) -> lstring {
|
||||
auto lstring::match(rstring pattern) const -> lstring {
|
||||
lstring result;
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
if(self[n].match(pattern)) result.append(self[n]);
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(operator[](n).match(pattern)) result.append(operator[](n));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto merge(const lstring& self, const string& separator) -> string {
|
||||
auto lstring::merge(rstring separator) const -> string {
|
||||
string output;
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
output.append(self[n]);
|
||||
if(n < self.size() - 1) output.append(separator);
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
output.append(operator[](n));
|
||||
if(n < size() - 1) output.append(separator.data());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
auto strip(lstring& self) -> lstring& {
|
||||
for(unsigned n = 0; n < self.size(); n++) {
|
||||
self[n].strip();
|
||||
auto lstring::strip() -> lstring& {
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
operator[](n).strip();
|
||||
}
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ protected:
|
|||
unsigned length = 0;
|
||||
while(valid(p[length])) length++;
|
||||
if(length == 0) throw "Invalid node name";
|
||||
_name = substr(p, 0, length);
|
||||
_name = slice(p, 0, length);
|
||||
p += length;
|
||||
}
|
||||
|
||||
|
@ -46,18 +46,18 @@ protected:
|
|||
unsigned length = 2;
|
||||
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
|
||||
if(p[length] != '\"') throw "Unescaped value";
|
||||
_value = {substr(p, 2, length - 2), "\n"};
|
||||
_value = {slice(p, 2, length - 2), "\n"};
|
||||
p += length + 1;
|
||||
} else if(*p == '=') {
|
||||
unsigned length = 1;
|
||||
while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++;
|
||||
if(p[length] == '\"') throw "Illegal character in value";
|
||||
_value = {substr(p, 1, length - 1), "\n"};
|
||||
_value = {slice(p, 1, length - 1), "\n"};
|
||||
p += length;
|
||||
} else if(*p == ':') {
|
||||
unsigned length = 1;
|
||||
while(p[length] && p[length] != '\n') length++;
|
||||
_value = {substr(p, 1, length - 1), "\n"};
|
||||
_value = {slice(p, 1, length - 1), "\n"};
|
||||
p += length;
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ protected:
|
|||
unsigned length = 0;
|
||||
while(valid(p[length])) length++;
|
||||
if(length == 0) throw "Invalid attribute name";
|
||||
node->_name = substr(p, 0, length);
|
||||
node->_name = slice(p, 0, length);
|
||||
node->parseData(p += length);
|
||||
node->_value.rtrim("\n", 1L);
|
||||
_children.append(node);
|
||||
|
@ -93,7 +93,7 @@ protected:
|
|||
if(depth <= _metadata) break;
|
||||
|
||||
if(text[y][depth] == ':') {
|
||||
_value.append(substr(text[y++], depth + 1), "\n");
|
||||
_value.append(slice(text[y++], depth + 1), "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,10 +99,10 @@ auto ManagedNode::_find(const string& query) const -> vector<Node> {
|
|||
|
||||
auto ManagedNode::_lookup(const string& path) const -> Node {
|
||||
if(auto position = path.find("/")) {
|
||||
auto name = path.slice(0, *position);
|
||||
auto name = slice(path, 0, *position);
|
||||
for(auto& node : _children) {
|
||||
if(name == node->_name) {
|
||||
return node->_lookup(path.slice(*position + 1));
|
||||
return node->_lookup(slice(path, *position + 1));
|
||||
}
|
||||
}
|
||||
} else for(auto& node : _children) {
|
||||
|
@ -113,14 +113,14 @@ auto ManagedNode::_lookup(const string& path) const -> Node {
|
|||
|
||||
auto ManagedNode::_create(const string& path) -> Node {
|
||||
if(auto position = path.find("/")) {
|
||||
auto name = path.slice(0, *position);
|
||||
auto name = slice(path, 0, *position);
|
||||
for(auto& node : _children) {
|
||||
if(name == node->_name) {
|
||||
return node->_create(path.slice(*position + 1));
|
||||
return node->_create(slice(path, *position + 1));
|
||||
}
|
||||
}
|
||||
_children.append(new ManagedNode(name));
|
||||
return _children.last()->_create(path.slice(*position + 1));
|
||||
return _children.last()->_create(slice(path, *position + 1));
|
||||
}
|
||||
for(auto& node : _children) {
|
||||
if(path == node->_name) return node;
|
||||
|
|
|
@ -4,8 +4,8 @@ namespace nall {
|
|||
|
||||
//todo: these functions are not binary-safe
|
||||
|
||||
auto match(const string& self, rstring source) -> bool {
|
||||
const char* s = self.data();
|
||||
auto string::match(rstring source) const -> bool {
|
||||
const char* s = data();
|
||||
const char* p = source.data();
|
||||
|
||||
const char* cp = nullptr;
|
||||
|
@ -28,12 +28,12 @@ auto match(const string& self, rstring source) -> bool {
|
|||
return !*p;
|
||||
}
|
||||
|
||||
auto imatch(const string& self, rstring source) -> bool {
|
||||
auto string::imatch(rstring source) const -> bool {
|
||||
static auto chrlower = [](char c) -> char {
|
||||
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
|
||||
};
|
||||
|
||||
const char* s = self.data();
|
||||
const char* s = data();
|
||||
const char* p = source.data();
|
||||
|
||||
const char* cp = nullptr;
|
||||
|
@ -56,7 +56,7 @@ auto imatch(const string& self, rstring source) -> bool {
|
|||
return !*p;
|
||||
}
|
||||
|
||||
inline bool tokenize(const char* s, const char* p) {
|
||||
auto tokenize(const char* s, const char* p) -> bool {
|
||||
while(*s) {
|
||||
if(*p == '*') {
|
||||
while(*s) if(tokenize(s++, p + 1)) return true;
|
||||
|
@ -74,7 +74,7 @@ auto tokenize(lstring& list, const char* s, const char* p) -> bool {
|
|||
const char* b = s;
|
||||
while(*s) {
|
||||
if(tokenize(list, s++, p + 1)) {
|
||||
list.prepend(substr(b, 0, --s - b));
|
||||
list.prepend(slice(b, 0, --s - b));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace nall {
|
|||
|
||||
// (/parent/child.type/)
|
||||
// (/parent/child.type/)name.type
|
||||
auto pathname(const string& self) -> string {
|
||||
auto pathname(rstring self) -> string {
|
||||
const char* p = self.data() + self.size() - 1;
|
||||
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
|
||||
if(*p == '/') return slice(self, 0, offset + 1);
|
||||
|
@ -14,7 +14,7 @@ auto pathname(const string& self) -> string {
|
|||
|
||||
// /parent/child.type/()
|
||||
// /parent/child.type/(name.type)
|
||||
auto filename(const string& self) -> string {
|
||||
auto filename(rstring self) -> string {
|
||||
const char* p = self.data() + self.size() - 1;
|
||||
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
|
||||
if(*p == '/') return slice(self, offset + 1);
|
||||
|
@ -24,18 +24,18 @@ auto filename(const string& self) -> string {
|
|||
|
||||
// (/parent/)child.type/
|
||||
// (/parent/child.type/)name.type
|
||||
auto dirname(const string& self) -> string {
|
||||
auto dirname(rstring self) -> string {
|
||||
const char* p = self.data() + self.size() - 1, *last = p;
|
||||
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
|
||||
if(*p == '/' && p == last) continue;
|
||||
if(*p == '/') return slice(self, 0, offset + 1);
|
||||
}
|
||||
return self; //this is the root directory
|
||||
return self.data(); //this is the root directory
|
||||
}
|
||||
|
||||
// /parent/(child.type/)
|
||||
// /parent/child.type/(name.type)
|
||||
auto basename(const string& self) -> string {
|
||||
auto basename(rstring self) -> string {
|
||||
const char* p = self.data() + self.size() - 1, *last = p;
|
||||
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
|
||||
if(*p == '/' && p == last) continue;
|
||||
|
@ -46,7 +46,7 @@ auto basename(const string& self) -> string {
|
|||
|
||||
// /parent/(child).type/
|
||||
// /parent/child.type/(name).type
|
||||
auto prefixname(const string& self) -> string {
|
||||
auto prefixname(rstring self) -> string {
|
||||
const char* p = self.data() + self.size() - 1, *last = p;
|
||||
for(signed offset = self.size() - 1, suffix = -1; offset >= 0; offset--, p--) {
|
||||
if(*p == '/' && p == last) continue;
|
||||
|
@ -59,7 +59,7 @@ auto prefixname(const string& self) -> string {
|
|||
|
||||
// /parent/child(.type)/
|
||||
// /parent/child.type/name(.type)
|
||||
auto suffixname(const string& self) -> string {
|
||||
auto suffixname(rstring self) -> string {
|
||||
const char* p = self.data() + self.size() - 1, *last = p;
|
||||
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
|
||||
if(*p == '/' && p == last) continue;
|
||||
|
|
|
@ -15,7 +15,7 @@ auto activepath() -> string {
|
|||
auto realpath(rstring name) -> string {
|
||||
string result;
|
||||
char path[PATH_MAX] = "";
|
||||
if(::realpath(name, path)) result = string{path}.transform("\\", "/").pathname();
|
||||
if(::realpath(name, path)) result = pathname(string{path}.transform("\\", "/"));
|
||||
if(result.empty()) return activepath();
|
||||
result.transform("\\", "/");
|
||||
if(result.endsWith("/") == false) result.append("/");
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct stringref {
|
||||
operator const char*() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
auto data() const -> const char* {
|
||||
return _data;
|
||||
}
|
||||
|
||||
auto size() const -> unsigned {
|
||||
if(!_initialized) _size = strlen(_data), _initialized = true;
|
||||
return _size;
|
||||
}
|
||||
|
||||
stringref() {
|
||||
_string = nullptr;
|
||||
_data = "";
|
||||
_size = 0;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
stringref(const char* source) {
|
||||
_string = nullptr;
|
||||
_data = source;
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
stringref(const string& source) {
|
||||
_string = nullptr;
|
||||
_data = source.data();
|
||||
_size = source.size();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
template<typename... P> stringref(P&&... p) {
|
||||
_string = new string{std::forward<P>(p)...};
|
||||
_data = _string->data();
|
||||
_size = _string->size();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
~stringref() {
|
||||
if(_string) delete _string;
|
||||
}
|
||||
|
||||
stringref(const stringref& source) = delete;
|
||||
stringref(stringref&& source) = delete;
|
||||
|
||||
protected:
|
||||
string* _string;
|
||||
const char* _data;
|
||||
mutable signed _size;
|
||||
mutable unsigned _initialized;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -3,16 +3,16 @@
|
|||
namespace nall {
|
||||
|
||||
template<bool Insensitive, bool Quoted>
|
||||
auto _replace(string& self, rstring from, rstring to, long limit) -> string& {
|
||||
if(limit <= 0 || from.size() == 0) return self;
|
||||
auto string::_replace(rstring from, rstring to, long limit) -> string& {
|
||||
if(limit <= 0 || from.size() == 0) return *this;
|
||||
|
||||
signed size = self.size();
|
||||
signed size = this->size();
|
||||
signed matches = 0;
|
||||
signed quoted = 0;
|
||||
|
||||
//count matches first, so that we only need to reallocate memory once
|
||||
//(recording matches would also require memory allocation, so this is not done)
|
||||
{ const char* p = self.data();
|
||||
{ const char* p = data();
|
||||
for(signed n = 0; n <= size - (signed)from.size();) {
|
||||
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
|
||||
if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }
|
||||
|
@ -21,11 +21,11 @@ auto _replace(string& self, rstring from, rstring to, long limit) -> string& {
|
|||
n += from.size();
|
||||
}
|
||||
}
|
||||
if(matches == 0) return self;
|
||||
if(matches == 0) return *this;
|
||||
|
||||
//in-place overwrite
|
||||
if(to.size() == from.size()) {
|
||||
char* p = self.get();
|
||||
char* p = get();
|
||||
|
||||
for(signed n = 0, remaining = matches, quoted = 0; n <= size - (signed)from.size();) {
|
||||
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
|
||||
|
@ -40,7 +40,7 @@ auto _replace(string& self, rstring from, rstring to, long limit) -> string& {
|
|||
|
||||
//left-to-right shrink
|
||||
else if(to.size() < from.size()) {
|
||||
char* p = self.get();
|
||||
char* p = get();
|
||||
signed offset = 0;
|
||||
signed base = 0;
|
||||
|
||||
|
@ -58,15 +58,15 @@ auto _replace(string& self, rstring from, rstring to, long limit) -> string& {
|
|||
}
|
||||
|
||||
memory::move(p + offset, p + base, size - base);
|
||||
self.resize(size - matches * (from.size() - to.size()));
|
||||
resize(size - matches * (from.size() - to.size()));
|
||||
}
|
||||
|
||||
//right-to-left expand
|
||||
else if(to.size() > from.size()) {
|
||||
self.resize(size + matches * (to.size() - from.size()));
|
||||
char* p = self.get();
|
||||
resize(size + matches * (to.size() - from.size()));
|
||||
char* p = get();
|
||||
|
||||
signed offset = self.size();
|
||||
signed offset = this->size();
|
||||
signed base = size;
|
||||
|
||||
for(signed n = size, remaining = matches; n >= (signed)from.size();) { //quoted reused from parent scope since we are iterating backward
|
||||
|
@ -83,13 +83,13 @@ auto _replace(string& self, rstring from, rstring to, long limit) -> string& {
|
|||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto replace(string& self, rstring from, rstring to, long limit) -> string& { return _replace<0, 0>(self, from, to, limit); }
|
||||
auto ireplace(string& self, rstring from, rstring to, long limit) -> string& { return _replace<1, 0>(self, from, to, limit); }
|
||||
auto qreplace(string& self, rstring from, rstring to, long limit) -> string& { return _replace<0, 1>(self, from, to, limit); }
|
||||
auto iqreplace(string& self, rstring from, rstring to, long limit) -> string& { return _replace<1, 1>(self, from, to, limit); }
|
||||
auto string::replace(rstring from, rstring to, long limit) -> string& { return _replace<0, 0>(from, to, limit); }
|
||||
auto string::ireplace(rstring from, rstring to, long limit) -> string& { return _replace<1, 0>(from, to, limit); }
|
||||
auto string::qreplace(rstring from, rstring to, long limit) -> string& { return _replace<0, 1>(from, to, limit); }
|
||||
auto string::iqreplace(rstring from, rstring to, long limit) -> string& { return _replace<1, 1>(from, to, limit); }
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
template<bool Insensitive, bool Quoted> auto _split(lstring& self, rstring source, rstring find, long limit) -> lstring& {
|
||||
self.reset();
|
||||
if(limit <= 0 || find.size() == 0) return self;
|
||||
template<bool Insensitive, bool Quoted>
|
||||
auto lstring::_split(rstring source, rstring find, long limit) -> lstring& {
|
||||
reset();
|
||||
if(limit <= 0 || find.size() == 0) return *this;
|
||||
|
||||
const char* p = source.data();
|
||||
signed size = source.size();
|
||||
|
@ -13,10 +14,10 @@ template<bool Insensitive, bool Quoted> auto _split(lstring& self, rstring sourc
|
|||
|
||||
for(signed n = 0, quoted = 0; n <= size - (signed)find.size();) {
|
||||
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
|
||||
if(_compare<Insensitive>(p + n, size - n, find.data(), find.size())) { n++; continue; }
|
||||
if(string::_compare<Insensitive>(p + n, size - n, find.data(), find.size())) { n++; continue; }
|
||||
if(matches >= limit) break;
|
||||
|
||||
string& s = self(matches);
|
||||
string& s = operator()(matches);
|
||||
s.resize(n - base);
|
||||
memory::copy(s.get(), p + base, n - base);
|
||||
|
||||
|
@ -25,22 +26,17 @@ template<bool Insensitive, bool Quoted> auto _split(lstring& self, rstring sourc
|
|||
matches++;
|
||||
}
|
||||
|
||||
string& s = self(matches);
|
||||
string& s = operator()(matches);
|
||||
s.resize(size - base);
|
||||
memory::copy(s.get(), p + base, size - base);
|
||||
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto split(string& self, rstring on, long limit) -> lstring { return lstring().split(self, on, limit); }
|
||||
auto isplit(string& self, rstring on, long limit) -> lstring { return lstring().isplit(self, on, limit); }
|
||||
auto qsplit(string& self, rstring on, long limit) -> lstring { return lstring().qsplit(self, on, limit); }
|
||||
auto iqsplit(string& self, rstring on, long limit) -> lstring { return lstring().iqsplit(self, on, limit); }
|
||||
|
||||
auto string::split(rstring on, long limit) const -> lstring { return lstring().split(*this, on, limit); }
|
||||
auto string::isplit(rstring on, long limit) const -> lstring { return lstring().isplit(*this, on, limit); }
|
||||
auto string::qsplit(rstring on, long limit) const -> lstring { return lstring().qsplit(*this, on, limit); }
|
||||
auto string::iqsplit(rstring on, long limit) const -> lstring { return lstring().iqsplit(*this, on, limit); }
|
||||
auto string::split(rstring on, long limit) const -> lstring { return lstring()._split<0, 0>(*this, on, limit); }
|
||||
auto string::isplit(rstring on, long limit) const -> lstring { return lstring()._split<1, 0>(*this, on, limit); }
|
||||
auto string::qsplit(rstring on, long limit) const -> lstring { return lstring()._split<0, 1>(*this, on, limit); }
|
||||
auto string::iqsplit(rstring on, long limit) const -> lstring { return lstring()._split<1, 1>(*this, on, limit); }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ private:
|
|||
};
|
||||
|
||||
auto CML::parse(const string& filename) -> string {
|
||||
if(!settings.path) settings.path = filename.pathname();
|
||||
if(!settings.path) settings.path = pathname(filename);
|
||||
string document = settings.reader ? settings.reader(filename) : string::read(filename);
|
||||
parseDocument(document, settings.path, 0);
|
||||
return state.output;
|
||||
|
@ -61,7 +61,7 @@ auto CML::parseDocument(const string& filedata, const string& pathname, unsigned
|
|||
name.ltrim("include ", 1L);
|
||||
string filename{pathname, name};
|
||||
string document = settings.reader ? settings.reader(filename) : string::read(filename);
|
||||
parseDocument(document, filename.pathname(), depth + 1);
|
||||
parseDocument(document, nall::pathname(filename), depth + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -80,10 +80,10 @@ auto CML::parseDocument(const string& filedata, const string& pathname, unsigned
|
|||
while(auto offset = value.find("var(")) {
|
||||
bool found = false;
|
||||
if(auto length = value.findFrom(*offset, ")")) {
|
||||
string name = value.slice(*offset + 4, *length - 4);
|
||||
string name = slice(value, *offset + 4, *length - 4);
|
||||
for(auto& variable : variables) {
|
||||
if(variable.name == name) {
|
||||
value = {value.slice(0, *offset), variable.value, value.slice(*offset + *length + 1)};
|
||||
value = {slice(value, 0, *offset), variable.value, slice(value, *offset + *length + 1)};
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ auto DML::parse(const string& filedata, const string& pathname) -> string {
|
|||
}
|
||||
|
||||
auto DML::parse(const string& filename) -> string {
|
||||
if(!settings.path) settings.path = filename.pathname();
|
||||
if(!settings.path) settings.path = pathname(filename);
|
||||
string document = settings.reader ? settings.reader(filename) : string::read(filename);
|
||||
parseDocument(document, settings.path, 0);
|
||||
return state.output;
|
||||
|
@ -68,7 +68,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
if(block.beginsWith("<include ") && block.endsWith(">")) {
|
||||
string filename{pathname, block.trim("<include ", ">", 1L).strip()};
|
||||
string document = settings.reader ? settings.reader(filename) : string::read(filename);
|
||||
parseDocument(document, filename.pathname(), depth + 1);
|
||||
parseDocument(document, nall::pathname(filename), depth + 1);
|
||||
}
|
||||
|
||||
//html
|
||||
|
@ -87,7 +87,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
}
|
||||
auto content = lines.takeFirst().ltrim("# ", 1L).split(" => ", 1L);
|
||||
auto data = markup(content[0]);
|
||||
auto name = escape(content(1, data.crc32()));
|
||||
auto name = escape(content(1, crc32(data)));
|
||||
state.output.append("<header id=\"", name, "\">", data);
|
||||
for(auto& line : lines) {
|
||||
if(!line.beginsWith("# ")) continue;
|
||||
|
@ -98,14 +98,14 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
|
||||
//header
|
||||
else if(auto depth = count(block, '=')) {
|
||||
auto content = lines.takeFirst().slice(depth + 1).split(" => ", 1L);
|
||||
auto content = slice(lines.takeFirst(), depth + 1).split(" => ", 1L);
|
||||
auto data = markup(content[0]);
|
||||
auto name = escape(content(1, data.crc32()));
|
||||
auto name = escape(content(1, crc32(data)));
|
||||
if(depth <= 6) {
|
||||
state.output.append("<h", depth, " id=\"", name, "\">", data);
|
||||
for(auto& line : lines) {
|
||||
if(count(line, '=') != depth) continue;
|
||||
state.output.append("<span>", line.slice(depth + 1), "</span>");
|
||||
state.output.append("<span>", slice(line, depth + 1), "</span>");
|
||||
}
|
||||
state.output.append("</h", depth, ">\n");
|
||||
}
|
||||
|
@ -119,9 +119,9 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
if(auto depth = count(line, '-')) {
|
||||
while(level < depth) level++, state.output.append("<ul>\n");
|
||||
while(level > depth) level--, state.output.append("</ul>\n");
|
||||
auto content = line.slice(depth + 1).split(" => ", 1L);
|
||||
auto content = slice(line, depth + 1).split(" => ", 1L);
|
||||
auto data = markup(content[0]);
|
||||
auto name = escape(content(1, data.crc32()));
|
||||
auto name = escape(content(1, crc32(data)));
|
||||
state.output.append("<li><a href=\"#", name, "\">", data, "</a></li>\n");
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
if(auto depth = count(line, '*')) {
|
||||
while(level < depth) level++, state.output.append("<ul>\n");
|
||||
while(level > depth) level--, state.output.append("</ul>\n");
|
||||
auto data = markup(line.slice(depth + 1));
|
||||
auto data = markup(slice(line, depth + 1));
|
||||
state.output.append("<li>", data, "</li>\n");
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
if(auto depth = count(line, '>')) {
|
||||
while(level < depth) level++, state.output.append("<blockquote>\n");
|
||||
while(level > depth) level--, state.output.append("</blockquote>\n");
|
||||
auto data = markup(line.slice(depth + 1));
|
||||
auto data = markup(slice(line, depth + 1));
|
||||
state.output.append(data, "\n");
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ auto DML::markup(const string& text) -> string {
|
|||
|
||||
if(match && b == match && !f) {
|
||||
match = 0;
|
||||
auto content = text.slice(offset, n - offset - 1);
|
||||
auto content = slice(text, offset, n - offset - 1);
|
||||
if(b == '*') { output.append("<strong>", escape(content), "</strong>"); continue; }
|
||||
if(b == '/') { output.append("<em>", escape(content), "</em>"); continue; }
|
||||
if(b == '_') { output.append("<ins>", escape(content), "</ins>"); continue; }
|
||||
|
|
|
@ -2,101 +2,101 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
auto trim(string& self, rstring lhs, rstring rhs, long limit) -> string& {
|
||||
rtrim(self, rhs, limit);
|
||||
ltrim(self, lhs, limit);
|
||||
return self;
|
||||
auto string::trim(rstring lhs, rstring rhs, long limit) -> string& {
|
||||
rtrim(rhs, limit);
|
||||
ltrim(lhs, limit);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ltrim(string& self, rstring lhs, long limit) -> string& {
|
||||
if(lhs.size() == 0) return self;
|
||||
auto string::ltrim(rstring lhs, long limit) -> string& {
|
||||
if(lhs.size() == 0) return *this;
|
||||
long matches = 0;
|
||||
while(matches < limit) {
|
||||
signed offset = lhs.size() * matches;
|
||||
signed size = (signed)self.size() - offset;
|
||||
if(size < (signed)lhs.size()) break;
|
||||
if(memory::compare(self.data() + offset, lhs.data(), lhs.size()) != 0) break;
|
||||
signed length = (signed)size() - offset;
|
||||
if(length < (signed)lhs.size()) break;
|
||||
if(memory::compare(data() + offset, lhs.data(), lhs.size()) != 0) break;
|
||||
matches++;
|
||||
}
|
||||
if(matches) self.remove(0, lhs.size() * matches);
|
||||
return self;
|
||||
if(matches) remove(0, lhs.size() * matches);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto rtrim(string& self, rstring rhs, long limit) -> string& {
|
||||
if(rhs.size() == 0) return self;
|
||||
auto string::rtrim(rstring rhs, long limit) -> string& {
|
||||
if(rhs.size() == 0) return *this;
|
||||
long matches = 0;
|
||||
while(matches < limit) {
|
||||
signed offset = (signed)self.size() - rhs.size() * (matches + 1);
|
||||
signed size = (signed)self.size() - offset;
|
||||
if(offset < 0 || size < (signed)rhs.size()) break;
|
||||
if(memory::compare(self.data() + offset, rhs.data(), rhs.size()) != 0) break;
|
||||
signed offset = (signed)size() - rhs.size() * (matches + 1);
|
||||
signed length = (signed)size() - offset;
|
||||
if(offset < 0 || length < (signed)rhs.size()) break;
|
||||
if(memory::compare(data() + offset, rhs.data(), rhs.size()) != 0) break;
|
||||
matches++;
|
||||
}
|
||||
if(matches) self.resize(self.size() - rhs.size() * matches);
|
||||
return self;
|
||||
if(matches) resize(size() - rhs.size() * matches);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto itrim(string& self, rstring lhs, rstring rhs, long limit) -> string& {
|
||||
irtrim(self, rhs, limit);
|
||||
iltrim(self, lhs, limit);
|
||||
return self;
|
||||
auto string::itrim(rstring lhs, rstring rhs, long limit) -> string& {
|
||||
irtrim(rhs, limit);
|
||||
iltrim(lhs, limit);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto iltrim(string& self, rstring lhs, long limit) -> string& {
|
||||
if(lhs.size() == 0) return self;
|
||||
auto string::iltrim(rstring lhs, long limit) -> string& {
|
||||
if(lhs.size() == 0) return *this;
|
||||
long matches = 0;
|
||||
while(matches < limit) {
|
||||
signed offset = lhs.size() * matches;
|
||||
signed size = (signed)self.size() - offset;
|
||||
if(size < (signed)lhs.size()) break;
|
||||
if(memory::icompare(self.data() + offset, lhs.data(), lhs.size()) != 0) break;
|
||||
signed length = (signed)size() - offset;
|
||||
if(length < (signed)lhs.size()) break;
|
||||
if(memory::icompare(data() + offset, lhs.data(), lhs.size()) != 0) break;
|
||||
matches++;
|
||||
}
|
||||
if(matches) self.remove(0, lhs.size() * matches);
|
||||
return self;
|
||||
if(matches) remove(0, lhs.size() * matches);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto irtrim(string& self, rstring rhs, long limit) -> string& {
|
||||
if(rhs.size() == 0) return self;
|
||||
auto string::irtrim(rstring rhs, long limit) -> string& {
|
||||
if(rhs.size() == 0) return *this;
|
||||
long matches = 0;
|
||||
while(matches < limit) {
|
||||
signed offset = (signed)self.size() - rhs.size() * (matches + 1);
|
||||
signed size = (signed)self.size() - offset;
|
||||
if(offset < 0 || size < (signed)rhs.size()) break;
|
||||
if(memory::icompare(self.data() + offset, rhs.data(), rhs.size()) != 0) break;
|
||||
signed offset = (signed)size() - rhs.size() * (matches + 1);
|
||||
signed length = (signed)size() - offset;
|
||||
if(offset < 0 || length < (signed)rhs.size()) break;
|
||||
if(memory::icompare(data() + offset, rhs.data(), rhs.size()) != 0) break;
|
||||
matches++;
|
||||
}
|
||||
if(matches) self.resize(self.size() - rhs.size() * matches);
|
||||
return self;
|
||||
if(matches) resize(size() - rhs.size() * matches);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto strip(string& self) -> string& {
|
||||
rstrip(self);
|
||||
lstrip(self);
|
||||
return self;
|
||||
auto string::strip() -> string& {
|
||||
rstrip();
|
||||
lstrip();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto lstrip(string& self) -> string& {
|
||||
unsigned size = 0;
|
||||
while(size < self.size()) {
|
||||
char input = self[size];
|
||||
auto string::lstrip() -> string& {
|
||||
unsigned length = 0;
|
||||
while(length < size()) {
|
||||
char input = operator[](length);
|
||||
if(input != ' ' && input != '\t' && input != '\r' && input != '\n') break;
|
||||
size++;
|
||||
length++;
|
||||
}
|
||||
if(size) self.remove(0, size);
|
||||
return self;
|
||||
if(length) remove(0, length);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto rstrip(string& self) -> string& {
|
||||
unsigned size = 0;
|
||||
while(size < self.size()) {
|
||||
auto string::rstrip() -> string& {
|
||||
unsigned length = 0;
|
||||
while(length < size()) {
|
||||
bool matched = false;
|
||||
char input = self[self.size() - size - 1];
|
||||
char input = operator[](size() - length - 1);
|
||||
if(input != ' ' && input != '\t' && input != '\r' && input != '\n') break;
|
||||
size++;
|
||||
length++;
|
||||
}
|
||||
if(size) self.resize(self.size() - size);
|
||||
return self;
|
||||
if(length) resize(size() - length);
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
auto string::read(const string& filename) -> string {
|
||||
auto string::read(rstring filename) -> string {
|
||||
#if !defined(_WIN32)
|
||||
FILE* fp = fopen(filename, "rb");
|
||||
#else
|
||||
|
@ -22,67 +22,67 @@ auto string::read(const string& filename) -> string {
|
|||
return fclose(fp), result;
|
||||
}
|
||||
|
||||
auto string::repeat(const string& pattern, unsigned times) -> string {
|
||||
auto string::repeat(rstring pattern, unsigned times) -> string {
|
||||
string result;
|
||||
while(times--) result.append(pattern);
|
||||
while(times--) result.append(pattern.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
auto fill(string& self, char fill) -> string& {
|
||||
memory::fill(self.get(), self.size(), fill);
|
||||
return self;
|
||||
auto string::fill(char fill) -> string& {
|
||||
memory::fill(get(), size(), fill);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto hash(const string& self) -> unsigned {
|
||||
const char* p = self.data();
|
||||
unsigned size = self.size();
|
||||
auto string::hash() const -> unsigned {
|
||||
const char* p = data();
|
||||
unsigned length = size();
|
||||
unsigned result = 5381;
|
||||
while(size--) result = (result << 5) + result + *p++;
|
||||
while(length--) result = (result << 5) + result + *p++;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto remove(string& self, unsigned offset, unsigned length) -> string& {
|
||||
char* p = self.get();
|
||||
length = min(length, self.size());
|
||||
memory::move(p + offset, p + offset + length, self.size() - length);
|
||||
return self.resize(self.size() - length);
|
||||
auto string::remove(unsigned offset, unsigned length) -> string& {
|
||||
char* p = get();
|
||||
length = min(length, size());
|
||||
memory::move(p + offset, p + offset + length, size() - length);
|
||||
return resize(size() - length);
|
||||
}
|
||||
|
||||
auto reverse(string& self) -> string& {
|
||||
char* p = self.get();
|
||||
unsigned size = self.size();
|
||||
unsigned pivot = size >> 1;
|
||||
for(signed x = 0, y = size - 1; x < pivot && y >= 0; x++, y--) std::swap(p[x], p[y]);
|
||||
return self;
|
||||
auto string::reverse() -> string& {
|
||||
char* p = get();
|
||||
unsigned length = size();
|
||||
unsigned pivot = length >> 1;
|
||||
for(signed x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(p[x], p[y]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//+length => insert/delete from start (right justify)
|
||||
//-length => insert/delete from end (left justify)
|
||||
auto size(string& self, signed length, char fill) -> string& {
|
||||
unsigned size = self.size();
|
||||
if(size == length) return self;
|
||||
auto string::size(signed length, char fill) -> string& {
|
||||
unsigned size = this->size();
|
||||
if(size == length) return *this;
|
||||
|
||||
bool right = length >= 0;
|
||||
length = abs(length);
|
||||
|
||||
if(size < length) { //expand
|
||||
self.resize(length);
|
||||
char* p = self.get();
|
||||
resize(length);
|
||||
char* p = get();
|
||||
unsigned displacement = length - size;
|
||||
if(right) memory::move(p + displacement, p, size);
|
||||
else p += size;
|
||||
while(displacement--) *p++ = fill;
|
||||
} else { //shrink
|
||||
char* p = self.get();
|
||||
char* p = get();
|
||||
unsigned displacement = size - length;
|
||||
if(right) memory::move(p, p + displacement, length);
|
||||
self.resize(length);
|
||||
resize(length);
|
||||
}
|
||||
|
||||
return self;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto slice(const string& self, signed offset, signed length) -> string {
|
||||
auto slice(rstring self, signed offset, signed length) -> string {
|
||||
string result;
|
||||
if(offset < self.size()) {
|
||||
if(length < 0) length = self.size() - offset;
|
||||
|
@ -92,15 +92,6 @@ auto slice(const string& self, signed offset, signed length) -> string {
|
|||
return result;
|
||||
}
|
||||
|
||||
//legacy function: required for some library functions, do not use in newly written code
|
||||
auto substr(rstring source, signed offset, signed length) -> string {
|
||||
string result;
|
||||
if(length < 0) length = source.size() - offset;
|
||||
result.resize(length);
|
||||
memory::copy(result.get(), source.data() + offset, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto integer(char* result, intmax_t value) -> char* {
|
||||
bool negative = value < 0;
|
||||
if(negative) value = -value;
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct string_view {
|
||||
string_view() {
|
||||
_string = nullptr;
|
||||
_data = "";
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
string_view(const char* data) {
|
||||
_string = nullptr;
|
||||
_data = data;
|
||||
_size = -1; //defer length calculation, as it is often unnecessary
|
||||
}
|
||||
|
||||
string_view(const char* data, unsigned size) {
|
||||
_string = nullptr;
|
||||
_data = data;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
string_view(const string& source) {
|
||||
_string = nullptr;
|
||||
_data = source.data();
|
||||
_size = source.size();
|
||||
}
|
||||
|
||||
template<typename... P>
|
||||
string_view(P&&... p) {
|
||||
_string = new string{forward<P>(p)...};
|
||||
_data = _string->data();
|
||||
_size = _string->size();
|
||||
}
|
||||
|
||||
~string_view() {
|
||||
if(_string) delete _string;
|
||||
}
|
||||
|
||||
string_view(const string_view& source) {
|
||||
_string = nullptr;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
}
|
||||
|
||||
string_view(string_view&& source) {
|
||||
_string = source._string;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
source._string = nullptr;
|
||||
}
|
||||
|
||||
auto operator=(const string_view& source) -> string_view& {
|
||||
_string = nullptr;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
return *this;
|
||||
};
|
||||
|
||||
auto operator=(string_view&& source) -> string_view& {
|
||||
_string = source._string;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
source._string = nullptr;
|
||||
return *this;
|
||||
};
|
||||
|
||||
operator const char*() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
auto data() const -> const char* {
|
||||
return _data;
|
||||
}
|
||||
|
||||
auto size() const -> unsigned {
|
||||
if(_size < 0) _size = strlen(_data);
|
||||
return _size;
|
||||
}
|
||||
|
||||
protected:
|
||||
string* _string;
|
||||
const char* _data;
|
||||
mutable signed _size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,23 +5,23 @@
|
|||
#include <utility>
|
||||
|
||||
namespace nall {
|
||||
using std::nullptr_t;
|
||||
using std::forward;
|
||||
using std::move;
|
||||
using std::add_const;
|
||||
using std::decay;
|
||||
using std::declval;
|
||||
|
||||
using true_type = std::true_type;
|
||||
using false_type = std::false_type;
|
||||
|
||||
template<typename T, typename U> using is_same = std::is_same<T, U>;
|
||||
template<typename T, typename U> using is_base_of = std::is_base_of<T, U>;
|
||||
template<typename T> using is_array = std::is_array<T>;
|
||||
template<typename T> using is_function = std::is_function<T>;
|
||||
template<typename T> using is_integral = std::is_integral<T>;
|
||||
template<typename T> using add_const = std::add_const<T>;
|
||||
template<typename T> using remove_extent = std::remove_extent<T>;
|
||||
template<typename T> using remove_reference = std::remove_reference<T>;
|
||||
using std::false_type;
|
||||
using std::forward;
|
||||
using std::initializer_list;
|
||||
using std::is_array;
|
||||
using std::is_base_of;
|
||||
using std::is_function;
|
||||
using std::is_integral;
|
||||
using std::is_same;
|
||||
using std::move;
|
||||
using std::nullptr_t;
|
||||
using std::remove_extent;
|
||||
using std::remove_reference;
|
||||
using std::swap;
|
||||
using std::true_type;
|
||||
}
|
||||
|
||||
namespace nall {
|
||||
|
|
|
@ -25,7 +25,7 @@ template<typename T> struct vector {
|
|||
auto size() const -> unsigned { return objectsize; }
|
||||
auto capacity() const -> unsigned { return poolsize; }
|
||||
|
||||
auto move() -> T* {
|
||||
auto release() -> T* {
|
||||
T* result = pool + poolbase;
|
||||
pool = nullptr;
|
||||
poolbase = 0;
|
||||
|
@ -50,7 +50,7 @@ template<typename T> struct vector {
|
|||
size = bit::round(size); //amortize growth
|
||||
|
||||
T* copy = (T*)memory::allocate(size * sizeof(T));
|
||||
for(unsigned n = 0; n < objectsize; n++) new(copy + n) T(std::move(pool[poolbase + n]));
|
||||
for(unsigned n = 0; n < objectsize; n++) new(copy + n) T(move(pool[poolbase + n]));
|
||||
free(pool);
|
||||
pool = copy;
|
||||
poolbase = 0;
|
||||
|
@ -59,7 +59,7 @@ template<typename T> struct vector {
|
|||
|
||||
auto resize(unsigned size, T value = T()) -> void {
|
||||
T* copy = (T*)memory::allocate(size * sizeof(T));
|
||||
for(unsigned n = 0; n < size && n < objectsize; n++) new(copy + n) T(std::move(pool[poolbase + n]));
|
||||
for(unsigned n = 0; n < size && n < objectsize; n++) new(copy + n) T(move(pool[poolbase + n]));
|
||||
for(unsigned n = objectsize; n < size; n++) new(copy + n) T(value);
|
||||
reset();
|
||||
pool = copy;
|
||||
|
@ -84,7 +84,7 @@ template<typename T> struct vector {
|
|||
unsigned available = poolsize - objectsize;
|
||||
poolbase = max(1u, available >> 1);
|
||||
for(signed n = objectsize - 1; n >= 0; n--) {
|
||||
pool[poolbase + n] = std::move(pool[n]);
|
||||
pool[poolbase + n] = move(pool[n]);
|
||||
}
|
||||
}
|
||||
new(pool + --poolbase) T(data);
|
||||
|
@ -116,7 +116,7 @@ template<typename T> struct vector {
|
|||
append(data);
|
||||
if(position == ~0u) return;
|
||||
for(signed n = objectsize - 1; n > position; n--) {
|
||||
pool[poolbase + n] = std::move(pool[poolbase + n - 1]);
|
||||
pool[poolbase + n] = move(pool[poolbase + n - 1]);
|
||||
}
|
||||
pool[poolbase + position] = data;
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ template<typename T> struct vector {
|
|||
} else {
|
||||
for(unsigned n = position; n < objectsize; n++) {
|
||||
if(n + length < objectsize) {
|
||||
pool[poolbase + n] = std::move(pool[poolbase + n + length]);
|
||||
pool[poolbase + n] = move(pool[poolbase + n + length]);
|
||||
} else {
|
||||
pool[poolbase + n].~T();
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ template<typename T> struct vector {
|
|||
auto reverse() -> void {
|
||||
unsigned pivot = size() / 2;
|
||||
for(unsigned l = 0, r = size() - 1; l < pivot; l++, r--) {
|
||||
std::swap(pool[poolbase + l], pool[poolbase + r]);
|
||||
swap(pool[poolbase + l], pool[poolbase + r]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,9 +270,9 @@ template<typename T> struct vector {
|
|||
|
||||
//construction and destruction
|
||||
vector() = default;
|
||||
vector(std::initializer_list<T> list) { for(auto& data : list) append(data); }
|
||||
vector(initializer_list<T> list) { for(auto& data : list) append(data); }
|
||||
vector(const vector& source) { operator=(source); }
|
||||
vector(vector&& source) { operator=(std::move(source)); }
|
||||
vector(vector&& source) { operator=(move(source)); }
|
||||
~vector() { reset(); }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -101,11 +101,14 @@ auto Cartridge::parseMarkupMCC(Markup::Node root) -> void {
|
|||
hasSatellaviewSlot = true;
|
||||
hasMCC = true;
|
||||
|
||||
interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs");
|
||||
interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs", false);
|
||||
|
||||
parseMarkupMemory(mcc.rom, root["rom"], ID::MCCROM, false);
|
||||
parseMarkupMemory(mcc.ram, root["ram"], ID::MCCRAM, true);
|
||||
parseMarkupMemory(mcc.psram, root["psram"], ID::MCCPSRAM, true);
|
||||
auto rom = root.find("rom");
|
||||
auto ram = root.find("ram");
|
||||
|
||||
parseMarkupMemory(mcc.rom, rom(0), ID::MCCROM, false);
|
||||
parseMarkupMemory(mcc.ram, ram(0), ID::MCCRAM, true);
|
||||
parseMarkupMemory(mcc.psram, ram(1), ID::MCCPSRAM, true);
|
||||
|
||||
for(auto node : root.find("map")) {
|
||||
if(node["id"].text() == "rom"
|
||||
|
@ -126,7 +129,7 @@ auto Cartridge::parseMarkupMCC(Markup::Node root) -> void {
|
|||
auto Cartridge::parseMarkupSatellaview(Markup::Node root) -> void {
|
||||
hasSatellaviewSlot = true;
|
||||
|
||||
interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs");
|
||||
interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs", false);
|
||||
|
||||
for(auto node : root.find("map")) {
|
||||
if(node["id"].text() == "rom") {
|
||||
|
@ -144,7 +147,7 @@ auto Cartridge::parseMarkupSufamiTurbo(Markup::Node root, bool slot) -> void {
|
|||
|
||||
if(slot == 0) {
|
||||
//load required slot A (will request slot B if slot A cartridge is linkable)
|
||||
interface->loadRequest(ID::SufamiTurboSlotA, "Sufami Turbo - Slot A", "st");
|
||||
interface->loadRequest(ID::SufamiTurboSlotA, "Sufami Turbo - Slot A", "st", false);
|
||||
}
|
||||
|
||||
for(auto node : root.find("map")) {
|
||||
|
|
|
@ -36,8 +36,8 @@ auto Program::loadRequest(unsigned id, string filename, bool required) -> void {
|
|||
}
|
||||
}
|
||||
if(required) MessageDialog().setTitle("higan").setText({
|
||||
"Missing required file: ", location.filename(), "\n\n",
|
||||
"From location:\n", location.pathname()
|
||||
"Missing required file: ", nall::filename(location), "\n\n",
|
||||
"From location:\n", nall::pathname(location)
|
||||
}).error();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ auto Program::saveState(unsigned slot, bool manager) -> bool {
|
|||
auto location = stateName(slot, manager);
|
||||
serializer s = emulator->serialize();
|
||||
if(s.size() == 0) return showMessage({"Failed to save state to slot ", slot}), false;
|
||||
directory::create(location.pathname());
|
||||
directory::create(pathname(location));
|
||||
if(file::write(location, s.data(), s.size()) == false) {
|
||||
return showMessage({"Unable to write to slot ", slot}), false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue