From 39ca8a2fabb450faea03a5cc130c7bb110d91860 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 2 May 2015 23:05:46 +1000 Subject: [PATCH] Update to v094r17 release. byuu says: This updates higan to use the new Markup::Node changes. This is a really big change, and one slight typo anywhere could break certain classes of games from playing. I don't have ananke hooked up again yet, so I don't have the ability to test this much. If anyone with some v094 game folders wouldn't mind testing, I'd help out a great deal. I'm most concerned about testing one of each SNES special chip game. Most notably, systems like the SA-1, HitachiDSP and NEC-DSP were using the fancier lookups, eg node["rom[0]/name"], which I had to convert to a rather ugly node["rom"].at(0)["name"], which I'm fairly confident won't work. I'm going to blame that on the fumes from the shelves I just stained >.> Might work with node.find("rom[0]/name")(0) though ...? But so ugly ... ugh. That aside, this WIP adds the accuracy-PPU inlining, so the accuracy profile should run around 7.5% faster than before. --- emulator/emulator.hpp | 2 +- fc/cartridge/board/board.cpp | 26 +-- fc/cartridge/board/konami-vrc2.cpp | 4 +- fc/cartridge/board/konami-vrc3.cpp | 2 +- fc/cartridge/board/konami-vrc4.cpp | 4 +- fc/cartridge/board/nes-bnrom.cpp | 2 +- fc/cartridge/board/nes-cnrom.cpp | 2 +- fc/cartridge/board/nes-gxrom.cpp | 2 +- fc/cartridge/board/nes-nrom.cpp | 2 +- fc/cartridge/board/nes-uxrom.cpp | 2 +- fc/system/system.cpp | 2 +- gb/cartridge/cartridge.cpp | 18 +- gb/system/system.cpp | 7 +- gba/cartridge/cartridge.cpp | 36 ++-- gba/system/system.cpp | 7 +- nall/base64.hpp | 4 +- nall/config.hpp | 10 +- nall/decode/url.hpp | 36 ++++ nall/file.hpp | 68 +++---- nall/http/request.hpp | 21 +- nall/http/role.hpp | 8 +- nall/nall.hpp | 3 +- nall/string.hpp | 3 +- nall/string/base.hpp | 5 +- nall/string/markup/bml.hpp | 104 ++++++---- nall/string/markup/document.hpp | 14 -- nall/string/markup/find.hpp | 135 +++++++++++++ nall/string/markup/node.hpp | 244 +++++++++++------------ nall/string/markup/xml.hpp | 80 ++++---- nall/traits.hpp | 1 + nall/vector.hpp | 10 +- ruby/video/opengl/main.hpp | 24 +-- ruby/video/opengl/program.hpp | 6 +- sfc/cartridge/cartridge.cpp | 50 ++--- sfc/cartridge/markup.cpp | 257 +++++++++++-------------- sfc/chip/msu1/msu1.cpp | 12 +- sfc/chip/sa1/sa1.cpp | 2 +- sfc/ppu/background/background.hpp | 6 +- sfc/ppu/mmio/mmio.hpp | 14 +- sfc/ppu/ppu.hpp | 2 +- sfc/ppu/screen/screen.hpp | 8 +- sfc/ppu/sprite/sprite.hpp | 8 +- sfc/system/system.cpp | 7 +- target-tomoko/tools/cheat-database.cpp | 8 +- target-tomoko/tools/cheat-editor.cpp | 7 +- 45 files changed, 709 insertions(+), 566 deletions(-) create mode 100644 nall/decode/url.hpp delete mode 100644 nall/string/markup/document.hpp create mode 100644 nall/string/markup/find.hpp diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 84f71df4..6c26489b 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "094.16"; + static const char Version[] = "094.17"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/fc/cartridge/board/board.cpp b/fc/cartridge/board/board.cpp index 383ebc88..4f6afe02 100644 --- a/fc/cartridge/board/board.cpp +++ b/fc/cartridge/board/board.cpp @@ -86,31 +86,31 @@ Board::Board(Markup::Node& document) { cartridge.board = this; auto cartridge = document["cartridge"]; - information.type = cartridge["board/type"].data; - information.battery = cartridge["prg/ram/name"].exists(); + information.type = cartridge["board/type"].text(); + information.battery = (bool)cartridge["prg/ram/name"]; auto prom = cartridge["prg/rom"]; auto pram = cartridge["prg/ram"]; auto crom = cartridge["chr/rom"]; auto cram = cartridge["chr/ram"]; - prgrom.size = numeral(prom["size"].data); - prgram.size = numeral(pram["size"].data); - chrrom.size = numeral(crom["size"].data); - chrram.size = numeral(cram["size"].data); + prgrom.size = prom["size"].text().numeral(); + prgram.size = pram["size"].text().numeral(); + chrrom.size = crom["size"].text().numeral(); + chrram.size = cram["size"].text().numeral(); if(prgrom.size) prgrom.data = new uint8[prgrom.size](); if(prgram.size) prgram.data = new uint8[prgram.size](); if(chrrom.size) chrrom.data = new uint8[chrrom.size](); if(chrram.size) chrram.data = new uint8[chrram.size](); - if(prom["name"].data) interface->loadRequest(ID::ProgramROM, prom["name"].data); - if(pram["name"].data) interface->loadRequest(ID::ProgramRAM, pram["name"].data); - if(crom["name"].data) interface->loadRequest(ID::CharacterROM, crom["name"].data); - if(cram["name"].data) interface->loadRequest(ID::CharacterRAM, cram["name"].data); + if(auto name = prom["name"].text()) interface->loadRequest(ID::ProgramROM, name); + if(auto name = pram["name"].text()) interface->loadRequest(ID::ProgramRAM, name); + if(auto name = crom["name"].text()) interface->loadRequest(ID::CharacterROM, name); + if(auto name = cram["name"].text()) interface->loadRequest(ID::CharacterRAM, name); - if(pram["name"].data) Famicom::cartridge.memory.append({ID::ProgramRAM, pram["name"].data}); - if(cram["name"].data) Famicom::cartridge.memory.append({ID::CharacterRAM, cram["name"].data}); + if(auto name = pram["name"].text()) Famicom::cartridge.memory.append({ID::ProgramRAM, name}); + if(auto name = cram["name"].text()) Famicom::cartridge.memory.append({ID::CharacterRAM, name}); prgram.writable = true; chrram.writable = true; @@ -120,7 +120,7 @@ Board::~Board() { } Board* Board::load(string manifest) { - auto document = Markup::Document(manifest); + auto document = BML::unserialize(manifest); cartridge.information.title = document["information/title"].text(); string type = document["cartridge/board/type"].text(); diff --git a/fc/cartridge/board/konami-vrc2.cpp b/fc/cartridge/board/konami-vrc2.cpp index 826ac31f..8a98947f 100644 --- a/fc/cartridge/board/konami-vrc2.cpp +++ b/fc/cartridge/board/konami-vrc2.cpp @@ -50,8 +50,8 @@ void serialize(serializer& s) { } KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) { - settings.pinout.a0 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a0"].data); - settings.pinout.a1 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a1"].data); + settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].decimal(); + settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].decimal(); } }; diff --git a/fc/cartridge/board/konami-vrc3.cpp b/fc/cartridge/board/konami-vrc3.cpp index accda1ad..11685a43 100644 --- a/fc/cartridge/board/konami-vrc3.cpp +++ b/fc/cartridge/board/konami-vrc3.cpp @@ -51,7 +51,7 @@ void serialize(serializer& s) { } KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) { - settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0; + settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; } }; diff --git a/fc/cartridge/board/konami-vrc4.cpp b/fc/cartridge/board/konami-vrc4.cpp index a9988afe..42bba609 100644 --- a/fc/cartridge/board/konami-vrc4.cpp +++ b/fc/cartridge/board/konami-vrc4.cpp @@ -54,8 +54,8 @@ void serialize(serializer& s) { } KonamiVRC4(Markup::Node& document) : Board(document), vrc4(*this) { - settings.pinout.a0 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a0"].data); - settings.pinout.a1 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a1"].data); + settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].decimal(); + settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].decimal(); } }; diff --git a/fc/cartridge/board/nes-bnrom.cpp b/fc/cartridge/board/nes-bnrom.cpp index 41723c13..c6790b63 100644 --- a/fc/cartridge/board/nes-bnrom.cpp +++ b/fc/cartridge/board/nes-bnrom.cpp @@ -46,7 +46,7 @@ void serialize(serializer& s) { } NES_BNROM(Markup::Node& document) : Board(document) { - settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0; + settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; } }; diff --git a/fc/cartridge/board/nes-cnrom.cpp b/fc/cartridge/board/nes-cnrom.cpp index aafc7bda..fac9b016 100644 --- a/fc/cartridge/board/nes-cnrom.cpp +++ b/fc/cartridge/board/nes-cnrom.cpp @@ -48,7 +48,7 @@ void serialize(serializer& s) { } NES_CNROM(Markup::Node& document) : Board(document) { - settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0; + settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; } }; diff --git a/fc/cartridge/board/nes-gxrom.cpp b/fc/cartridge/board/nes-gxrom.cpp index 36548d18..8e8d99af 100644 --- a/fc/cartridge/board/nes-gxrom.cpp +++ b/fc/cartridge/board/nes-gxrom.cpp @@ -55,7 +55,7 @@ void serialize(serializer& s) { } NES_GxROM(Markup::Node& document) : Board(document) { - settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0; + settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; } }; diff --git a/fc/cartridge/board/nes-nrom.cpp b/fc/cartridge/board/nes-nrom.cpp index 490d2094..10f4eda6 100644 --- a/fc/cartridge/board/nes-nrom.cpp +++ b/fc/cartridge/board/nes-nrom.cpp @@ -37,7 +37,7 @@ void serialize(serializer& s) { } NES_NROM(Markup::Node& document) : Board(document) { - settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0; + settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; } }; diff --git a/fc/cartridge/board/nes-uxrom.cpp b/fc/cartridge/board/nes-uxrom.cpp index 7aced28e..f006e96b 100644 --- a/fc/cartridge/board/nes-uxrom.cpp +++ b/fc/cartridge/board/nes-uxrom.cpp @@ -49,7 +49,7 @@ void serialize(serializer& s) { } NES_UxROM(Markup::Node& document) : Board(document) { - settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0; + settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; } }; diff --git a/fc/system/system.cpp b/fc/system/system.cpp index 4a86bea1..39955f4d 100644 --- a/fc/system/system.cpp +++ b/fc/system/system.cpp @@ -43,7 +43,7 @@ void System::runthreadtosave() { void System::load() { string manifest = string::read({interface->path(ID::System), "manifest.bml"}); - auto document = Markup::Document(manifest); + auto document = BML::unserialize(manifest); serialize_init(); } diff --git a/gb/cartridge/cartridge.cpp b/gb/cartridge/cartridge.cpp index 0b85220c..b9f0af7a 100644 --- a/gb/cartridge/cartridge.cpp +++ b/gb/cartridge/cartridge.cpp @@ -47,7 +47,7 @@ void Cartridge::load(System::Revision revision) { information.romsize = 0; information.ramsize = 0; - auto document = Markup::Document(information.markup); + auto document = BML::unserialize(information.markup); information.title = document["information/title"].text(); auto mapperid = document["cartridge/board/type"].text(); @@ -66,22 +66,22 @@ void Cartridge::load(System::Revision revision) { auto rom = document["cartridge/rom"]; auto ram = document["cartridge/ram"]; - romsize = numeral(rom["size"].data); + romsize = rom["size"].decimal(); romdata = allocate(romsize, 0xff); - ramsize = numeral(ram["size"].data); + ramsize = ram["size"].decimal(); ramdata = allocate(ramsize, 0xff); //Super Game Boy core loads memory from Super Famicom core if(revision != System::Revision::SuperGameBoy) { - if(rom["name"].exists()) interface->loadRequest(ID::ROM, rom["name"].data); - if(ram["name"].exists()) interface->loadRequest(ID::RAM, ram["name"].data); - if(ram["name"].exists()) memory.append({ID::RAM, ram["name"].data}); + if(rom["name"]) interface->loadRequest(ID::ROM, rom["name"].text()); + if(ram["name"]) interface->loadRequest(ID::RAM, ram["name"].text()); + if(ram["name"]) memory.append({ID::RAM, ram["name"].text()}); } - information.romsize = numeral(rom["size"].data); - information.ramsize = numeral(ram["size"].data); - information.battery = ram["name"].exists(); + information.romsize = rom["size"].decimal(); + information.ramsize = ram["size"].decimal(); + information.battery = (bool)ram["name"]; switch(information.mapper) { default: case Mapper::MBC0: mapper = &mbc0; break; diff --git a/gb/system/system.cpp b/gb/system/system.cpp index c8a23c82..f8f72344 100644 --- a/gb/system/system.cpp +++ b/gb/system/system.cpp @@ -50,13 +50,14 @@ void System::load(Revision revision) { if(revision == Revision::SuperGameBoy) return; //Super Famicom core loads boot ROM for SGB string manifest = string::read({interface->path(ID::System), "manifest.bml"}); - auto document = Markup::Document(manifest); + auto document = BML::unserialize(manifest); + auto bootROM = document["system/cpu/rom/name"].text(); interface->loadRequest( revision == Revision::GameBoy ? ID::GameBoyBootROM : ID::GameBoyColorBootROM, - document["system/cpu/rom/name"].data + bootROM ); - if(!file::exists({interface->path(ID::System), document["system/cpu/rom/name"].data})) { + if(!file::exists({interface->path(ID::System), bootROM})) { interface->notify("Error: required Game Boy firmware boot.rom not found.\n"); } } diff --git a/gba/cartridge/cartridge.cpp b/gba/cartridge/cartridge.cpp index 24f6be0f..68d6d80e 100644 --- a/gba/cartridge/cartridge.cpp +++ b/gba/cartridge/cartridge.cpp @@ -14,14 +14,14 @@ string Cartridge::title() { void Cartridge::load() { interface->loadRequest(ID::Manifest, "manifest.bml"); - auto document = Markup::Document(information.markup); + auto document = BML::unserialize(information.markup); information.title = document["information/title"].text(); unsigned rom_size = 0; - if(document["cartridge/rom"].exists()) { + if(document["cartridge/rom"]) { auto info = document["cartridge/rom"]; - interface->loadRequest(ID::ROM, info["name"].data); - rom_size = numeral(info["size"].data); + interface->loadRequest(ID::ROM, info["name"].text()); + rom_size = info["size"].decimal(); for(unsigned addr = rom_size; addr < rom.size; addr++) { rom.data[addr] = rom.data[Bus::mirror(addr, rom_size)]; } @@ -31,40 +31,40 @@ void Cartridge::load() { has_eeprom = false; has_flashrom = false; - if(document["cartridge/ram"].exists()) { + if(document["cartridge/ram"]) { auto info = document["cartridge/ram"]; - if(info["type"].data == "SRAM" || info["type"].data == "FRAM") { + if(info["type"].text() == "SRAM" || info["type"].text() == "FRAM") { has_sram = true; - ram.size = numeral(info["size"].data); + ram.size = info["size"].decimal(); ram.mask = ram.size - 1; for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff; - interface->loadRequest(ID::RAM, info["name"].data); - memory.append({ID::RAM, info["name"].data}); + interface->loadRequest(ID::RAM, info["name"].text()); + memory.append({ID::RAM, info["name"].text()}); } - if(info["type"].data == "EEPROM") { + if(info["type"].text() == "EEPROM") { has_eeprom = true; - eeprom.size = numeral(info["size"].data); + eeprom.size = info["size"].decimal(); eeprom.bits = eeprom.size <= 512 ? 6 : 14; if(eeprom.size == 0) eeprom.size = 8192, eeprom.bits = 0; //auto-detect size eeprom.mask = rom_size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000; eeprom.test = rom_size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000; for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff; - interface->loadRequest(ID::EEPROM, info["name"].data); - memory.append({ID::EEPROM, info["name"].data}); + interface->loadRequest(ID::EEPROM, info["name"].text()); + memory.append({ID::EEPROM, info["name"].text()}); } - if(info["type"].data == "FlashROM") { + if(info["type"].text() == "FlashROM") { has_flashrom = true; - flashrom.id = numeral(info["id"].data); - flashrom.size = numeral(info["size"].data); + flashrom.id = info["id"].decimal(); + flashrom.size = info["size"].decimal(); for(unsigned n = 0; n < flashrom.size; n++) flashrom.data[n] = 0xff; - interface->loadRequest(ID::FlashROM, info["name"].data); - memory.append({ID::FlashROM, info["name"].data}); + interface->loadRequest(ID::FlashROM, info["name"].text()); + memory.append({ID::FlashROM, info["name"].text()}); } } diff --git a/gba/system/system.cpp b/gba/system/system.cpp index 1e948707..ee7e2028 100644 --- a/gba/system/system.cpp +++ b/gba/system/system.cpp @@ -25,10 +25,11 @@ void System::power() { void System::load() { string manifest = string::read({interface->path(ID::System), "manifest.bml"}); - auto document = Markup::Document(manifest); + auto document = BML::unserialize(manifest); - interface->loadRequest(ID::BIOS, document["system/cpu/rom/name"].data); - if(!file::exists({interface->path(ID::System), document["system/cpu/rom/name"].data})) { + auto bios = document["system/cpu/rom/name"].text(); + interface->loadRequest(ID::BIOS, bios); + if(!file::exists({interface->path(ID::System), bios})) { interface->notify("Error: required Game Boy Advance firmware bios.rom not found.\n"); } diff --git a/nall/base64.hpp b/nall/base64.hpp index 7d9fb116..b3fb3707 100644 --- a/nall/base64.hpp +++ b/nall/base64.hpp @@ -77,7 +77,7 @@ vector Base64::decode(const string& text) { uint8_t buffer, output; for(unsigned i = 0; i < text.size(); i++) { uint8_t buffer = value(text[i]); - if(buffer == 0) break; + if(buffer > 63) break; switch(i & 3) { case 0: @@ -129,7 +129,7 @@ uint8_t Base64::value(char n) { if(n >= '0' && n <= '9') return n - '0' + 52; if(n == '+' || n == '-') return 62; if(n == '/' || n == '_') return 63; - return 0; + return 64; //error code } } diff --git a/nall/config.hpp b/nall/config.hpp index 0a7d8f55..b655b515 100644 --- a/nall/config.hpp +++ b/nall/config.hpp @@ -58,10 +58,10 @@ struct Node { void load(Markup::Node path) { for(auto& child : children) { - auto leaf = path[child.name]; - if(!leaf.exists()) continue; - if(!child.empty()) child.set(leaf.text()); - child.load(leaf); + if(auto leaf = path[child.name]) { + if(!child.empty()) child.set(leaf.text()); + child.load(leaf); + } } } @@ -84,7 +84,7 @@ struct Node { struct Document : Node { bool load(const string& filename) { if(!file::exists(filename)) return false; - auto document = Markup::Document(string::read(filename)); + auto document = BML::unserialize(string::read(filename)); Node::load(document); return true; } diff --git a/nall/decode/url.hpp b/nall/decode/url.hpp new file mode 100644 index 00000000..45df16d8 --- /dev/null +++ b/nall/decode/url.hpp @@ -0,0 +1,36 @@ +#ifndef NALL_DECODE_URL_HPP +#define NALL_DECODE_URL_HPP + +namespace nall { namespace Decode { + +//returns empty string on malformed content +inline auto URL(const string& input) -> string { + string output; + for(unsigned n = 0; n < input.size();) { + char c = input[n]; + if(c >= 'A' && c <= 'Z') { output.append(c); n++; continue; } + if(c >= 'a' && c <= 'z') { output.append(c); n++; continue; } + if(c >= '0' && c <= '9') { output.append(c); n++; continue; } + if(c == '-' || c == '_' || c == '.' || c == '~') { output.append(c); n++; continue; } + if(c == '+') { output.append(' '); n++; continue; } + if(c != '%' || n + 2 >= input.size()) return ""; + char hi = input[n + 1]; + char lo = input[n + 2]; + if(hi >= '0' && hi <= '9') hi -= '0'; + else if(hi >= 'A' && hi <= 'F') hi -= 'A' - 10; + else if(hi >= 'a' && hi <= 'f') hi -= 'a' - 10; + else return ""; + if(lo >= '0' && lo <= '9') lo -= '0'; + else if(lo >= 'A' && lo <= 'F') lo -= 'A' - 10; + else if(lo >= 'a' && lo <= 'f') lo -= 'a' - 10; + else return ""; + char byte = hi * 16 + lo; + output.append(byte); + n += 3; + } + return output; +} + +}} + +#endif diff --git a/nall/file.hpp b/nall/file.hpp index cdebda4f..f73ec4b3 100644 --- a/nall/file.hpp +++ b/nall/file.hpp @@ -16,7 +16,7 @@ struct file : storage, varint { enum class mode : unsigned { read, write, modify, append, readwrite = modify, writeread = append }; enum class index : unsigned { absolute, relative }; - static bool copy(const string& sourcename, const string& targetname) { + static auto copy(const string& sourcename, const string& targetname) -> bool { file rd, wr; if(rd.open(sourcename, mode::read) == false) return false; if(wr.open(targetname, mode::write) == false) return false; @@ -26,7 +26,7 @@ struct file : storage, varint { //attempt to rename file first //this will fail if paths point to different file systems; fall back to copy+remove in this case - static bool move(const string& sourcename, const string& targetname) { + static auto move(const string& sourcename, const string& targetname) -> bool { if(rename(sourcename, targetname)) return true; if(!writable(sourcename)) return false; if(copy(sourcename, targetname)) { @@ -36,7 +36,7 @@ struct file : storage, varint { return false; } - static bool truncate(const string& filename, unsigned size) { + static auto truncate(const string& filename, unsigned size) -> bool { #if !defined(_WIN32) return truncate(filename, size) == 0; #else @@ -51,7 +51,7 @@ struct file : storage, varint { } //specialization of storage::exists(); returns false for folders - static bool exists(const string& filename) { + static auto exists(const string& filename) -> bool { #if !defined(_WIN32) struct stat data; if(stat(filename, &data) != 0) return false; @@ -62,7 +62,7 @@ struct file : storage, varint { return !(data.st_mode & S_IFDIR); } - static uintmax_t size(const string& filename) { + static auto size(const string& filename) -> uintmax_t { #if !defined(_WIN32) struct stat data; stat(filename, &data); @@ -73,7 +73,7 @@ struct file : storage, varint { return S_ISREG(data.st_mode) ? data.st_size : 0u; } - static vector read(const string& filename) { + static auto read(const string& filename) -> vector { vector memory; file fp; if(fp.open(filename, mode::read)) { @@ -83,7 +83,7 @@ struct file : storage, varint { return memory; } - static bool read(const string& filename, uint8_t* data, unsigned size) { + static auto read(const string& filename, uint8_t* data, unsigned size) -> bool { file fp; if(fp.open(filename, mode::read) == false) return false; fp.read(data, size); @@ -91,7 +91,7 @@ struct file : storage, varint { return true; } - static bool write(const string& filename, const string& text) { + static auto write(const string& filename, const string& text) -> bool { file fp; if(fp.open(filename, mode::write) == false) return false; fp.print(text); @@ -99,7 +99,7 @@ struct file : storage, varint { return true; } - static bool write(const string& filename, const vector& buffer) { + static auto write(const string& filename, const vector& buffer) -> bool { file fp; if(fp.open(filename, mode::write) == false) return false; fp.write(buffer.data(), buffer.size()); @@ -107,7 +107,7 @@ struct file : storage, varint { return true; } - static bool write(const string& filename, const uint8_t* data, unsigned size) { + static auto write(const string& filename, const uint8_t* data, unsigned size) -> bool { file fp; if(fp.open(filename, mode::write) == false) return false; fp.write(data, size); @@ -115,7 +115,7 @@ struct file : storage, varint { return true; } - static bool create(const string& filename) { + static auto create(const string& filename) -> bool { //create an empty file (will replace existing files) file fp; if(fp.open(filename, mode::write) == false) return false; @@ -123,12 +123,12 @@ struct file : storage, varint { return true; } - static string sha256(const string& filename) { + static auto sha256(const string& filename) -> string { auto buffer = read(filename); return Hash::SHA256(buffer.data(), buffer.size()).digest(); } - uint8_t read() { + auto read() -> uint8_t { if(!fp) return 0xff; //file not open if(file_mode == mode::write) return 0xff; //reads not permitted if(file_offset >= file_size) return 0xff; //cannot read past end of file @@ -136,7 +136,7 @@ struct file : storage, varint { return buffer[(file_offset++) & buffer_mask]; } - uintmax_t readl(unsigned length = 1) { + auto readl(unsigned length = 1) -> uintmax_t { uintmax_t data = 0; for(int i = 0; i < length; i++) { data |= (uintmax_t)read() << (i << 3); @@ -144,7 +144,7 @@ struct file : storage, varint { return data; } - uintmax_t readm(unsigned length = 1) { + auto readm(unsigned length = 1) -> uintmax_t { uintmax_t data = 0; while(length--) { data <<= 8; @@ -153,11 +153,11 @@ struct file : storage, varint { return data; } - void read(uint8_t* buffer, unsigned length) { + auto read(uint8_t* buffer, unsigned length) -> void { while(length--) *buffer++ = read(); } - void write(uint8_t data) { + auto write(uint8_t data) -> void { if(!fp) return; //file not open if(file_mode == mode::read) return; //writes not permitted buffer_sync(); @@ -166,35 +166,35 @@ struct file : storage, varint { if(file_offset > file_size) file_size = file_offset; } - void writel(uintmax_t data, unsigned length = 1) { + auto writel(uintmax_t data, unsigned length = 1) -> void { while(length--) { write(data); data >>= 8; } } - void writem(uintmax_t data, unsigned length = 1) { + auto writem(uintmax_t data, unsigned length = 1) -> void { for(int i = length - 1; i >= 0; i--) { write(data >> (i << 3)); } } - void write(const uint8_t* buffer, unsigned length) { + auto write(const uint8_t* buffer, unsigned length) -> void { while(length--) write(*buffer++); } - template void print(Args... args) { + template auto print(Args... args) -> void { string data(args...); const char* p = data; while(*p) write(*p++); } - void flush() { + auto flush() -> void { buffer_flush(); fflush(fp); } - void seek(int offset, index index_ = index::absolute) { + auto seek(signed offset, index index_ = index::absolute) -> void { if(!fp) return; //file not open buffer_flush(); @@ -217,17 +217,17 @@ struct file : storage, varint { file_offset = req_offset; } - unsigned offset() const { + auto offset() const -> unsigned { if(!fp) return 0; //file not open return file_offset; } - unsigned size() const { + auto size() const -> unsigned { if(!fp) return 0; //file not open return file_size; } - bool truncate(unsigned size) { + auto truncate(unsigned size) -> bool { if(!fp) return false; //file not open #if !defined(_WIN32) return ftruncate(fileno(fp), size) == 0; @@ -236,12 +236,12 @@ struct file : storage, varint { #endif } - bool end() { + auto end() -> bool { if(!fp) return true; //file not open return file_offset >= file_size; } - bool open() const { + auto open() const -> bool { return fp; } @@ -249,7 +249,7 @@ struct file : storage, varint { return open(); } - bool open(const string& filename, mode mode_) { + auto open(const string& filename, mode mode_) -> bool { if(fp) return false; switch(file_mode = mode_) { @@ -274,14 +274,14 @@ struct file : storage, varint { return true; } - void close() { + auto close() -> void { if(!fp) return; buffer_flush(); fclose(fp); fp = nullptr; } - file& operator=(const file&) = delete; + auto operator=(const file&) -> file& = delete; file(const file&) = delete; file() = default; @@ -298,12 +298,12 @@ private: char buffer[buffer_size] = {0}; int buffer_offset = -1; //invalidate buffer bool buffer_dirty = false; - FILE *fp = nullptr; + FILE* fp = nullptr; unsigned file_offset = 0; unsigned file_size = 0; mode file_mode = mode::read; - void buffer_sync() { + auto buffer_sync() -> void { if(!fp) return; //file not open if(buffer_offset != (file_offset & ~buffer_mask)) { buffer_flush(); @@ -314,7 +314,7 @@ private: } } - void buffer_flush() { + auto buffer_flush() -> void { if(!fp) return; //file not open if(file_mode == mode::read) return; //buffer cannot be written to if(buffer_offset < 0) return; //buffer unused diff --git a/nall/http/request.hpp b/nall/http/request.hpp index 934db529..bca7ae1e 100644 --- a/nall/http/request.hpp +++ b/nall/http/request.hpp @@ -32,11 +32,14 @@ struct httpRequest : httpMessage { auto removeHeader(const string& name) -> type& { return httpMessage::removeHeader(name), *this; } auto setHeader(const string& name, const string& value = "") -> type& { return httpMessage::setHeader(name, value), *this; } - auto get(const string& name) -> string { return _get.get(name); } - auto setGet(const string& name, const string& value = "") -> void { return _get.set(name, value); } + auto cookie(const string& name) const -> string { return _cookie.get(name); } + auto setCookie(const string& name, const string& value = "") -> void { _cookie.set(name, value); } - auto post(const string& name) -> string { return _post.get(name); } - auto setPost(const string& name, const string& value = "") -> void { return _post.set(name, value); } + auto get(const string& name) const -> string { return _get.get(name); } + auto setGet(const string& name, const string& value = "") -> void { _get.set(name, value); } + + auto post(const string& name) const -> string { return _post.get(name); } + auto setPost(const string& name, const string& value = "") -> void { _post.set(name, value); } //private: uint32_t _ip = 0; @@ -112,6 +115,14 @@ auto httpRequest::setHead() -> bool { auto part = header.split<1>(":").strip(); if(!part[0] || part.size() != 2) continue; appendHeader(part[0], part[1]); + + if(part[0].iequals("Cookie")) { + for(auto& block : part[1].split(";")) { + lstring variable = block.split<1>("=").strip(); + variable(1).ltrim("\"").rtrim("\""); + if(variable(0)) setCookie(variable(0), variable(1)); + } + } } if(requestHost) setHeader("Host", requestHost); //request URI overrides host header @@ -131,7 +142,7 @@ auto httpRequest::body(const function& callback auto httpRequest::setBody() -> bool { if(requestType() == RequestType::Post) { if(header("Content-Type").iequals("application/x-www-form-urlencoded")) { - for(auto& block : _body.split("\n")) { + for(auto& block : _body.split("&")) { lstring variable = block.rtrim("\r").split<1>("="); if(variable(0)) setPost(variable(0), variable(1)); } diff --git a/nall/http/role.hpp b/nall/http/role.hpp index 7a1970e2..248cc0bd 100644 --- a/nall/http/role.hpp +++ b/nall/http/role.hpp @@ -26,10 +26,10 @@ struct httpRole { }; auto httpRole::configure(const string& parameters) -> bool { - auto document = Markup::Document(parameters); - for(auto& parameter : document) { - string& name = parameter.name; - signed value = parameter.integer(); + auto document = BML::unserialize(parameters); + for(auto parameter : document) { + auto name = parameter.name(); + auto value = parameter.integer(); if(0); else if(name == "connectionLimit") settings.connectionLimit = value; diff --git a/nall/nall.hpp b/nall/nall.hpp index 6e4264c4..a3b96922 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -21,7 +21,7 @@ #include #include #include -#include +//#include #include #include #include @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include diff --git a/nall/string.hpp b/nall/string.hpp index 32f15f4c..12f4162f 100644 --- a/nall/string.hpp +++ b/nall/string.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -48,9 +49,9 @@ #include #include #include +#include #include #include -#include #include #include #undef NALL_STRING_INTERNAL_HPP diff --git a/nall/string/base.hpp b/nall/string/base.hpp index 5bac59f3..338ac3a3 100644 --- a/nall/string/base.hpp +++ b/nall/string/base.hpp @@ -211,6 +211,8 @@ public: auto operator> (const char* s) const -> bool { return strcmp(data(), s) > 0; } auto operator>=(const char* s) const -> bool { return strcmp(data(), s) >= 0; } + auto operator+=(const string& s) -> type& { return append(s); } + string(const string& source) : string() { operator=(source); } string(string&& source) : string() { operator=(std::move(source)); } @@ -223,6 +225,7 @@ public: inline auto integer() const -> intmax_t { return nall::integer(*this); } inline auto decimal() const -> uintmax_t { return nall::decimal(*this); } inline auto hex() const -> uintmax_t { return nall::hex(*this); } + inline auto numeral() const -> intmax_t { return nall::numeral(*this); } //core.hpp inline auto operator[](signed) const -> const char&; @@ -244,7 +247,7 @@ public: //compare.hpp auto compare(rstring source) const -> signed { return nall::compare(*this, source); } - auto icompare(rstring source) const -> signed { return nall::compare(*this, source); } + auto icompare(rstring source) const -> signed { return nall::icompare(*this, source); } auto equals(rstring source) const -> bool { return nall::equals(*this, source); } auto iequals(rstring source) const -> bool { return nall::iequals(*this, source); } diff --git a/nall/string/markup/bml.hpp b/nall/string/markup/bml.hpp index 7f5e8252..08d28973 100644 --- a/nall/string/markup/bml.hpp +++ b/nall/string/markup/bml.hpp @@ -1,12 +1,17 @@ #ifdef NALL_STRING_INTERNAL_HPP //BML v1.0 parser -//revision 0.03 +//revision 0.04 namespace nall { namespace BML { -struct Node : Markup::Node { +//metadata is used to store nesting level + +struct ManagedNode; +using SharedNode = shared_pointer; + +struct ManagedNode : Markup::ManagedNode { protected: //test to verify if a valid character for a node name bool valid(char p) const { //A-Z, a-z, 0-9, -. @@ -32,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 = substr(p, 0, length); p += length; } @@ -41,18 +46,18 @@ protected: unsigned length = 2; while(p[length] && p[length] != '\n' && p[length] != '\"') length++; if(p[length] != '\"') throw "Unescaped value"; - data = {substr(p, 2, length - 2), "\n"}; + _value = {substr(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"; - data = {substr(p, 1, length - 1), "\n"}; + _value = {substr(p, 1, length - 1), "\n"}; p += length; } else if(*p == ':') { unsigned length = 1; while(p[length] && p[length] != '\n') length++; - data = {substr(p, 1, length - 1), "\n"}; + _value = {substr(p, 1, length - 1), "\n"}; p += length; } } @@ -64,41 +69,40 @@ protected: while(*p == ' ') p++; //skip excess spaces if(*(p + 0) == '/' && *(p + 1) == '/') break; //skip comments - Node node; - node.attribute = true; + SharedNode node(new ManagedNode); unsigned length = 0; while(valid(p[length])) length++; if(length == 0) throw "Invalid attribute name"; - node.name = substr(p, 0, length); - node.parseData(p += length); - node.data.rtrim("\n"); - children.append(node); + node->_name = substr(p, 0, length); + node->parseData(p += length); + node->_value.rtrim("\n"); + _children.append(node); } } //read a node and all of its child nodes void parseNode(const lstring& text, unsigned& y) { const char* p = text[y++]; - level = parseDepth(p); + _metadata = parseDepth(p); parseName(p); parseData(p); parseAttributes(p); while(y < text.size()) { unsigned depth = readDepth(text[y]); - if(depth <= level) break; + if(depth <= _metadata) break; if(text[y][depth] == ':') { - data.append(substr(text[y++], depth + 1), "\n"); + _value.append(substr(text[y++], depth + 1), "\n"); continue; } - Node node; - node.parseNode(text, y); - children.append(node); + SharedNode node(new ManagedNode); + node->parseNode(text, y); + _children.append(node); } - data.rtrim("\n"); + _value.rtrim("\n"); } //read top-level nodes @@ -120,36 +124,58 @@ protected: unsigned y = 0; while(y < text.size()) { - Node node; - node.parseNode(text, y); - if(node.level > 0) throw "Root nodes cannot be indented"; - children.append(node); + SharedNode node(new ManagedNode); + node->parseNode(text, y); + if(node->_metadata > 0) throw "Root nodes cannot be indented"; + _children.append(node); } } - friend class Document; + friend auto unserialize(const string&) -> Markup::Node; }; -struct Document : Node { - string error; +inline auto unserialize(const string& markup) -> Markup::Node { + SharedNode node(new ManagedNode); + try { + node->parse(markup); + } catch(const char* error) { + node.reset(); + } + return (Markup::SharedNode&)node; +} - bool load(const string& document) { - name = "", data = ""; - - try { - parse(document); - } catch(const char* error) { - this->error = error; - children.reset(); - return false; +inline auto serialize(const Markup::Node& node, unsigned depth = 0) -> string { + if(!node.name()) { + string result; + for(auto leaf : node) { + result.append(serialize(leaf, depth)); } - return true; + return result; } - Document(const string& document = "") { - load(document); + string padding; + padding.resize(depth * 2); + for(auto& byte : padding) byte = ' '; + + lstring lines; + if(auto value = node.value()) lines = value.split("\n"); + + string result; + result.append(padding); + result.append(node.name()); + if(lines.size() == 1) result.append(":", lines[0]); + result.append("\n"); + if(lines.size() > 1) { + padding.append(" "); + for(auto& line : lines) { + result.append(padding, ":", line, "\n"); + } } -}; + for(auto leaf : node) { + result.append(serialize(leaf, depth + 1)); + } + return result; +} } } diff --git a/nall/string/markup/document.hpp b/nall/string/markup/document.hpp deleted file mode 100644 index d9a8027e..00000000 --- a/nall/string/markup/document.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifdef NALL_STRING_INTERNAL_HPP - -namespace nall { -namespace Markup { - -inline Node Document(const string& markup) { - if(markup.beginsWith("<")) return XML::Document(markup); - return BML::Document(markup); -} - -} -} - -#endif diff --git a/nall/string/markup/find.hpp b/nall/string/markup/find.hpp new file mode 100644 index 00000000..d4b95931 --- /dev/null +++ b/nall/string/markup/find.hpp @@ -0,0 +1,135 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Markup { + +auto ManagedNode::_evaluate(string query) const -> bool { + if(!query) return true; + + for(auto& rule : query.replace(" ", "").split(",")) { + enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE }; + auto comparator = Comparator::ID; + if(rule.match("*!=*")) comparator = Comparator::NE; + else if(rule.match("*<=*")) comparator = Comparator::LE; + else if(rule.match("*>=*")) comparator = Comparator::GE; + else if(rule.match ("*=*")) comparator = Comparator::EQ; + else if(rule.match ("*<*")) comparator = Comparator::LT; + else if(rule.match ("*>*")) comparator = Comparator::GT; + + if(comparator == Comparator::ID) { + if(_find(rule).size()) continue; + return false; + } + + lstring side; + switch(comparator) { + case Comparator::EQ: side = rule.split<1> ("="); break; + case Comparator::NE: side = rule.split<1>("!="); break; + case Comparator::LT: side = rule.split<1> ("<"); break; + case Comparator::LE: side = rule.split<1>("<="); break; + case Comparator::GT: side = rule.split<1> (">"); break; + case Comparator::GE: side = rule.split<1>(">="); break; + } + + string data = string{_value}.strip(); + if(side(0).empty() == false) { + auto result = _find(side(0)); + if(result.size() == 0) return false; + data = result[0].value(); + } + + switch(comparator) { + case Comparator::EQ: if(data.match(side(1)) == true) continue; break; + case Comparator::NE: if(data.match(side(1)) == false) continue; break; + case Comparator::LT: if(numeral(data) < numeral(side(1))) continue; break; + case Comparator::LE: if(numeral(data) <= numeral(side(1))) continue; break; + case Comparator::GT: if(numeral(data) > numeral(side(1))) continue; break; + case Comparator::GE: if(numeral(data) >= numeral(side(1))) continue; break; + } + + return false; + } + + return true; +} + +auto ManagedNode::_find(const string& query) const -> vector { + vector result; + + lstring path = query.split("/"); + string name = path.take(0), rule; + unsigned lo = 0u, hi = ~0u; + + if(name.match("*[*]")) { + auto p = name.rtrim("]").split<1>("["); + name = p(0); + if(p(1).find("-")) { + p = p(1).split<1>("-"); + lo = p(0).empty() ? 0u : numeral(p(0)); + hi = p(1).empty() ? ~0u : numeral(p(1)); + } else { + lo = hi = numeral(p(1)); + } + } + + if(name.match("*(*)")) { + auto p = name.rtrim(")").split<1>("("); + name = p(0); + rule = p(1); + } + + unsigned position = 0; + for(auto& node : _children) { + if(!node->_name.match(name)) continue; + if(!node->_evaluate(rule)) continue; + + bool inrange = position >= lo && position <= hi; + position++; + if(!inrange) continue; + + if(path.size() == 0) { + result.append(node); + } else for(auto& item : node->_find(path.merge("/"))) { + result.append(item); + } + } + + return result; +} + +auto ManagedNode::_lookup(const string& path) const -> Node { + if(auto position = path.find("/")) { + auto name = path.slice(0, *position); + for(auto& node : _children) { + if(name == node->_name) { + return node->_lookup(path.slice(*position + 1)); + } + } + } else for(auto& node : _children) { + if(path == node->_name) return node; + } + return {}; +} + +auto ManagedNode::_create(const string& path) -> Node { + if(auto position = path.find("/")) { + auto name = path.slice(0, *position); + for(auto& node : _children) { + if(name == node->_name) { + return node->_create(path.slice(*position + 1)); + } + } + _children.append(new ManagedNode(name)); + return _children.last()->_create(path.slice(*position + 1)); + } + for(auto& node : _children) { + if(path == node->_name) return node; + } + _children.append(new ManagedNode(path)); + return _children.last(); +} + +} +} + +#endif diff --git a/nall/string/markup/node.hpp b/nall/string/markup/node.hpp index fbf2ae46..208205ad 100644 --- a/nall/string/markup/node.hpp +++ b/nall/string/markup/node.hpp @@ -1,151 +1,123 @@ #ifdef NALL_STRING_INTERNAL_HPP -//note: specific markups inherit from Markup::Node -//vector will slice any data; so derived nodes must not contain data nor virtual functions -//vector would incur a large performance penalty and greatly increased complexity - namespace nall { namespace Markup { -struct Node { - string name; - string data; - bool attribute; +struct Node; +struct ManagedNode; +using SharedNode = shared_pointer; - bool exists() const { - return !name.empty(); +struct ManagedNode { + ManagedNode() = default; + ManagedNode(const string& name) : _name(name) {} + ManagedNode(const string& name, const string& value) : _name(name), _value(value) {} + + auto clone() const -> SharedNode { + SharedNode clone(new ManagedNode(_name, _value)); + for(auto& child : _children) clone->_children.append(child->clone()); + return clone; } - string text() const { - return string{data}.strip(); - } - - bool boolean() const { - return text() != "false"; - } - - intmax_t integer() const { - return numeral(text()); - } - - uintmax_t decimal() const { - return numeral(text()); - } - - void reset() { - children.reset(); - } - - bool evaluate(const string& query) const { - if(query.empty()) return true; - lstring rules = string{query}.replace(" ", "").split(","); - - for(auto& rule : rules) { - enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE }; - auto comparator = Comparator::ID; - if(rule.match("*!=*")) comparator = Comparator::NE; - else if(rule.match("*<=*")) comparator = Comparator::LE; - else if(rule.match("*>=*")) comparator = Comparator::GE; - else if(rule.match ("*=*")) comparator = Comparator::EQ; - else if(rule.match ("*<*")) comparator = Comparator::LT; - else if(rule.match ("*>*")) comparator = Comparator::GT; - - if(comparator == Comparator::ID) { - if(find(rule).size()) continue; - return false; - } - - lstring side; - switch(comparator) { - case Comparator::EQ: side = rule.split<1> ("="); break; - case Comparator::NE: side = rule.split<1>("!="); break; - case Comparator::LT: side = rule.split<1> ("<"); break; - case Comparator::LE: side = rule.split<1>("<="); break; - case Comparator::GT: side = rule.split<1> (">"); break; - case Comparator::GE: side = rule.split<1>(">="); break; - } - - string data = text(); - if(side(0).empty() == false) { - auto result = find(side(0)); - if(result.size() == 0) return false; - data = result(0).data; - } - - switch(comparator) { - case Comparator::EQ: if(data.match(side(1)) == true) continue; break; - case Comparator::NE: if(data.match(side(1)) == false) continue; break; - case Comparator::LT: if(numeral(data) < numeral(side(1))) continue; break; - case Comparator::LE: if(numeral(data) <= numeral(side(1))) continue; break; - case Comparator::GT: if(numeral(data) > numeral(side(1))) continue; break; - case Comparator::GE: if(numeral(data) >= numeral(side(1))) continue; break; - } - - return false; - } - - return true; - } - - vector find(const string& query) const { - vector result; - - lstring path = query.split("/"); - string name = path.take(0), rule; - unsigned lo = 0u, hi = ~0u; - - if(name.match("*[*]")) { - lstring side = name.split<1>("["); - name = side(0); - side = side(1).rtrim("]").split<1>("-"); - lo = side(0).empty() ? 0u : numeral(side(0)); - hi = side(1).empty() ? ~0u : numeral(side(1)); - } - - if(name.match("*(*)")) { - lstring side = name.split<1>("("); - name = side(0); - rule = side(1).rtrim(")"); - } - - unsigned position = 0; - for(auto& node : children) { - if(node.name.match(name) == false) continue; - if(node.evaluate(rule) == false) continue; - - bool inrange = position >= lo && position <= hi; - position++; - if(inrange == false) continue; - - if(path.size() == 0) result.append(node); - else { - auto list = node.find(path.merge("/")); - for(auto& item : list) result.append(item); - } - } - - return result; - } - - Node operator[](const string& query) const { - auto result = find(query); - return result(0); - } - - vector::iterator begin() { return children.begin(); } - vector::iterator end() { return children.end(); } - - const vector::constIterator begin() const { return children.begin(); } - const vector::constIterator end() const { return children.end(); } - - Node() : attribute(false), level(0) {} - protected: - unsigned level; - vector children; + string _name; + string _value; + uintptr_t _metadata = 0; + vector _children; + + inline auto _evaluate(string query) const -> bool; + inline auto _find(const string& query) const -> vector; + inline auto _lookup(const string& path) const -> Node; + inline auto _create(const string& path) -> Node; + + friend class Node; +}; + +struct Node { + Node() : shared(new ManagedNode) {} + Node(const SharedNode& source) : shared(source ? source : new ManagedNode) {} + Node(const string& name) : shared(new ManagedNode(name)) {} + Node(const string& name, const string& value) : shared(new ManagedNode(name, value)) {} + + auto unique() const -> bool { return shared.unique(); } + auto clone() const -> Node { return shared->clone(); } + + explicit operator bool() const { return shared->_name || shared->_children; } + auto name() const -> string { return shared->_name; } + auto value() const -> string { return shared->_value; } + + auto text() const -> string { return value().strip(); } + auto boolean() const -> bool { return text() == "true"; } + auto integer() const -> intmax_t { return text().numeral(); } + auto decimal() const -> uintmax_t { return text().numeral(); } + + auto setName(const string& name = "") -> void { shared->_name = name; } + auto setValue(const string& value = "") -> void { shared->_value = value; } + + auto reset() -> void { shared->_children.reset(); } + auto size() const -> unsigned { return shared->_children.size(); } + + auto prepend(const Node& node) -> void { shared->_children.prepend(node.shared); } + auto append(const Node& node) -> void { shared->_children.append(node.shared); } + auto remove(const Node& node) -> bool { + for(auto n : range(size())) { + if(node.shared == shared->_children[n]) { + return shared->_children.remove(n), true; + } + } + return false; + } + + auto at(unsigned position) const -> Node { + if(position >= size()) return {}; + return shared->_children[position]; + } + + auto swap(unsigned x, unsigned y) -> bool { + if(x >= size() || y >= size()) return false; + return std::swap(shared->_children[x], shared->_children[y]), true; + } + + auto insert(unsigned position, const Node& node) -> bool { + if(position > size()) return false; //used > instead of >= to allow indexed-equivalent of append() + return shared->_children.insert(position, node.shared), true; + } + + auto remove(unsigned position) -> bool { + if(position >= size()) return false; + return shared->_children.remove(position), true; + } + + auto operator()(const string& path) -> Node { return shared->_create(path); } + auto operator[](const string& path) const -> Node { return shared->_lookup(path); } + auto find(const string& query) const -> vector { return shared->_find(query); } + + struct iterator { + auto operator*() -> Node { return {source.shared->_children[position]}; } + auto operator!=(const iterator& source) const -> bool { return position != source.position; } + auto operator++() -> iterator& { return position++, *this; } + iterator(const Node& source, unsigned position) : source(source), position(position) {} + + private: + const Node& source; + unsigned position; + }; + + auto begin() const -> iterator { return iterator(*this, 0); } + auto end() const -> iterator { return iterator(*this, size()); } + +protected: + SharedNode shared; }; } } +namespace nall { + +inline range_t range(const Markup::Node& node) { + return range_t{0, (signed)node.size(), 1}; +} + +} + #endif diff --git a/nall/string/markup/xml.hpp b/nall/string/markup/xml.hpp index fd9fe787..16a6d2c9 100644 --- a/nall/string/markup/xml.hpp +++ b/nall/string/markup/xml.hpp @@ -1,21 +1,29 @@ #ifdef NALL_STRING_INTERNAL_HPP //XML v1.0 subset parser -//revision 0.03 +//revision 0.04 namespace nall { namespace XML { -struct Node : Markup::Node { +//metadata: +// 0 = element +// 1 = attribute + +struct ManagedNode; +using SharedNode = shared_pointer; + +struct ManagedNode : Markup::ManagedNode { protected: inline string escape() const { - string result = data; + string result = _value; result.replace("&", "&"); result.replace("<", "<"); result.replace(">", ">"); - if(attribute == false) return result; - result.replace("\'", "'"); - result.replace("\"", """); + if(_metadata == 1) { + result.replace("\'", "'"); + result.replace("\"", """); + } return result; } @@ -54,7 +62,7 @@ protected: if(!memory::compare(source, """, 6)) { *output++ = '\"'; source += 6; length -= 6; continue; } } - if(attribute == false && source[0] == '<' && source[1] == '!') { + if(_metadata == 0 && source[0] == '<' && source[1] == '!') { //comment if(!memory::compare(source, "