diff --git a/Makefile b/GNUmakefile
similarity index 75%
rename from Makefile
rename to GNUmakefile
index c5241339..616430e8 100644
--- a/Makefile
+++ b/GNUmakefile
@@ -1,4 +1,4 @@
-include nall/Makefile
+include nall/GNUmakefile
 
 fc  := fc
 sfc := sfc
@@ -7,17 +7,12 @@ gba := gba
 
 profile := accuracy
 target := higan
-# target := loki
-
-ifeq ($(target),loki)
-  options += debugger
-endif
 
 # arch := x86
 # console := true
 
 # compiler
-flags += -I. -O3 -fomit-frame-pointer
+flags += -I. -O3
 link +=
 objects := libco
 
@@ -43,16 +38,16 @@ ifeq ($(platform),windows)
   else
     link += -mwindows
   endif
-  link += -s -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
+  link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
   link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
 else ifeq ($(platform),macosx)
   flags += -march=native
 else ifeq ($(platform),linux)
   flags += -march=native
-  link += -s -Wl,-export-dynamic -lX11 -lXext -ldl
+  link += -Wl,-export-dynamic -lX11 -lXext -ldl
 else ifeq ($(platform),bsd)
   flags += -march=native
-  link += -s -Wl,-export-dynamic -lX11 -lXext
+  link += -Wl,-export-dynamic -lX11 -lXext
 else
   $(error unsupported platform.)
 endif
@@ -76,7 +71,7 @@ all: build;
 
 obj/libco.o: libco/libco.c libco/*
 
-include $(ui)/Makefile
+include $(ui)/GNUmakefile
 flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
 
 # targets
@@ -87,8 +82,6 @@ clean:
 	-@$(call delete,obj/*.so)
 	-@$(call delete,obj/*.dylib)
 	-@$(call delete,obj/*.dll)
-	-@$(call delete,*.res)
-	-@$(call delete,*.manifest)
 
 archive:
 	if [ -f higan.tar.xz ]; then rm higan.tar.xz; fi
@@ -99,16 +92,16 @@ ifeq ($(shell id -un),byuu)
 	if [ -d ./libco ]; then rm -r ./libco; fi
 	if [ -d ./nall ]; then rm -r ./nall; fi
 	if [ -d ./ruby ]; then rm -r ./ruby; fi
-	if [ -d ./phoenix ]; then rm -r ./phoenix; fi
+	if [ -d ./hiro ]; then rm -r ./hiro; fi
 	cp -r ../libco ./libco
 	cp -r ../nall ./nall
 	cp -r ../ruby ./ruby
-	cp -r ../phoenix ./phoenix
+	cp -r ../hiro ./hiro
 	rm -r libco/doc
-	rm -r libco/.test
-	rm -r nall/.test
-	rm -r ruby/.test
-	rm -r phoenix/.test
+	rm -r libco/-test
+	rm -r nall/-test
+	rm -r ruby/-test
+	rm -r hiro/-test
 endif
 
 help:;
diff --git a/data/higan.desktop b/data/higan.desktop
index 70a2242e..4731230b 100644
--- a/data/higan.desktop
+++ b/data/higan.desktop
@@ -1,6 +1,6 @@
 [Desktop Entry]
 Name=higan
-Comment=SNES emulator
+Comment=Nintendo emulator
 Exec=higan
 Icon=higan
 Terminal=false
diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp
index 8b59706b..efa5b2b4 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.08";
+  static const char Version[] = "094.09";
   static const char Author[] = "byuu";
   static const char License[] = "GPLv3";
   static const char Website[] = "http://byuu.org/";
@@ -26,7 +26,6 @@ namespace Emulator {
 #include <nall/endian.hpp>
 #include <nall/file.hpp>
 #include <nall/function.hpp>
-#include <nall/http.hpp>
 #include <nall/image.hpp>
 #include <nall/invoke.hpp>
 #include <nall/priority-queue.hpp>
@@ -34,12 +33,12 @@ namespace Emulator {
 #include <nall/random.hpp>
 #include <nall/serializer.hpp>
 #include <nall/set.hpp>
-#include <nall/sha256.hpp>
 #include <nall/stdint.hpp>
 #include <nall/string.hpp>
 #include <nall/utility.hpp>
 #include <nall/varint.hpp>
 #include <nall/vector.hpp>
+#include <nall/hash/sha256.hpp>
 #include <nall/stream/memory.hpp>
 #include <nall/stream/vector.hpp>
 using namespace nall;
diff --git a/fc/Makefile b/fc/GNUmakefile
similarity index 100%
rename from fc/Makefile
rename to fc/GNUmakefile
diff --git a/fc/cartridge/cartridge.cpp b/fc/cartridge/cartridge.cpp
index 57dc723b..844c3b0d 100644
--- a/fc/cartridge/cartridge.cpp
+++ b/fc/cartridge/cartridge.cpp
@@ -24,16 +24,10 @@ void Cartridge::load() {
   Board::load(information.markup);  //this call will set Cartridge::board if successful
   if(board == nullptr) return;
 
-  sha256_ctx sha;
-  uint8 hash[32];
-  sha256_init(&sha);
-  sha256_chunk(&sha, board->prgrom.data, board->prgrom.size);
-  sha256_chunk(&sha, board->chrrom.data, board->chrrom.size);
-  sha256_final(&sha);
-  sha256_hash(&sha, hash);
-  string result;
-  for(auto& byte : hash) result.append(hex<2>(byte));
-  sha256 = result;
+  Hash::SHA256 sha;
+  sha.data(board->prgrom.data, board->prgrom.size);
+  sha.data(board->chrrom.data, board->chrrom.size);
+  sha256 = sha.digest();
 
   system.load();
   loaded = true;
diff --git a/gb/Makefile b/gb/GNUmakefile
similarity index 100%
rename from gb/Makefile
rename to gb/GNUmakefile
diff --git a/gb/cartridge/cartridge.cpp b/gb/cartridge/cartridge.cpp
index fa8b2706..0b85220c 100644
--- a/gb/cartridge/cartridge.cpp
+++ b/gb/cartridge/cartridge.cpp
@@ -25,7 +25,7 @@ void Cartridge::load_empty(System::Revision revision) {
   romdata = allocate<uint8>(romsize, 0xff);
   ramsize = 0;
   mapper = &mbc0;
-  sha256 = nall::sha256(romdata, romsize);
+  sha256 = Hash::SHA256(romdata, romsize).digest();
   loaded = true;
   system.load(revision);
 }
@@ -94,7 +94,7 @@ void Cartridge::load(System::Revision revision) {
   case Mapper::HuC3:  mapper = &huc3;  break;
   }
 
-  sha256 = nall::sha256(romdata, romsize);
+  sha256 = Hash::SHA256(romdata, romsize).digest();
   loaded = true;
   system.load(revision);
 }
diff --git a/gba/Makefile b/gba/GNUmakefile
similarity index 100%
rename from gba/Makefile
rename to gba/GNUmakefile
diff --git a/gba/cartridge/cartridge.cpp b/gba/cartridge/cartridge.cpp
index e2d787d3..24f6be0f 100644
--- a/gba/cartridge/cartridge.cpp
+++ b/gba/cartridge/cartridge.cpp
@@ -68,7 +68,7 @@ void Cartridge::load() {
     }
   }
 
-  sha256 = nall::sha256(rom.data, rom_size);
+  sha256 = Hash::SHA256(rom.data, rom_size).digest();
 
   system.load();
   loaded = true;
diff --git a/gba/player/player.cpp b/gba/player/player.cpp
index 4248b916..1b7b59bb 100644
--- a/gba/player/player.cpp
+++ b/gba/player/player.cpp
@@ -20,7 +20,7 @@ void Player::power() {
 }
 
 void Player::frame() {
-  uint32 hash = crc32_calculate((const uint8*)ppu.output, 240 * 160 * sizeof(uint32));
+  uint32 hash = Hash::CRC32(ppu.output, 240 * 160 * sizeof(uint32)).value();
   status.logoDetected = (hash == 0x7776eb55);
 
   if(status.logoDetected) {
diff --git a/hiro/GNUmakefile b/hiro/GNUmakefile
new file mode 100644
index 00000000..1be0edb0
--- /dev/null
+++ b/hiro/GNUmakefile
@@ -0,0 +1,24 @@
+ifeq ($(platform),)
+  hiroflags = $(cppflags) $(flags) -DHIRO_REFERENCE
+  hirolink =
+else ifeq ($(platform),windows)
+  hiroflags = $(cppflags) $(flags) -DHIRO_WINDOWS
+  hirolink = -lkernel32 -luser32 -lgdi32 -ladvapi32 -lole32 -lcomctl32 -lcomdlg32 -luxtheme -lmsimg32 -lshlwapi
+else ifeq ($(platform),macosx)
+  hiroflags = $(objcppflags) $(flags) -DHIRO_COCOA
+  hirolink = -framework Cocoa -framework Carbon
+else
+  ifeq ($(hiro),)
+    hiro := gtk
+  endif
+
+  ifeq ($(hiro),gtk)
+    hiroflags = $(cppflags) $(flags) -DHIRO_GTK `pkg-config --cflags gtk+-2.0 gtksourceview-2.0`
+    hirolink = `pkg-config --libs gtk+-2.0 gtksourceview-2.0` -lX11
+  endif
+
+  ifeq ($(hiro),qt)
+    hiroflags = $(cppflags) $(flags) -DHIRO_QT `pkg-config --cflags QtCore QtGui`
+    hirolink = `pkg-config --libs QtCore QtGui` -lX11
+  endif
+endif
diff --git a/phoenix/cocoa/action/action.cpp b/hiro/cocoa/action/action.cpp
similarity index 100%
rename from phoenix/cocoa/action/action.cpp
rename to hiro/cocoa/action/action.cpp
diff --git a/phoenix/cocoa/action/action.hpp b/hiro/cocoa/action/action.hpp
similarity index 100%
rename from phoenix/cocoa/action/action.hpp
rename to hiro/cocoa/action/action.hpp
diff --git a/phoenix/cocoa/action/check-item.cpp b/hiro/cocoa/action/check-item.cpp
similarity index 100%
rename from phoenix/cocoa/action/check-item.cpp
rename to hiro/cocoa/action/check-item.cpp
diff --git a/phoenix/cocoa/action/check-item.hpp b/hiro/cocoa/action/check-item.hpp
similarity index 100%
rename from phoenix/cocoa/action/check-item.hpp
rename to hiro/cocoa/action/check-item.hpp
diff --git a/phoenix/cocoa/action/item.cpp b/hiro/cocoa/action/item.cpp
similarity index 100%
rename from phoenix/cocoa/action/item.cpp
rename to hiro/cocoa/action/item.cpp
diff --git a/phoenix/cocoa/action/item.hpp b/hiro/cocoa/action/item.hpp
similarity index 100%
rename from phoenix/cocoa/action/item.hpp
rename to hiro/cocoa/action/item.hpp
diff --git a/phoenix/cocoa/action/menu.cpp b/hiro/cocoa/action/menu.cpp
similarity index 100%
rename from phoenix/cocoa/action/menu.cpp
rename to hiro/cocoa/action/menu.cpp
diff --git a/phoenix/cocoa/action/menu.hpp b/hiro/cocoa/action/menu.hpp
similarity index 100%
rename from phoenix/cocoa/action/menu.hpp
rename to hiro/cocoa/action/menu.hpp
diff --git a/phoenix/cocoa/action/radio-item.cpp b/hiro/cocoa/action/radio-item.cpp
similarity index 100%
rename from phoenix/cocoa/action/radio-item.cpp
rename to hiro/cocoa/action/radio-item.cpp
diff --git a/phoenix/cocoa/action/radio-item.hpp b/hiro/cocoa/action/radio-item.hpp
similarity index 100%
rename from phoenix/cocoa/action/radio-item.hpp
rename to hiro/cocoa/action/radio-item.hpp
diff --git a/phoenix/cocoa/action/separator.cpp b/hiro/cocoa/action/separator.cpp
similarity index 100%
rename from phoenix/cocoa/action/separator.cpp
rename to hiro/cocoa/action/separator.cpp
diff --git a/phoenix/cocoa/action/separator.hpp b/hiro/cocoa/action/separator.hpp
similarity index 100%
rename from phoenix/cocoa/action/separator.hpp
rename to hiro/cocoa/action/separator.hpp
diff --git a/phoenix/cocoa/application.cpp b/hiro/cocoa/application.cpp
similarity index 100%
rename from phoenix/cocoa/application.cpp
rename to hiro/cocoa/application.cpp
diff --git a/phoenix/cocoa/application.hpp b/hiro/cocoa/application.hpp
similarity index 100%
rename from phoenix/cocoa/application.hpp
rename to hiro/cocoa/application.hpp
diff --git a/phoenix/cocoa/browser-window.cpp b/hiro/cocoa/browser-window.cpp
similarity index 94%
rename from phoenix/cocoa/browser-window.cpp
rename to hiro/cocoa/browser-window.cpp
index 014cb6dd..9302061b 100644
--- a/phoenix/cocoa/browser-window.cpp
+++ b/hiro/cocoa/browser-window.cpp
@@ -24,7 +24,7 @@ string pBrowserWindow::open(BrowserWindow::State& state) {
   @autoreleasepool {
     NSMutableArray* filters = [[NSMutableArray alloc] init];
     for(auto& rule : state.filters) {
-      string pattern = rule.split<1>("(")(1).rtrim<1>(")");
+      string pattern = rule.split<1>("(")(1).rtrim(")");
       if(!pattern.empty()) [filters addObject:[NSString stringWithUTF8String:pattern]];
     }
     NSOpenPanel* panel = [NSOpenPanel openPanel];
@@ -49,7 +49,7 @@ string pBrowserWindow::save(BrowserWindow::State& state) {
   @autoreleasepool {
     NSMutableArray* filters = [[NSMutableArray alloc] init];
     for(auto& rule : state.filters) {
-      string pattern = rule.split<1>("(")(1).rtrim<1>(")");
+      string pattern = rule.split<1>("(")(1).rtrim(")");
       if(!pattern.empty()) [filters addObject:[NSString stringWithUTF8String:pattern]];
     }
     NSSavePanel* panel = [NSSavePanel savePanel];
diff --git a/phoenix/cocoa/browser-window.hpp b/hiro/cocoa/browser-window.hpp
similarity index 100%
rename from phoenix/cocoa/browser-window.hpp
rename to hiro/cocoa/browser-window.hpp
diff --git a/phoenix/cocoa/desktop.cpp b/hiro/cocoa/desktop.cpp
similarity index 100%
rename from phoenix/cocoa/desktop.cpp
rename to hiro/cocoa/desktop.cpp
diff --git a/phoenix/cocoa/desktop.hpp b/hiro/cocoa/desktop.hpp
similarity index 100%
rename from phoenix/cocoa/desktop.hpp
rename to hiro/cocoa/desktop.hpp
diff --git a/phoenix/cocoa/font.cpp b/hiro/cocoa/font.cpp
similarity index 100%
rename from phoenix/cocoa/font.cpp
rename to hiro/cocoa/font.cpp
diff --git a/phoenix/cocoa/font.hpp b/hiro/cocoa/font.hpp
similarity index 100%
rename from phoenix/cocoa/font.hpp
rename to hiro/cocoa/font.hpp
diff --git a/phoenix/cocoa/header.hpp b/hiro/cocoa/header.hpp
similarity index 100%
rename from phoenix/cocoa/header.hpp
rename to hiro/cocoa/header.hpp
diff --git a/phoenix/cocoa/keyboard.cpp b/hiro/cocoa/keyboard.cpp
similarity index 100%
rename from phoenix/cocoa/keyboard.cpp
rename to hiro/cocoa/keyboard.cpp
diff --git a/phoenix/cocoa/keyboard.hpp b/hiro/cocoa/keyboard.hpp
similarity index 100%
rename from phoenix/cocoa/keyboard.hpp
rename to hiro/cocoa/keyboard.hpp
diff --git a/phoenix/cocoa/message-window.cpp b/hiro/cocoa/message-window.cpp
similarity index 100%
rename from phoenix/cocoa/message-window.cpp
rename to hiro/cocoa/message-window.cpp
diff --git a/phoenix/cocoa/message-window.hpp b/hiro/cocoa/message-window.hpp
similarity index 100%
rename from phoenix/cocoa/message-window.hpp
rename to hiro/cocoa/message-window.hpp
diff --git a/phoenix/cocoa/monitor.cpp b/hiro/cocoa/monitor.cpp
similarity index 100%
rename from phoenix/cocoa/monitor.cpp
rename to hiro/cocoa/monitor.cpp
diff --git a/phoenix/cocoa/monitor.hpp b/hiro/cocoa/monitor.hpp
similarity index 100%
rename from phoenix/cocoa/monitor.hpp
rename to hiro/cocoa/monitor.hpp
diff --git a/phoenix/cocoa/mouse.cpp b/hiro/cocoa/mouse.cpp
similarity index 100%
rename from phoenix/cocoa/mouse.cpp
rename to hiro/cocoa/mouse.cpp
diff --git a/phoenix/cocoa/mouse.hpp b/hiro/cocoa/mouse.hpp
similarity index 100%
rename from phoenix/cocoa/mouse.hpp
rename to hiro/cocoa/mouse.hpp
diff --git a/phoenix/cocoa/object.cpp b/hiro/cocoa/object.cpp
similarity index 100%
rename from phoenix/cocoa/object.cpp
rename to hiro/cocoa/object.cpp
diff --git a/phoenix/cocoa/object.hpp b/hiro/cocoa/object.hpp
similarity index 100%
rename from phoenix/cocoa/object.hpp
rename to hiro/cocoa/object.hpp
diff --git a/phoenix/cocoa/platform.cpp b/hiro/cocoa/platform.cpp
similarity index 100%
rename from phoenix/cocoa/platform.cpp
rename to hiro/cocoa/platform.cpp
diff --git a/phoenix/cocoa/platform.hpp b/hiro/cocoa/platform.hpp
similarity index 100%
rename from phoenix/cocoa/platform.hpp
rename to hiro/cocoa/platform.hpp
diff --git a/phoenix/cocoa/timer.cpp b/hiro/cocoa/timer.cpp
similarity index 100%
rename from phoenix/cocoa/timer.cpp
rename to hiro/cocoa/timer.cpp
diff --git a/phoenix/cocoa/timer.hpp b/hiro/cocoa/timer.hpp
similarity index 100%
rename from phoenix/cocoa/timer.hpp
rename to hiro/cocoa/timer.hpp
diff --git a/phoenix/cocoa/utility.cpp b/hiro/cocoa/utility.cpp
similarity index 100%
rename from phoenix/cocoa/utility.cpp
rename to hiro/cocoa/utility.cpp
diff --git a/phoenix/cocoa/widget/button.cpp b/hiro/cocoa/widget/button.cpp
similarity index 94%
rename from phoenix/cocoa/widget/button.cpp
rename to hiro/cocoa/widget/button.cpp
index 950bffef..3114c7b1 100644
--- a/phoenix/cocoa/widget/button.cpp
+++ b/hiro/cocoa/widget/button.cpp
@@ -32,7 +32,10 @@ Size pButton::minimumSize() {
     size.height += button.state.image.height;
   }
 
-  return {size.width + 20, size.height + 4};
+  return {size.width + (button.state.text ? 20 : 4), size.height + 4};
+}
+
+void pButton::setBordered(bool bordered) {
 }
 
 void pButton::setGeometry(Geometry geometry) {
diff --git a/phoenix/cocoa/widget/button.hpp b/hiro/cocoa/widget/button.hpp
similarity index 93%
rename from phoenix/cocoa/widget/button.hpp
rename to hiro/cocoa/widget/button.hpp
index 98569454..62f16361 100644
--- a/phoenix/cocoa/widget/button.hpp
+++ b/hiro/cocoa/widget/button.hpp
@@ -13,6 +13,7 @@ struct pButton : public pWidget {
   CocoaButton* cocoaButton = nullptr;
 
   Size minimumSize();
+  void setBordered(bool bordered);
   void setGeometry(Geometry geometry);
   void setImage(const image& image, Orientation orientation);
   void setText(string text);
diff --git a/phoenix/cocoa/widget/canvas.cpp b/hiro/cocoa/widget/canvas.cpp
similarity index 100%
rename from phoenix/cocoa/widget/canvas.cpp
rename to hiro/cocoa/widget/canvas.cpp
diff --git a/phoenix/cocoa/widget/canvas.hpp b/hiro/cocoa/widget/canvas.hpp
similarity index 100%
rename from phoenix/cocoa/widget/canvas.hpp
rename to hiro/cocoa/widget/canvas.hpp
diff --git a/phoenix/cocoa/widget/check-button.cpp b/hiro/cocoa/widget/check-button.cpp
similarity index 100%
rename from phoenix/cocoa/widget/check-button.cpp
rename to hiro/cocoa/widget/check-button.cpp
diff --git a/phoenix/cocoa/widget/check-button.hpp b/hiro/cocoa/widget/check-button.hpp
similarity index 100%
rename from phoenix/cocoa/widget/check-button.hpp
rename to hiro/cocoa/widget/check-button.hpp
diff --git a/phoenix/cocoa/widget/check-label.cpp b/hiro/cocoa/widget/check-label.cpp
similarity index 100%
rename from phoenix/cocoa/widget/check-label.cpp
rename to hiro/cocoa/widget/check-label.cpp
diff --git a/phoenix/cocoa/widget/check-label.hpp b/hiro/cocoa/widget/check-label.hpp
similarity index 100%
rename from phoenix/cocoa/widget/check-label.hpp
rename to hiro/cocoa/widget/check-label.hpp
diff --git a/phoenix/cocoa/widget/combo-button.cpp b/hiro/cocoa/widget/combo-button.cpp
similarity index 100%
rename from phoenix/cocoa/widget/combo-button.cpp
rename to hiro/cocoa/widget/combo-button.cpp
diff --git a/phoenix/cocoa/widget/combo-button.hpp b/hiro/cocoa/widget/combo-button.hpp
similarity index 100%
rename from phoenix/cocoa/widget/combo-button.hpp
rename to hiro/cocoa/widget/combo-button.hpp
diff --git a/phoenix/cocoa/widget/console.cpp b/hiro/cocoa/widget/console.cpp
similarity index 100%
rename from phoenix/cocoa/widget/console.cpp
rename to hiro/cocoa/widget/console.cpp
diff --git a/phoenix/cocoa/widget/console.hpp b/hiro/cocoa/widget/console.hpp
similarity index 100%
rename from phoenix/cocoa/widget/console.hpp
rename to hiro/cocoa/widget/console.hpp
diff --git a/phoenix/cocoa/widget/frame.cpp b/hiro/cocoa/widget/frame.cpp
similarity index 100%
rename from phoenix/cocoa/widget/frame.cpp
rename to hiro/cocoa/widget/frame.cpp
diff --git a/phoenix/cocoa/widget/frame.hpp b/hiro/cocoa/widget/frame.hpp
similarity index 100%
rename from phoenix/cocoa/widget/frame.hpp
rename to hiro/cocoa/widget/frame.hpp
diff --git a/phoenix/cocoa/widget/hex-edit.cpp b/hiro/cocoa/widget/hex-edit.cpp
similarity index 100%
rename from phoenix/cocoa/widget/hex-edit.cpp
rename to hiro/cocoa/widget/hex-edit.cpp
diff --git a/phoenix/cocoa/widget/hex-edit.hpp b/hiro/cocoa/widget/hex-edit.hpp
similarity index 100%
rename from phoenix/cocoa/widget/hex-edit.hpp
rename to hiro/cocoa/widget/hex-edit.hpp
diff --git a/phoenix/cocoa/widget/horizontal-scroller.cpp b/hiro/cocoa/widget/horizontal-scroller.cpp
similarity index 100%
rename from phoenix/cocoa/widget/horizontal-scroller.cpp
rename to hiro/cocoa/widget/horizontal-scroller.cpp
diff --git a/phoenix/cocoa/widget/horizontal-scroller.hpp b/hiro/cocoa/widget/horizontal-scroller.hpp
similarity index 100%
rename from phoenix/cocoa/widget/horizontal-scroller.hpp
rename to hiro/cocoa/widget/horizontal-scroller.hpp
diff --git a/phoenix/cocoa/widget/horizontal-slider.cpp b/hiro/cocoa/widget/horizontal-slider.cpp
similarity index 100%
rename from phoenix/cocoa/widget/horizontal-slider.cpp
rename to hiro/cocoa/widget/horizontal-slider.cpp
diff --git a/phoenix/cocoa/widget/horizontal-slider.hpp b/hiro/cocoa/widget/horizontal-slider.hpp
similarity index 100%
rename from phoenix/cocoa/widget/horizontal-slider.hpp
rename to hiro/cocoa/widget/horizontal-slider.hpp
diff --git a/phoenix/cocoa/widget/label.cpp b/hiro/cocoa/widget/label.cpp
similarity index 100%
rename from phoenix/cocoa/widget/label.cpp
rename to hiro/cocoa/widget/label.cpp
diff --git a/phoenix/cocoa/widget/label.hpp b/hiro/cocoa/widget/label.hpp
similarity index 100%
rename from phoenix/cocoa/widget/label.hpp
rename to hiro/cocoa/widget/label.hpp
diff --git a/phoenix/cocoa/widget/layout.hpp b/hiro/cocoa/widget/layout.hpp
similarity index 100%
rename from phoenix/cocoa/widget/layout.hpp
rename to hiro/cocoa/widget/layout.hpp
diff --git a/phoenix/cocoa/widget/line-edit.cpp b/hiro/cocoa/widget/line-edit.cpp
similarity index 100%
rename from phoenix/cocoa/widget/line-edit.cpp
rename to hiro/cocoa/widget/line-edit.cpp
diff --git a/phoenix/cocoa/widget/line-edit.hpp b/hiro/cocoa/widget/line-edit.hpp
similarity index 100%
rename from phoenix/cocoa/widget/line-edit.hpp
rename to hiro/cocoa/widget/line-edit.hpp
diff --git a/phoenix/cocoa/widget/list-view.cpp b/hiro/cocoa/widget/list-view.cpp
similarity index 100%
rename from phoenix/cocoa/widget/list-view.cpp
rename to hiro/cocoa/widget/list-view.cpp
diff --git a/phoenix/cocoa/widget/list-view.hpp b/hiro/cocoa/widget/list-view.hpp
similarity index 100%
rename from phoenix/cocoa/widget/list-view.hpp
rename to hiro/cocoa/widget/list-view.hpp
diff --git a/phoenix/cocoa/widget/progress-bar.cpp b/hiro/cocoa/widget/progress-bar.cpp
similarity index 100%
rename from phoenix/cocoa/widget/progress-bar.cpp
rename to hiro/cocoa/widget/progress-bar.cpp
diff --git a/phoenix/cocoa/widget/progress-bar.hpp b/hiro/cocoa/widget/progress-bar.hpp
similarity index 100%
rename from phoenix/cocoa/widget/progress-bar.hpp
rename to hiro/cocoa/widget/progress-bar.hpp
diff --git a/phoenix/cocoa/widget/radio-button.cpp b/hiro/cocoa/widget/radio-button.cpp
similarity index 100%
rename from phoenix/cocoa/widget/radio-button.cpp
rename to hiro/cocoa/widget/radio-button.cpp
diff --git a/phoenix/cocoa/widget/radio-button.hpp b/hiro/cocoa/widget/radio-button.hpp
similarity index 100%
rename from phoenix/cocoa/widget/radio-button.hpp
rename to hiro/cocoa/widget/radio-button.hpp
diff --git a/phoenix/cocoa/widget/radio-label.cpp b/hiro/cocoa/widget/radio-label.cpp
similarity index 100%
rename from phoenix/cocoa/widget/radio-label.cpp
rename to hiro/cocoa/widget/radio-label.cpp
diff --git a/phoenix/cocoa/widget/radio-label.hpp b/hiro/cocoa/widget/radio-label.hpp
similarity index 100%
rename from phoenix/cocoa/widget/radio-label.hpp
rename to hiro/cocoa/widget/radio-label.hpp
diff --git a/phoenix/cocoa/widget/sizable.hpp b/hiro/cocoa/widget/sizable.hpp
similarity index 100%
rename from phoenix/cocoa/widget/sizable.hpp
rename to hiro/cocoa/widget/sizable.hpp
diff --git a/phoenix/cocoa/widget/tab-frame.cpp b/hiro/cocoa/widget/tab-frame.cpp
similarity index 100%
rename from phoenix/cocoa/widget/tab-frame.cpp
rename to hiro/cocoa/widget/tab-frame.cpp
diff --git a/phoenix/cocoa/widget/tab-frame.hpp b/hiro/cocoa/widget/tab-frame.hpp
similarity index 100%
rename from phoenix/cocoa/widget/tab-frame.hpp
rename to hiro/cocoa/widget/tab-frame.hpp
diff --git a/phoenix/cocoa/widget/text-edit.cpp b/hiro/cocoa/widget/text-edit.cpp
similarity index 100%
rename from phoenix/cocoa/widget/text-edit.cpp
rename to hiro/cocoa/widget/text-edit.cpp
diff --git a/phoenix/cocoa/widget/text-edit.hpp b/hiro/cocoa/widget/text-edit.hpp
similarity index 100%
rename from phoenix/cocoa/widget/text-edit.hpp
rename to hiro/cocoa/widget/text-edit.hpp
diff --git a/phoenix/cocoa/widget/vertical-scroller.cpp b/hiro/cocoa/widget/vertical-scroller.cpp
similarity index 100%
rename from phoenix/cocoa/widget/vertical-scroller.cpp
rename to hiro/cocoa/widget/vertical-scroller.cpp
diff --git a/phoenix/cocoa/widget/vertical-scroller.hpp b/hiro/cocoa/widget/vertical-scroller.hpp
similarity index 100%
rename from phoenix/cocoa/widget/vertical-scroller.hpp
rename to hiro/cocoa/widget/vertical-scroller.hpp
diff --git a/phoenix/cocoa/widget/vertical-slider.cpp b/hiro/cocoa/widget/vertical-slider.cpp
similarity index 100%
rename from phoenix/cocoa/widget/vertical-slider.cpp
rename to hiro/cocoa/widget/vertical-slider.cpp
diff --git a/phoenix/cocoa/widget/vertical-slider.hpp b/hiro/cocoa/widget/vertical-slider.hpp
similarity index 100%
rename from phoenix/cocoa/widget/vertical-slider.hpp
rename to hiro/cocoa/widget/vertical-slider.hpp
diff --git a/phoenix/cocoa/widget/viewport.cpp b/hiro/cocoa/widget/viewport.cpp
similarity index 100%
rename from phoenix/cocoa/widget/viewport.cpp
rename to hiro/cocoa/widget/viewport.cpp
diff --git a/phoenix/cocoa/widget/viewport.hpp b/hiro/cocoa/widget/viewport.hpp
similarity index 100%
rename from phoenix/cocoa/widget/viewport.hpp
rename to hiro/cocoa/widget/viewport.hpp
diff --git a/phoenix/cocoa/widget/widget.cpp b/hiro/cocoa/widget/widget.cpp
similarity index 100%
rename from phoenix/cocoa/widget/widget.cpp
rename to hiro/cocoa/widget/widget.cpp
diff --git a/phoenix/cocoa/widget/widget.hpp b/hiro/cocoa/widget/widget.hpp
similarity index 100%
rename from phoenix/cocoa/widget/widget.hpp
rename to hiro/cocoa/widget/widget.hpp
diff --git a/phoenix/cocoa/window.cpp b/hiro/cocoa/window.cpp
similarity index 100%
rename from phoenix/cocoa/window.cpp
rename to hiro/cocoa/window.cpp
diff --git a/phoenix/cocoa/window.hpp b/hiro/cocoa/window.hpp
similarity index 100%
rename from phoenix/cocoa/window.hpp
rename to hiro/cocoa/window.hpp
diff --git a/hiro/core/action/action.cpp b/hiro/core/action/action.cpp
new file mode 100644
index 00000000..c4199296
--- /dev/null
+++ b/hiro/core/action/action.cpp
@@ -0,0 +1,12 @@
+auto mAction::allocate() -> pObject* {
+  return new pAction(*this);
+}
+
+//
+
+auto mAction::remove() -> type& {
+  if(auto menu = parentMenu()) menu->remove(*this);
+  if(auto menuBar = parentMenuBar()) menuBar->remove((mMenu&)*this);
+  if(auto popupMenu = parentPopupMenu()) popupMenu->remove(*this);
+  return *this;
+}
diff --git a/hiro/core/action/menu-check-item.cpp b/hiro/core/action/menu-check-item.cpp
new file mode 100644
index 00000000..4b2d6392
--- /dev/null
+++ b/hiro/core/action/menu-check-item.cpp
@@ -0,0 +1,33 @@
+auto mMenuCheckItem::allocate() -> pObject* {
+  return new pMenuCheckItem(*this);
+}
+
+//
+
+auto mMenuCheckItem::checked() const -> bool {
+  return state.checked;
+}
+
+auto mMenuCheckItem::doToggle() const -> void {
+  if(state.onToggle) return state.onToggle();
+}
+
+auto mMenuCheckItem::onToggle(const function<void ()>& function) -> type& {
+  state.onToggle = function;
+  return *this;
+}
+
+auto mMenuCheckItem::setChecked(bool checked) -> type& {
+  state.checked = checked;
+  signal(setChecked, checked);
+}
+
+auto mMenuCheckItem::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mMenuCheckItem::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/action/menu-item.cpp b/hiro/core/action/menu-item.cpp
new file mode 100644
index 00000000..25a8ad8f
--- /dev/null
+++ b/hiro/core/action/menu-item.cpp
@@ -0,0 +1,34 @@
+auto mMenuItem::allocate() -> pObject* {
+  return new pMenuItem(*this);
+}
+
+//
+
+auto mMenuItem::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mMenuItem::icon() const -> image {
+  return state.icon;
+}
+
+auto mMenuItem::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mMenuItem::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mMenuItem::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mMenuItem::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/action/menu-radio-item.cpp b/hiro/core/action/menu-radio-item.cpp
new file mode 100644
index 00000000..c4d940b8
--- /dev/null
+++ b/hiro/core/action/menu-radio-item.cpp
@@ -0,0 +1,51 @@
+auto mMenuRadioItem::allocate() -> pObject* {
+  return new pMenuRadioItem(*this);
+}
+
+//
+
+auto mMenuRadioItem::group(const vector<shared_pointer_weak<mMenuRadioItem>>& group) -> void {
+  for(auto& weak : group) {
+    if(auto item = weak.acquire()) item->state.group = group;
+  }
+  for(auto& weak : group) {
+    if(auto item = weak.acquire()) {
+      if(item->self()) item->self()->setGroup(group);
+    }
+  }
+  if(group.size()) {
+    if(auto item = group.first().acquire()) item->setChecked();
+  }
+}
+
+auto mMenuRadioItem::checked() const -> bool {
+  return state.checked;
+}
+
+auto mMenuRadioItem::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mMenuRadioItem::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mMenuRadioItem::setChecked() -> type& {
+  for(auto& weak : state.group) {
+    if(auto item = weak.acquire()) item->state.checked = false;
+  }
+  state.checked = true;
+  signal(setChecked);
+  return *this;
+}
+
+auto mMenuRadioItem::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mMenuRadioItem::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/action/menu-separator.cpp b/hiro/core/action/menu-separator.cpp
new file mode 100644
index 00000000..98246b1b
--- /dev/null
+++ b/hiro/core/action/menu-separator.cpp
@@ -0,0 +1,3 @@
+auto mMenuSeparator::allocate() -> pObject* {
+  return new pMenuSeparator(*this);
+}
diff --git a/hiro/core/action/menu.cpp b/hiro/core/action/menu.cpp
new file mode 100644
index 00000000..4cfeec33
--- /dev/null
+++ b/hiro/core/action/menu.cpp
@@ -0,0 +1,60 @@
+auto mMenu::allocate() -> pObject* {
+  return new pMenu(*this);
+}
+
+auto mMenu::destruct() -> void {
+  for(auto& action : state.actions) action->destruct();
+  mAction::destruct();
+}
+
+//
+
+auto mMenu::action(unsigned position) const -> sAction {
+  if(position >= actions()) throw;
+  return state.actions[position];
+}
+
+auto mMenu::actions() const -> unsigned {
+  return state.actions.size();
+}
+
+auto mMenu::append(sAction action) -> type& {
+  state.actions.append(action);
+  action->setParent(this, actions() - 1);
+  signal(append, *action);
+  return *this;
+}
+
+auto mMenu::icon() const -> image {
+  return state.icon;
+}
+
+auto mMenu::remove(sAction action) -> type& {
+  signal(remove, *action);
+  state.actions.remove(action->offset());
+  for(auto n : range(action->offset(), actions())) {
+    state.actions[n]->offset(-1);
+  }
+  action->setParent();
+}
+
+auto mMenu::reset() -> type& {
+  while(state.actions) remove(state.actions.last());
+  return *this;
+}
+
+auto mMenu::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mMenu::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mMenu::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/application.cpp b/hiro/core/application.cpp
new file mode 100644
index 00000000..d6597aad
--- /dev/null
+++ b/hiro/core/application.cpp
@@ -0,0 +1,97 @@
+auto Application::doMain() -> void {
+  if(state.onMain) return state.onMain();
+}
+
+auto Application::font() -> string {
+  return state.font;
+}
+
+auto Application::name() -> string {
+  return state.name;
+}
+
+auto Application::onMain(const nall::function<void ()>& function) -> void {
+  state.onMain = function;
+}
+
+auto Application::run() -> void {
+  return pApplication::run();
+}
+
+auto Application::pendingEvents() -> bool {
+  return pApplication::pendingEvents();
+}
+
+auto Application::processEvents() -> void {
+  return pApplication::processEvents();
+}
+
+auto Application::quit() -> void {
+  state.quit = true;
+  return pApplication::quit();
+}
+
+auto Application::setFont(const string& font) -> void {
+  state.font = font;
+}
+
+auto Application::setName(const string& name) -> void {
+  state.name = name;
+}
+
+//Windows
+//=======
+
+auto Application::Windows::doModalChange(bool modal) -> void {
+  if(state.windows.onModalChange) return state.windows.onModalChange(modal);
+}
+
+auto Application::Windows::onModalChange(const function<void (bool)>& function) -> void {
+  state.windows.onModalChange = function;
+}
+
+//Cocoa
+//=====
+
+auto Application::Cocoa::doAbout() -> void {
+  if(state.cocoa.onAbout) return state.cocoa.onAbout();
+}
+
+auto Application::Cocoa::doActivate() -> void {
+  if(state.cocoa.onActivate) return state.cocoa.onActivate();
+}
+
+auto Application::Cocoa::doPreferences() -> void {
+  if(state.cocoa.onPreferences) return state.cocoa.onPreferences();
+}
+
+auto Application::Cocoa::doQuit() -> void {
+  if(state.cocoa.onQuit) return state.cocoa.onQuit();
+}
+
+auto Application::Cocoa::onAbout(const function<void ()>& function) -> void {
+  state.cocoa.onAbout = function;
+}
+
+auto Application::Cocoa::onActivate(const function<void ()>& function) -> void {
+  state.cocoa.onActivate = function;
+}
+
+auto Application::Cocoa::onPreferences(const function<void ()>& function) -> void {
+  state.cocoa.onPreferences = function;
+}
+
+auto Application::Cocoa::onQuit(const function<void ()>& function) -> void {
+  state.cocoa.onQuit = function;
+}
+
+//Internal
+//========
+
+auto Application::initialize() -> void {
+  static bool initialized = false;
+  if(initialized == false) {
+    initialized = true;
+    return pApplication::initialize();
+  }
+}
diff --git a/hiro/core/browser-window.cpp b/hiro/core/browser-window.cpp
new file mode 100644
index 00000000..7d24a1c5
--- /dev/null
+++ b/hiro/core/browser-window.cpp
@@ -0,0 +1,31 @@
+auto BrowserWindow::directory() -> string {
+  return pBrowserWindow::directory(state);
+}
+
+auto BrowserWindow::open() -> string {
+  return pBrowserWindow::open(state);
+}
+
+auto BrowserWindow::save() -> string {
+  return pBrowserWindow::save(state);
+}
+
+auto BrowserWindow::setFilters(const lstring& filters) -> type& {
+  state.filters = filters;
+  return *this;
+}
+
+auto BrowserWindow::setParent(shared_pointer<mWindow> parent) -> type& {
+  state.parent = parent;
+  return *this;
+}
+
+auto BrowserWindow::setPath(const string& path) -> type& {
+  state.path = path;
+  return *this;
+}
+
+auto BrowserWindow::setTitle(const string& title) -> type& {
+  state.title = title;
+  return *this;
+}
diff --git a/hiro/core/color.cpp b/hiro/core/color.cpp
new file mode 100644
index 00000000..dd43696c
--- /dev/null
+++ b/hiro/core/color.cpp
@@ -0,0 +1,83 @@
+Color::Color() {
+  setColor(0, 0, 0, 0);
+}
+
+Color::Color(signed red, signed green, signed blue) {
+  setColor(255, red, green, blue);
+}
+
+Color::Color(signed alpha, signed red, signed green, signed blue) {
+  setColor(alpha, red, green, blue);
+}
+
+Color::operator bool() const {
+  return !empty();
+}
+
+auto Color::operator==(const Color& source) const -> bool {
+  return alpha() == source.alpha() && red() == source.red() && green() == source.green() && blue() == source.blue();
+}
+
+auto Color::operator!=(const Color& source) const -> bool {
+  return !operator==(source);
+}
+
+auto Color::alpha() const -> uint8_t {
+  return state.alpha;
+}
+
+auto Color::blue() const -> uint8_t {
+  return state.blue;
+}
+
+auto Color::empty() const -> bool {
+  return state.alpha == 0 && state.red == 0 && state.green == 0 && state.blue == 0;
+}
+
+auto Color::green() const -> uint8_t {
+  return state.green;
+}
+
+auto Color::red() const -> uint8_t {
+  return state.red;
+}
+
+auto Color::setAlpha(signed alpha) -> type& {
+  state.alpha = max(0, min(255, alpha));
+  return *this;
+}
+
+auto Color::setBlue(signed blue) -> type& {
+  state.blue = max(0, min(255, blue));
+  return *this;
+}
+
+auto Color::setColor(Color color) -> type& {
+  return setColor(color.alpha(), color.red(), color.green(), color.blue());
+}
+
+auto Color::setColor(signed red, signed green, signed blue) -> type& {
+  return setColor(255, red, green, blue);
+}
+
+auto Color::setColor(signed alpha, signed red, signed green, signed blue) -> type& {
+  state.alpha = max(0, min(255, alpha));
+  state.red   = max(0, min(255, red  ));
+  state.green = max(0, min(255, green));
+  state.blue  = max(0, min(255, blue ));
+  return *this;
+}
+
+auto Color::setGreen(signed green) -> type& {
+  state.green = max(0, min(255, green));
+  return *this;
+}
+
+auto Color::setRed(signed red) -> type& {
+  state.red = max(0, min(255, red));
+  return *this;
+}
+
+auto Color::value() const -> uint32_t {
+  return (state.alpha << 24) + (state.red << 16) + (state.green << 8) + (state.blue << 0);
+}
diff --git a/hiro/core/core.cpp b/hiro/core/core.cpp
new file mode 100644
index 00000000..531b94d7
--- /dev/null
+++ b/hiro/core/core.cpp
@@ -0,0 +1,100 @@
+#if defined(HIRO_WINDOWS)
+  #include "../windows/header.hpp"
+#elif defined(HIRO_QT)
+  #include "../qt/header.hpp"
+#elif defined(HIRO_GTK)
+  #include "../gtk/header.hpp"
+#elif defined(HIRO_COCOA)
+  #include "../cocoa/header.hpp"
+#elif defined(HIRO_REFERENCE)
+  #include "../reference/header.hpp"
+#endif
+
+#include "core.hpp"
+using namespace nall;
+
+namespace hiro {
+  Application::State Application::state;
+  Keyboard::State Keyboard::state;
+}
+
+#if defined(HIRO_WINDOWS)
+  #include "../windows/platform.cpp"
+#elif defined(HIRO_QT)
+  #include "../qt/platform.cpp"
+#elif defined(HIRO_GTK)
+  #include "../gtk/platform.cpp"
+#elif defined(HIRO_COCOA)
+  #include "../cocoa/platform.cpp"
+#elif defined(HIRO_REFERENCE)
+  #include "../reference/platform.cpp"
+#endif
+
+#define signal(function, ...) \
+  (delegate ? self()->function(__VA_ARGS__) : decltype(self()->function(__VA_ARGS__))())
+
+namespace hiro {
+  #include "application.cpp"
+  #include "color.cpp"
+  #include "position.cpp"
+  #include "size.cpp"
+  #include "geometry.cpp"
+  #include "font.cpp"
+  #include "desktop.cpp"
+  #include "monitor.cpp"
+  #include "keyboard.cpp"
+  #include "mouse.cpp"
+  #include "browser-window.cpp"
+  #include "message-window.cpp"
+  #include "object.cpp"
+  #include "hotkey.cpp"
+  #include "timer.cpp"
+  #include "window.cpp"
+  #include "status-bar.cpp"
+  #include "menu-bar.cpp"
+  #include "popup-menu.cpp"
+
+  #include "action/action.cpp"
+  #include "action/menu.cpp"
+  #include "action/menu-separator.cpp"
+  #include "action/menu-item.cpp"
+  #include "action/menu-check-item.cpp"
+  #include "action/menu-radio-item.cpp"
+
+  #include "sizable.cpp"
+  #include "layout.cpp"
+
+  #include "widget/widget.cpp"
+  #include "widget/button.cpp"
+  #include "widget/canvas.cpp"
+  #include "widget/check-button.cpp"
+  #include "widget/check-label.cpp"
+  #include "widget/combo-button.cpp"
+  #include "widget/combo-button-item.cpp"
+  #include "widget/console.cpp"
+  #include "widget/frame.cpp"
+  #include "widget/hex-edit.cpp"
+  #include "widget/horizontal-scroller.cpp"
+  #include "widget/horizontal-slider.cpp"
+  #include "widget/icon-view.cpp"
+  #include "widget/icon-view-item.cpp"
+  #include "widget/label.cpp"
+  #include "widget/line-edit.cpp"
+  #include "widget/list-view.cpp"
+  #include "widget/list-view-column.cpp"
+  #include "widget/list-view-item.cpp"
+  #include "widget/progress-bar.cpp"
+  #include "widget/radio-button.cpp"
+  #include "widget/radio-label.cpp"
+  #include "widget/source-edit.cpp"
+  #include "widget/tab-frame.cpp"
+  #include "widget/tab-frame-item.cpp"
+  #include "widget/text-edit.cpp"
+  #include "widget/tree-view.cpp"
+  #include "widget/tree-view-item.cpp"
+  #include "widget/vertical-scroller.cpp"
+  #include "widget/vertical-slider.cpp"
+  #include "widget/viewport.cpp"
+}
+
+#undef signal
diff --git a/hiro/core/core.hpp b/hiro/core/core.hpp
new file mode 100644
index 00000000..47e3d6e1
--- /dev/null
+++ b/hiro/core/core.hpp
@@ -0,0 +1,1571 @@
+#include <nall/platform.hpp>
+#include <nall/config.hpp>
+#include <nall/directory.hpp>
+#include <nall/function.hpp>
+#include <nall/image.hpp>
+#include <nall/maybe.hpp>
+#include <nall/range.hpp>
+#include <nall/shared-pointer.hpp>
+#include <nall/stdint.hpp>
+#include <nall/string.hpp>
+#include <nall/traits.hpp>
+#include <nall/utility.hpp>
+#include <nall/vector.hpp>
+
+using nall::function;
+using nall::image;
+using nall::lstring;
+using nall::maybe;
+using nall::shared_pointer;
+using nall::shared_pointer_weak;
+using nall::string;
+using nall::vector;
+
+namespace hiro {
+
+#define Declare(Name) \
+  struct m##Name; \
+  struct p##Name; \
+  using s##Name = shared_pointer<m##Name>; \
+  using w##Name = shared_pointer_weak<m##Name>; \
+
+Declare(Keyboard)
+Declare(Object)
+Declare(Timer)
+Declare(Hotkey)
+Declare(Window)
+Declare(StatusBar)
+Declare(MenuBar)
+Declare(PopupMenu)
+Declare(Action)
+Declare(Menu)
+Declare(MenuSeparator)
+Declare(MenuItem)
+Declare(MenuCheckItem)
+Declare(MenuRadioItem)
+Declare(Sizable)
+Declare(Layout)
+Declare(Widget)
+Declare(Button)
+Declare(Canvas)
+Declare(CheckButton)
+Declare(CheckLabel)
+Declare(ComboButton)
+Declare(ComboButtonItem)
+Declare(Console)
+Declare(Frame)
+Declare(HexEdit)
+Declare(HorizontalScroller)
+Declare(HorizontalSlider)
+Declare(IconView)
+Declare(IconViewItem)
+Declare(Label)
+Declare(LineEdit)
+Declare(ListView)
+Declare(ListViewColumn)
+Declare(ListViewItem)
+Declare(ProgressBar)
+Declare(RadioButton)
+Declare(RadioLabel)
+Declare(SourceEdit)
+Declare(TabFrame)
+Declare(TabFrameItem)
+Declare(TextEdit)
+Declare(TreeView)
+Declare(TreeViewItem)
+Declare(VerticalScroller)
+Declare(VerticalSlider)
+Declare(Viewport)
+
+#undef Declare
+
+enum class Edge : unsigned { Top, Bottom, Left, Right };
+
+enum class Orientation : unsigned { Horizontal, Vertical };
+
+struct Application {
+  Application() = delete;
+
+  static auto doMain() -> void;
+  static auto font() -> string;
+  static auto name() -> string;
+  static auto onMain(const function<void ()>& function = {}) -> void;
+  static auto run() -> void;
+  static auto pendingEvents() -> bool;
+  static auto processEvents() -> void;
+  static auto quit() -> void;
+  static auto setFont(const string& font = "") -> void;
+  static auto setName(const string& name = "") -> void;
+
+  struct Windows {
+    static auto doModalChange(bool modal) -> void;
+    static auto onModalChange(const function<void (bool)>& function = {}) -> void;
+  };
+
+  struct Cocoa {
+    static auto doAbout() -> void;
+    static auto doActivate() -> void;
+    static auto doPreferences() -> void;
+    static auto doQuit() -> void;
+    static auto onAbout(const function<void ()>& function = {}) -> void;
+    static auto onActivate(const function<void ()>& function = {}) -> void;
+    static auto onPreferences(const function<void ()>& function = {}) -> void;
+    static auto onQuit(const function<void ()>& function = {}) -> void;
+  };
+
+//private:
+  struct State {
+    string font;
+    string name;
+    function<void ()> onMain;
+    bool quit = false;
+
+    struct Windows {
+      function<void (bool)> onModalChange;
+    } windows;
+
+    struct Cocoa {
+      function<void ()> onAbout;
+      function<void ()> onActivate;
+      function<void ()> onPreferences;
+      function<void ()> onQuit;
+    } cocoa;
+  };
+  static State state;
+  static auto initialize() -> void;
+};
+
+struct Color {
+  using type = Color;
+
+  Color();
+  Color(signed red, signed green, signed blue);
+  Color(signed alpha, signed red, signed green, signed blue);
+
+  explicit operator bool() const;
+  auto operator==(const Color& source) const -> bool;
+  auto operator!=(const Color& source) const -> bool;
+
+  auto alpha() const -> uint8_t;
+  auto blue() const -> uint8_t;
+  auto empty() const -> bool;
+  auto green() const -> uint8_t;
+  auto red() const -> uint8_t;
+  auto setAlpha(signed alpha) -> type&;
+  auto setBlue(signed blue) -> type&;
+  auto setColor(Color color = {}) -> type&;
+  auto setColor(signed red, signed green, signed blue) -> type&;
+  auto setColor(signed alpha, signed red, signed green, signed blue) -> type&;
+  auto setGreen(signed green) -> type&;
+  auto setRed(signed red) -> type&;
+  auto value() const -> uint32_t;
+
+//private:
+  struct State {
+    signed alpha;
+    signed red;
+    signed green;
+    signed blue;
+  } state;
+};
+
+struct Position {
+  using type = Position;
+
+  Position();
+  Position(signed x, signed y);
+
+  auto operator==(const Position& source) const -> bool;
+  auto operator!=(const Position& source) const -> bool;
+
+  auto setPosition(Position position = {}) -> type&;
+  auto setPosition(signed x, signed y) -> type&;
+  auto setX(signed x) -> type&;
+  auto setY(signed y) -> type&;
+  auto x() const -> signed;
+  auto y() const -> signed;
+
+//private:
+  struct State {
+    signed x;
+    signed y;
+  } state;
+};
+
+struct Size {
+  using type = Size;
+
+  Size();
+  Size(signed width, signed height);
+
+  auto operator==(const Size& source) const -> bool;
+  auto operator!=(const Size& source) const -> bool;
+
+  auto height() const -> signed;
+  auto setHeight(signed height) -> type&;
+  auto setSize(Size source = {}) -> type&;
+  auto setSize(signed width, signed height) -> type&;
+  auto setWidth(signed width) -> type&;
+  auto width() const -> signed;
+
+  static const signed Maximum = ~0;  //~0 == -1
+  static const signed Minimum =  0;
+
+//private:
+  struct State {
+    signed width;
+    signed height;
+  } state;
+};
+
+struct Geometry {
+  using type = Geometry;
+
+  Geometry();
+  Geometry(Position position, Size size);
+  Geometry(signed x, signed y, signed width, signed height);
+  Geometry(const string& text);
+
+  auto operator==(const Geometry& source) const -> bool;
+  auto operator!=(const Geometry& source) const -> bool;
+
+  auto height() const -> signed;
+  auto position() const -> Position;
+  auto setGeometry(Geometry geometry = {}) -> type&;
+  auto setGeometry(Position position, Size size) -> type&;
+  auto setGeometry(signed x, signed y, signed width, signed height) -> type&;
+  auto setHeight(signed height) -> type&;
+  auto setPosition(Position position = {}) -> type&;
+  auto setPosition(signed x, signed y) -> type&;
+  auto setSize(Size size = {}) -> type&;
+  auto setSize(signed width, signed height) -> type&;
+  auto setWidth(signed width) -> type&;
+  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;
+
+//private:
+  struct State {
+    signed x;
+    signed y;
+    signed width;
+    signed height;
+  } state;
+};
+
+struct Font {
+  Font() = delete;
+
+  static auto serif(unsigned size = 0, const string& style = "") -> string;
+  static auto sans(unsigned size = 0, const string& style = "") -> string;
+  static auto monospace(unsigned size = 0, const string& style = "") -> string;
+  static auto size(const string& font, const string& text = " ") -> Size;
+};
+
+struct Desktop {
+  Desktop() = delete;
+
+  static auto size() -> Size;
+  static auto workspace() -> Geometry;
+};
+
+struct Monitor {
+  Monitor() = delete;
+
+  static auto count() -> unsigned;
+  static auto geometry(unsigned monitor) -> Geometry;
+  static auto primary() -> unsigned;
+};
+
+struct Keyboard {
+  Keyboard() = delete;
+
+  static auto append(sHotkey hotkey) -> void;
+  static auto hotkey(unsigned position) -> sHotkey;
+  static auto hotkeys() -> unsigned;
+  static auto poll() -> vector<bool>;
+  static auto pressed(const string& key) -> bool;
+  static auto released(const string& key) -> bool;
+  static auto remove(sHotkey hotkey) -> void;
+
+  static const vector<string> keys;
+
+//private:
+  struct State {
+    vector<sHotkey> hotkeys;
+  };
+  static State state;
+};
+
+struct Mouse {
+  enum class Button : unsigned { Left, Middle, Right };
+
+  Mouse() = delete;
+
+  static auto position() -> Position;
+  static auto pressed(Button) -> bool;
+  static auto released(Button) -> bool;
+};
+
+struct BrowserWindow {
+  using type = BrowserWindow;
+
+  auto directory() -> string;
+  auto open() -> string;
+  auto save() -> string;
+  auto setFilters(const lstring& filters = {"*"}) -> type&;
+  auto setParent(sWindow parent) -> type&;
+  auto setPath(const string& path = "") -> type&;
+  auto setTitle(const string& title = "") -> type&;
+
+//private:
+  struct State {
+    lstring filters;
+    sWindow parent;
+    string path;
+    string title;
+  } state;
+};
+
+struct MessageWindow {
+  enum class Buttons : unsigned { Ok, OkCancel, YesNo, YesNoCancel };
+  enum class Response : unsigned { Ok, Cancel, Yes, No };
+
+  using type = MessageWindow;
+
+  MessageWindow(const string& text = "");
+
+  auto error(Buttons = Buttons::Ok) -> Response;
+  auto information(Buttons = Buttons::Ok) -> Response;
+  auto question(Buttons = Buttons::YesNo) -> Response;
+  auto setParent(sWindow parent) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto setTitle(const string& title = "") -> type&;
+  auto warning(Buttons = Buttons::Ok) -> Response;
+
+//private:
+  struct State {
+    MessageWindow::Buttons buttons = MessageWindow::Buttons::Ok;
+    sWindow parent;
+    string text;
+    string title;
+  } state;
+};
+
+#define Declare(Name) \
+  using type = m##Name; \
+  operator s##Name() const { return instance; } \
+  auto self() -> p##Name* { return (p##Name*)delegate; } \
+  auto self() const -> const p##Name* { return (const p##Name*)delegate; } \
+  auto bind(const s##Name& instance) -> void { \
+    this->instance = instance; \
+    if(!abstract()) construct(); \
+  } \
+  auto unbind() -> void { \
+    reset(); \
+    destruct(); \
+    instance.reset(); \
+  } \
+  virtual auto allocate() -> pObject*; \
+
+struct mObject {
+  Declare(Object)
+
+  mObject();
+  virtual ~mObject();
+  mObject(const mObject&) = delete;
+  mObject& operator=(const mObject&) = delete;
+
+  auto abstract() const -> bool;
+  auto enabled(bool recursive = false) const -> bool;
+  virtual auto focused() const -> bool;
+  auto font(bool recursive = false) const -> string;
+  auto offset() const -> signed;
+  auto offset(signed displacement) -> type&;
+  auto parent() const -> mObject*;
+  auto parentComboButton(bool recursive = false) const -> mComboButton*;
+  auto parentFrame(bool recursive = false) const -> mFrame*;
+  auto parentIconView(bool recursive = false) const -> mIconView*;
+  auto parentLayout(bool recursive = false) const -> mLayout*;
+  auto parentListView(bool recursive = false) const -> mListView*;
+  auto parentMenu(bool recursive = false) const -> mMenu*;
+  auto parentMenuBar(bool recursive = false) const -> mMenuBar*;
+  auto parentPopupMenu(bool recursive = false) const -> mPopupMenu*;
+  auto parentSizable(bool recursive = false) const -> mSizable*;
+  auto parentTabFrame(bool recursive = false) const -> mTabFrame*;
+  auto parentTabFrameItem(bool recursive = false) const -> mTabFrameItem*;
+  auto parentTreeView(bool recursive = false) const -> mTreeView*;
+  auto parentTreeViewItem(bool recursive = false) const -> mTreeViewItem*;
+  auto parentWidget(bool recursive = false) const -> mWidget*;
+  auto parentWindow(bool recursive = false) const -> mWindow*;
+  virtual auto remove() -> type&;
+  virtual auto reset() -> type&;
+  virtual auto setEnabled(bool enabled = true) -> type&;
+  virtual auto setFocused() -> type&;
+  virtual auto setFont(const string& font = "") -> type&;
+  virtual auto setParent(mObject* parent = nullptr, signed offset = -1) -> type&;
+  virtual auto setVisible(bool visible = true) -> type&;
+  auto visible(bool recursive = false) const -> bool;
+
+//private:
+  struct State {
+    bool enabled = true;
+    string font;
+    signed offset = -1;
+    mObject* parent = nullptr;
+    bool visible = true;
+  } state;
+
+  wObject instance;
+  pObject* delegate = nullptr;
+
+  virtual auto construct() -> void;
+  virtual auto destruct() -> void;
+};
+
+struct mHotkey : mObject {
+  Declare(Hotkey)
+
+  auto doPress() const -> void;
+  auto doRelease() const -> void;
+  auto onPress(const function<void ()>& function = {}) -> type&;
+  auto onRelease(const function<void ()>& function = {}) -> type&;
+  auto parent() const -> wObject;
+  auto remove() -> type& override;
+  auto sequence() const -> string;
+  auto setParent(sObject parent) -> type&;
+  auto setSequence(const string& sequence = "") -> type&;
+
+//private:
+  struct State {
+    bool active = false;
+    vector<unsigned> keys;
+    function<void ()> onPress;
+    function<void ()> onRelease;
+    wObject parent;
+    string sequence;
+  } state;
+};
+
+struct mTimer : mObject {
+  Declare(Timer)
+
+  auto doActivate() const -> void;
+  auto interval() const -> unsigned;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto setInterval(unsigned interval = 0) -> type&;
+
+//private:
+  struct State {
+    unsigned interval = 0;
+    function<void ()> onActivate;
+  } state;
+};
+
+struct mWindow : mObject {
+  Declare(Window)
+  using mObject::remove;
+
+  auto append(sLayout layout) -> type&;
+  auto append(sMenuBar menuBar) -> type&;
+  auto append(sStatusBar statusBar) -> type&;
+  auto backgroundColor() const -> Color;
+  auto doClose() const -> void;
+  auto doDrop(lstring) const -> void;
+  auto doKeyPress(signed) const -> void;
+  auto doKeyRelease(signed) const -> void;
+  auto doMove() const -> void;
+  auto doSize() const -> void;
+  auto droppable() const -> bool;
+  auto frameGeometry() const -> Geometry;
+  auto fullScreen() const -> bool;
+  auto geometry() const -> Geometry;
+  auto layout() const -> sLayout;
+  auto menuBar() const -> sMenuBar;
+  auto modal() const -> bool;
+  auto onClose(const function<void ()>& function = {}) -> type&;
+  auto onDrop(const function<void (lstring)>& function = {}) -> type&;
+  auto onKeyPress(const function<void (signed)>& function = {}) -> type&;
+  auto onKeyRelease(const function<void (signed)>& function = {}) -> type&;
+  auto onMove(const function<void ()>& function = {}) -> type&;
+  auto onSize(const function<void ()>& function = {}) -> type&;
+  auto remove(sLayout layout) -> type&;
+  auto remove(sMenuBar menuBar) -> type&;
+  auto remove(sStatusBar statusBar) -> type&;
+  auto reset() -> type& override;
+  auto resizable() const -> bool;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setCentered() -> type&;
+  auto setCentered(sWindow parent) -> type&;
+  auto setDroppable(bool droppable = true) -> type&;
+  auto setFrameGeometry(Geometry geometry) -> type&;
+  auto setFramePosition(Position position) -> type&;
+  auto setFrameSize(Size size) -> type&;
+  auto setFullScreen(bool fullScreen = true) -> type&;
+  auto setGeometry(Geometry geometry) -> type&;
+  auto setModal(bool modal = true) -> type&;
+  auto setPosition(Position position) -> type&;
+  auto setResizable(bool resizable = true) -> type&;
+  auto setSize(Size size) -> type&;
+  auto setTitle(const string& title = "") -> type&;
+  auto statusBar() const -> sStatusBar;
+  auto title() const -> string;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    bool droppable = false;
+    bool fullScreen = false;
+    Geometry geometry = {128, 128, 256, 256};
+    sLayout layout;
+    sMenuBar menuBar;
+    bool modal = false;
+    function<void ()> onClose;
+    function<void (lstring)> onDrop;
+    function<void (signed)> onKeyPress;
+    function<void (signed)> onKeyRelease;
+    function<void ()> onMove;
+    function<void ()> onSize;
+    bool resizable = true;
+    sStatusBar statusBar;
+    string title;
+  } state;
+
+  auto destruct() -> void;
+};
+
+struct mStatusBar : mObject {
+  Declare(StatusBar)
+
+  auto remove() -> type& override;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    string text;
+  } state;
+};
+
+struct mMenuBar : mObject {
+  Declare(MenuBar)
+
+  auto append(sMenu menu) -> type&;
+  auto menu(unsigned position) const -> sMenu;
+  auto menus() const -> unsigned;
+  auto remove() -> type& override;
+  auto remove(sMenu menu) -> type&;
+  auto reset() -> type&;
+
+//private:
+  struct State {
+    vector<sMenu> menus;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mPopupMenu : mObject {
+  Declare(PopupMenu)
+  using mObject::remove;
+
+  auto action(unsigned position) const -> sAction;
+  auto actions() const -> unsigned;
+  auto append(sAction action) -> type&;
+  auto remove(sAction action) -> type&;
+  auto reset() -> type&;
+  auto setVisible(bool visible = true) -> type& override;
+
+//private:
+  struct State {
+    vector<sAction> actions;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mAction : mObject {
+  Declare(Action)
+
+  auto remove() -> type& override;
+
+//private:
+  struct State {
+  } state;
+};
+
+struct mMenu : mAction {
+  Declare(Menu)
+  using mObject::remove;
+
+  auto action(unsigned position) const -> sAction;
+  auto actions() const -> unsigned;
+  auto append(sAction action) -> type&;
+  auto icon() const -> image;
+  auto remove(sAction action) -> type&;
+  auto reset() -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    vector<sAction> actions;
+    image icon;
+    string text;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mMenuSeparator : mAction {
+  Declare(MenuSeparator)
+
+//private:
+  struct State {
+  } state;
+};
+
+struct mMenuItem : mAction {
+  Declare(MenuItem)
+
+  auto doActivate() const -> void;
+  auto icon() const -> image;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    image icon;
+    function<void ()> onActivate;
+    string text;
+  } state;
+};
+
+struct mMenuCheckItem : mAction {
+  Declare(MenuCheckItem)
+
+  auto checked() const -> bool;
+  auto doToggle() const -> void;
+  auto onToggle(const function<void ()>& function = {}) -> type&;
+  auto setChecked(bool checked = true) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    bool checked = false;
+    function<void ()> onToggle;
+    string text;
+  } state;
+};
+
+struct mMenuRadioItem : mAction {
+  Declare(MenuRadioItem)
+
+  auto checked() const -> bool;
+  auto doActivate() const -> void;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto setChecked() -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+  static auto group(const vector<wMenuRadioItem>& group) -> void;
+
+//private:
+  struct State {
+    bool checked = true;
+    vector<wMenuRadioItem> group;
+    function<void ()> onActivate;
+    string text;
+  } state;
+};
+
+struct mSizable : mObject {
+  Declare(Sizable)
+
+  auto geometry() const -> Geometry;
+  virtual auto minimumSize() const -> Size;
+  virtual auto setGeometry(Geometry geometry) -> type&;
+
+//private:
+  struct State {
+    Geometry geometry;
+  } state;
+};
+
+struct mLayout : mSizable {
+  Declare(Layout)
+
+  virtual auto append(sSizable sizable) -> type&;
+  virtual auto remove() -> type& override;
+  virtual auto remove(sSizable sizable) -> type&;
+  virtual auto reset() -> type&;
+  auto setParent(mObject* parent = nullptr, signed offset = -1) -> type& override;
+  auto sizable(unsigned position) const -> sSizable;
+  auto sizables() const -> unsigned;
+
+//private:
+  struct State {
+    vector<sSizable> sizables;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mWidget : mSizable {
+  Declare(Widget)
+
+  auto doSize() const -> void;
+  auto onSize(const function<void ()>& function = {}) -> type&;
+  auto remove() -> type& override;
+
+//private:
+  struct State {
+    function<void ()> onSize;
+  } state;
+};
+
+struct mButton : mWidget {
+  Declare(Button)
+
+  auto bordered() const -> bool;
+  auto doActivate() const -> void;
+  auto icon() const -> image;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto orientation() const -> Orientation;
+  auto setBordered(bool bordered = true) -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    bool bordered = true;
+    image icon;
+    function<void ()> onActivate;
+    Orientation orientation = Orientation::Horizontal;
+    string text;
+  } state;
+};
+
+struct mCanvas : mWidget {
+  Declare(Canvas)
+
+  auto color() const -> Color;
+  auto data() -> uint32_t*;
+  auto droppable() const -> bool;
+  auto doDrop(lstring names) const -> void;
+  auto doMouseLeave() const -> void;
+  auto doMouseMove(Position position) const -> void;
+  auto doMousePress(Mouse::Button button) const -> void;
+  auto doMouseRelease(Mouse::Button button) const -> void;
+  auto gradient() const -> vector<Color>;
+  auto icon() const -> image;
+  auto onDrop(const function<void (lstring)>& function = {}) -> type&;
+  auto onMouseLeave(const function<void ()>& function = {}) -> type&;
+  auto onMouseMove(const function<void (Position)>& function = {}) -> type&;
+  auto onMousePress(const function<void (Mouse::Button)>& function = {}) -> type&;
+  auto onMouseRelease(const function<void (Mouse::Button)>& function = {}) -> type&;
+  auto setColor(Color color) -> type&;
+  auto setData(Size size) -> type&;
+  auto setDroppable(bool droppable = true) -> type&;
+  auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> type&;
+  auto setHorizontalGradient(Color left, Color right) -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setVerticalGradient(Color top, Color bottom) -> type&;
+  auto size() const -> Size;
+  auto update() -> type&;
+
+//private:
+  struct State {
+    Color color;
+    vector<uint32_t> data;
+    bool droppable = false;
+    vector<Color> gradient = {{}, {}, {}, {}};
+    image icon;
+    function<void (lstring)> onDrop;
+    function<void ()> onMouseLeave;
+    function<void (Position)> onMouseMove;
+    function<void (Mouse::Button)> onMousePress;
+    function<void (Mouse::Button)> onMouseRelease;
+    Size size;
+  } state;
+};
+
+struct mCheckButton : mWidget {
+  Declare(CheckButton)
+
+  auto bordered() const -> bool;
+  auto checked() const -> bool;
+  auto doToggle() const -> void;
+  auto icon() const -> image;
+  auto onToggle(const function<void ()>& function = {}) -> type&;
+  auto orientation() const -> Orientation;
+  auto setBordered(bool bordered = true) -> type&;
+  auto setChecked(bool checked = true) -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    bool bordered = false;
+    bool checked = false;
+    image icon;
+    function<void ()> onToggle;
+    Orientation orientation = Orientation::Horizontal;
+    string text;
+  } state;
+};
+
+struct mCheckLabel : mWidget {
+  Declare(CheckLabel)
+
+  auto checked() const -> bool;
+  auto doToggle() const -> void;
+  auto onToggle(const function<void ()>& function = {}) -> type&;
+  auto setChecked(bool checked = true) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    bool checked = false;
+    function<void ()> onToggle;
+    string text;
+  } state;
+};
+
+struct mComboButton : mWidget {
+  Declare(ComboButton)
+  using mObject::remove;
+
+  auto append(sComboButtonItem item) -> type&;
+  auto doChange() const -> void;
+  auto item(unsigned position) const -> sComboButtonItem;
+  auto items() const -> unsigned;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto remove(sComboButtonItem item) -> type&;
+  auto reset() -> type&;
+  auto selected() const -> sComboButtonItem;
+
+//private:
+  struct State {
+    vector<sComboButtonItem> items;
+    function<void ()> onChange;
+    signed selected = -1;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mComboButtonItem : mObject {
+  Declare(ComboButtonItem)
+
+  auto icon() const -> image;
+  auto remove() -> type& override;
+  auto selected() const -> bool;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setSelected() -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    image icon;
+    string text;
+  } state;
+};
+
+struct mConsole : mWidget {
+  Declare(Console)
+
+  auto backgroundColor() const -> Color;
+  auto doActivate(string) const -> void;
+  auto foregroundColor() const -> Color;
+  auto onActivate(const function<void (string)>& function = {}) -> type&;
+  auto print(const string& text) -> type&;
+  auto prompt() const -> string;
+  auto reset() -> type&;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+  auto setPrompt(const string& prompt = "") -> type&;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    Color foregroundColor;
+    function<void (string)> onActivate;
+    string prompt;
+  } state;
+};
+
+struct mFrame : mWidget {
+  Declare(Frame)
+  using mObject::remove;
+
+  auto append(sLayout layout) -> type&;
+  auto layout() const -> sLayout;
+  auto remove(sLayout layout) -> type&;
+  auto reset() -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    sLayout layout;
+    string text;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mHexEdit : mWidget {
+  Declare(HexEdit)
+
+  auto backgroundColor() const -> Color;
+  auto columns() const -> unsigned;
+  auto doRead(unsigned offset) const -> uint8_t;
+  auto doWrite(unsigned offset, uint8_t data) const -> void;
+  auto foregroundColor() const -> Color;
+  auto length() const -> unsigned;
+  auto offset() const -> unsigned;
+  auto onRead(const function<uint8_t (unsigned)>& function = {}) -> type&;
+  auto onWrite(const function<void (unsigned, uint8_t)>& function = {}) -> type&;
+  auto rows() const -> unsigned;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setColumns(unsigned columns = 16) -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+  auto setLength(unsigned length) -> type&;
+  auto setOffset(unsigned offset) -> type&;
+  auto setRows(unsigned rows = 16) -> type&;
+  auto update() -> type&;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    unsigned columns = 16;
+    Color foregroundColor;
+    unsigned length = 0;
+    unsigned offset = 0;
+    function<uint8_t (unsigned)> onRead;
+    function<void (unsigned, uint8_t)> onWrite;
+    unsigned rows = 16;
+  } state;
+};
+
+struct mHorizontalScroller : mWidget {
+  Declare(HorizontalScroller)
+
+  auto doChange() const -> void;
+  auto length() const -> unsigned;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto position() const -> unsigned;
+  auto setLength(unsigned length = 101) -> type&;
+  auto setPosition(unsigned position = 0) -> type&;
+
+//private:
+  struct State {
+    unsigned length = 101;
+    function<void ()> onChange;
+    unsigned position = 0;
+  } state;
+};
+
+struct mHorizontalSlider : mWidget {
+  Declare(HorizontalSlider)
+
+  auto doChange() const -> void;
+  auto length() const -> unsigned;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto position() const -> unsigned;
+  auto setLength(unsigned length = 101) -> type&;
+  auto setPosition(unsigned position = 0) -> type&;
+
+//private:
+  struct State {
+    unsigned length = 101;
+    function<void ()> onChange;
+    unsigned position = 0;
+  } state;
+};
+
+struct mIconView : mWidget {
+  Declare(IconView)
+  using mObject::remove;
+
+  auto append(sIconViewItem item) -> type&;
+  auto backgroundColor() const -> Color;
+  auto doActivate() const -> void;
+  auto doChange() const -> void;
+  auto doContext() const -> void;
+  auto flow() const -> Orientation;
+  auto foregroundColor() const -> Color;
+  auto item(unsigned position) const -> sIconViewItem;
+  auto items() const -> unsigned;
+  auto multiSelect() const -> bool;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto onContext(const function<void ()>& function = {}) -> type&;
+  auto orientation() const -> Orientation;
+  auto remove(sIconViewItem item) -> type&;
+  auto reset() -> type&;
+  auto selected() const -> maybe<unsigned>;
+  auto selectedItems() const -> vector<unsigned>;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setFlow(Orientation flow = Orientation::Vertical) -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+  auto setMultiSelect(bool multipleSelections = true) -> type&;
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&;
+  auto setSelected(const vector<signed>& selections) -> type&;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    Color foregroundColor;
+    Orientation flow = Orientation::Vertical;
+    vector<sIconViewItem> items;
+    bool multiSelect = false;
+    function<void ()> onActivate;
+    function<void ()> onChange;
+    function<void ()> onContext;
+    Orientation orientation = Orientation::Horizontal;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mIconViewItem : mObject {
+  Declare(IconViewItem)
+
+  auto icon() const -> image;
+  auto remove() -> type& override;
+  auto selected() const -> bool;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setSelected(bool selected = true) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    image icon;
+    bool selected = false;
+    string text;
+  } state;
+};
+
+struct mLabel : mWidget {
+  Declare(Label)
+
+  auto horizontalAlignment() const -> double;
+  auto setHorizontalAlignment(double alignment = 0.0) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto setVerticalAlignment(double alignment = 0.5) -> type&;
+  auto text() const -> string;
+  auto verticalAlignment() const -> double;
+
+//private:
+  struct State {
+    double horizontalAlignment = 0.0;
+    string text;
+    double verticalAlignment = 0.5;
+  } state;
+};
+
+struct mLineEdit : mWidget {
+  Declare(LineEdit)
+
+  auto backgroundColor() const -> Color;
+  auto doActivate() const -> void;
+  auto doChange() const -> void;
+  auto editable() const -> bool;
+  auto foregroundColor() const -> Color;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setEditable(bool editable = true) -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    bool editable = true;
+    Color foregroundColor;
+    function<void ()> onActivate;
+    function<void ()> onChange;
+    string text;
+  } state;
+};
+
+struct mListView : mWidget {
+  Declare(ListView)
+  using mObject::remove;
+
+  auto append(sListViewColumn column) -> type&;
+  auto append(sListViewItem item) -> type&;
+  auto backgroundColor() const -> Color;
+  auto checkable() const -> bool;
+  auto checked() const -> vector<sListViewItem>;
+  auto column(unsigned position) const -> sListViewColumn;
+  auto columns() const -> unsigned;
+  auto doActivate() const -> void;
+  auto doChange() const -> void;
+  auto doContext() const -> void;
+  auto doEdit(sListViewItem item, sListViewColumn column) const -> void;
+  auto doSort(sListViewColumn column) const -> void;
+  auto doToggle(sListViewItem item) const -> void;
+  auto foregroundColor() const -> Color;
+  auto gridVisible() const -> bool;
+  auto headerVisible() const -> bool;
+  auto item(unsigned position) const -> sListViewItem;
+  auto items() const -> unsigned;
+  auto multiSelect() const -> bool;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto onContext(const function<void ()>& function = {}) -> type&;
+  auto onEdit(const function<void (sListViewItem, sListViewColumn)>& function = {}) -> type&;
+  auto onSort(const function<void (sListViewColumn)>& function = {}) -> type&;
+  auto onToggle(const function<void (sListViewItem)>& function = {}) -> type&;
+  auto remove(sListViewColumn column) -> type&;
+  auto remove(sListViewItem item) -> type&;
+  auto reset() -> type&;
+  auto resizeColumns() -> type&;
+  auto selected() const -> sListViewItem;
+  auto selectedItems() const -> vector<sListViewItem>;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setCheckable(bool checkable = true) -> type&;
+  auto setChecked(bool checked = true) -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+  auto setGridVisible(bool visible = true) -> type&;
+  auto setHeaderVisible(bool visible = true) -> type&;
+  auto setMultiSelect(bool multiSelect = true) -> type&;
+  auto setSelected(bool selected = true) -> type&;
+
+//private:
+  struct State {
+    unsigned activeColumn = 0;
+    Color backgroundColor;
+    bool checkable = false;
+    vector<sListViewColumn> columns;
+    Color foregroundColor;
+    bool gridVisible = false;
+    bool headerVisible = false;
+    vector<sListViewItem> items;
+    bool multiSelect = false;
+    function<void ()> onActivate;
+    function<void ()> onChange;
+    function<void ()> onContext;
+    function<void (sListViewItem, sListViewColumn)> onEdit;
+    function<void (sListViewColumn)> onSort;
+    function<void (sListViewItem)> onToggle;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mListViewColumn : mObject {
+  Declare(ListViewColumn)
+
+  auto active() const -> bool;
+  auto backgroundColor() const -> Color;
+  auto editable() const -> bool;
+  auto foregroundColor() const -> Color;
+  auto horizontalAlignment() const -> double;
+  auto icon() const -> image;
+  auto remove() -> type& override;
+  auto resizable() const -> bool;
+  auto setActive() -> type&;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setEditable(bool editable = true) -> type&;
+  auto setFont(const string& font = "") -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+  auto setHorizontalAlignment(double alignment = 0.0) -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setResizable(bool resizable = true) -> type&;
+  auto setSortable(bool sortable = true) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto setVerticalAlignment(double alignment = 0.5) -> type&;
+  auto setVisible(bool visible = true) -> type&;
+  auto setWidth(signed width = 0) -> type&;
+  auto sortable() const -> bool;
+  auto text() const -> string;
+  auto verticalAlignment() const -> double;
+  auto width() const -> signed;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    bool editable = false;
+    string font;
+    Color foregroundColor;
+    double horizontalAlignment = 0.0;
+    image icon;
+    bool resizable = true;
+    bool sortable = false;
+    string text;
+    double verticalAlignment = 0.5;
+    bool visible = true;
+    signed width = 0;
+  } state;
+};
+
+struct mListViewItem : mObject {
+  Declare(ListViewItem)
+
+  auto checked() const -> bool;
+  auto icon(unsigned column = 0) const -> image;
+  auto remove() -> type& override;
+  auto selected() const -> bool;
+  auto setChecked(bool checked = true) -> type&;
+  auto setFocused() -> type& override;
+  auto setIcon(unsigned column, const image& icon = {}) -> type&;
+  auto setSelected(bool selected = true) -> type&;
+  auto setText(const lstring& text) -> type&;
+  auto setText(unsigned column, const string& text = "") -> type&;
+  auto text(unsigned column = 0) const -> string;
+
+//private:
+  struct State {
+    bool checked = false;
+    vector<image> icon;
+    bool selected = false;
+    lstring text;
+  } state;
+};
+
+struct mProgressBar : mWidget {
+  Declare(ProgressBar)
+
+  auto position() const -> unsigned;
+  auto setPosition(unsigned position) -> type&;
+
+//private:
+  struct State {
+    unsigned position = 0;
+  } state;
+};
+
+struct mRadioButton : mWidget {
+  Declare(RadioButton)
+
+  auto bordered() const -> bool;
+  auto checked() const -> bool;
+  auto doActivate() const -> void;
+  auto icon() const -> image;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto orientation() const -> Orientation;
+  auto setBordered(bool bordered = true) -> type&;
+  auto setChecked() -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+  static auto group(const vector<wRadioButton>& group) -> void;
+
+//private:
+  struct State {
+    bool bordered = true;
+    bool checked = true;
+    vector<wRadioButton> group;
+    image icon;
+    function<void ()> onActivate;
+    Orientation orientation = Orientation::Horizontal;
+    string text;
+  } state;
+};
+
+struct mRadioLabel : mWidget {
+  Declare(RadioLabel)
+
+  auto checked() const -> bool;
+  auto doActivate() const -> void;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto setChecked() -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+  static auto group(const vector<wRadioLabel>& group) -> void;
+
+//private:
+  struct State {
+    bool checked = true;
+    vector<wRadioLabel> group;
+    function<void ()> onActivate;
+    string text;
+  } state;
+};
+
+struct mSourceEdit : mWidget {
+  Declare(SourceEdit)
+
+  auto doChange() const -> void;
+  auto doMove() const -> void;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto onMove(const function<void ()>& function = {}) -> type&;
+  auto position() const -> unsigned;
+  auto setPosition(signed position) -> type&;
+  auto setSelected(Position selected) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    function<void ()> onChange;
+    function<void ()> onMove;
+    unsigned position = 0;
+    Position selected;
+    string text;
+  } state;
+};
+
+struct mTabFrame : mWidget {
+  Declare(TabFrame)
+  using mObject::remove;
+  friend class mTabFrameItem;
+
+  auto append(sTabFrameItem item) -> type&;
+  auto doChange() const -> void;
+  auto doClose(sTabFrameItem item) const -> void;
+  auto doMove(sTabFrameItem from, sTabFrameItem to) const -> void;
+  auto edge() const -> Edge;
+  auto item(unsigned position) const -> sTabFrameItem;
+  auto items() const -> unsigned;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto onClose(const function<void (sTabFrameItem)>& function = {}) -> type&;
+  auto onMove(const function<void (sTabFrameItem, sTabFrameItem)>& function = {}) -> type&;
+  auto remove(sTabFrameItem item) -> type&;
+  auto reset() -> type&;
+  auto selected() const -> sTabFrameItem;
+  auto setEdge(Edge edge = Edge::Top) -> type&;
+  auto setParent(mObject* object = nullptr, signed offset = -1) -> type& override;
+
+//private:
+  struct State {
+    Edge edge = Edge::Top;
+    vector<sTabFrameItem> items;
+    function<void ()> onChange;
+    function<void (sTabFrameItem)> onClose;
+    function<void (sTabFrameItem, sTabFrameItem)> onMove;
+    unsigned selected = 0;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mTabFrameItem : mObject {
+  Declare(TabFrameItem)
+
+  auto append(sLayout layout) -> type&;
+  auto closable() const -> bool;
+  auto icon() const -> image;
+  auto layout() const -> sLayout;
+  auto movable() const -> bool;
+  auto remove() -> type& override;
+  auto remove(sLayout layout) -> type&;
+  auto reset() -> type&;
+  auto selected() const -> bool;
+  auto setClosable(bool closable = true) -> type&;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setMovable(bool movable = true) -> type&;
+  auto setParent(mObject* object = nullptr, signed offset = -1) -> type& override;
+  auto setSelected() -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    bool closable = false;
+    image icon;
+    sLayout layout;
+    bool movable = false;
+    string text;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mTextEdit : mWidget {
+  Declare(TextEdit)
+
+  auto backgroundColor() const -> Color;
+  auto cursorPosition() const -> unsigned;
+  auto doChange() const -> void;
+  auto doMove() const -> void;
+  auto editable() const -> bool;
+  auto foregroundColor() const -> Color;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto onMove(const function<void ()>& function = {}) -> type&;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setCursorPosition(unsigned position) -> type&;
+  auto setEditable(bool editable = true) -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto setWordWrap(bool wordWrap = true) -> type&;
+  auto text() const -> string;
+  auto wordWrap() const -> bool;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    unsigned cursorPosition = 0;
+    bool editable = true;
+    Color foregroundColor;
+    function<void ()> onChange;
+    function<void ()> onMove;
+    string text;
+    bool wordWrap = true;
+  } state;
+};
+
+struct mTreeView : mWidget {
+  Declare(TreeView)
+  using mObject::remove;
+
+  auto append(sTreeViewItem item) -> type&;
+  auto backgroundColor() const -> Color;
+  auto checkable() const -> bool;
+  auto collapse() -> type&;
+  auto doActivate() const -> void;
+  auto doChange() const -> void;
+  auto doContext() const -> void;
+  auto doToggle(sTreeViewItem item) const -> void;
+  auto expand() -> type&;
+  auto foregroundColor() const -> Color;
+  auto item(const string& path) const -> sTreeViewItem;
+  auto items() const -> unsigned;
+  auto onActivate(const function<void ()>& function = {}) -> type&;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto onContext(const function<void ()>& function = {}) -> type&;
+  auto onToggle(const function<void (sTreeViewItem)>& function = {}) -> type&;
+  auto remove(sTreeViewItem item) -> type&;
+  auto reset() -> type&;
+  auto selected() const -> sTreeViewItem;
+  auto setBackgroundColor(Color color = {}) -> type&;
+  auto setCheckable(bool checkable = true) -> type&;
+  auto setForegroundColor(Color color = {}) -> type&;
+
+//private:
+  struct State {
+    Color backgroundColor;
+    bool checkable = false;
+    Color foregroundColor;
+    vector<sTreeViewItem> items;
+    function<void ()> onActivate;
+    function<void ()> onChange;
+    function<void ()> onContext;
+    function<void (sTreeViewItem)> onToggle;
+    string selectedPath;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mTreeViewItem : mObject {
+  Declare(TreeViewItem)
+
+  auto append(sTreeViewItem item) -> type&;
+  auto checked() const -> bool;
+  auto icon() const -> image;
+  auto item(const string& path) const -> sTreeViewItem;
+  auto items() const -> unsigned;
+  auto path() const -> string;
+  auto remove() -> type& override;
+  auto remove(sTreeViewItem item) -> type&;
+  auto selected() const -> bool;
+  auto setChecked(bool checked = true) -> type&;
+  auto setFocused() -> type& override;
+  auto setIcon(const image& icon = {}) -> type&;
+  auto setSelected() -> type&;
+  auto setText(const string& text = "") -> type&;
+  auto text() const -> string;
+
+//private:
+  struct State {
+    bool checked = false;
+    image icon;
+    vector<sTreeViewItem> items;
+    string text;
+  } state;
+
+  auto destruct() -> void override;
+};
+
+struct mVerticalScroller : mWidget {
+  Declare(VerticalScroller)
+
+  auto doChange() const -> void;
+  auto length() const -> unsigned;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto position() const -> unsigned;
+  auto setLength(unsigned length = 101) -> type&;
+  auto setPosition(unsigned position = 0) -> type&;
+
+//private:
+  struct State {
+    unsigned length = 101;
+    function<void ()> onChange;
+    unsigned position = 0;
+  } state;
+};
+
+struct mVerticalSlider : mWidget {
+  Declare(VerticalSlider)
+
+  auto doChange() const -> void;
+  auto length() const -> unsigned;
+  auto onChange(const function<void ()>& function = {}) -> type&;
+  auto position() const -> unsigned;
+  auto setLength(unsigned length = 101) -> type&;
+  auto setPosition(unsigned position = 0) -> type&;
+
+//private:
+  struct State {
+    unsigned length = 101;
+    function<void ()> onChange;
+    unsigned position = 0;
+  } state;
+};
+
+struct mViewport : mWidget {
+  Declare(Viewport)
+
+  auto doDrop(lstring names) const -> void;
+  auto doMouseLeave() const -> void;
+  auto doMouseMove(Position position) const -> void;
+  auto doMousePress(Mouse::Button button) const -> void;
+  auto doMouseRelease(Mouse::Button button) const -> void;
+  auto droppable() const -> bool;
+  auto handle() const -> uintptr_t;
+  auto onDrop(const function<void (lstring)>& function = {}) -> type&;
+  auto onMouseLeave(const function<void ()>& function = {}) -> type&;
+  auto onMouseMove(const function<void (Position position)>& function = {}) -> type&;
+  auto onMousePress(const function<void (Mouse::Button)>& function = {}) -> type&;
+  auto onMouseRelease(const function<void (Mouse::Button)>& function = {}) -> type&;
+  auto setDroppable(bool droppable = true) -> type&;
+
+//private:
+  struct State {
+    bool droppable = false;
+    function<void (lstring)> onDrop;
+    function<void ()> onMouseLeave;
+    function<void (Position)> onMouseMove;
+    function<void (Mouse::Button)> onMousePress;
+    function<void (Mouse::Button)> onMouseRelease;
+  } state;
+};
+
+#undef Declare
+
+}
diff --git a/hiro/core/desktop.cpp b/hiro/core/desktop.cpp
new file mode 100644
index 00000000..f8981629
--- /dev/null
+++ b/hiro/core/desktop.cpp
@@ -0,0 +1,7 @@
+auto Desktop::size() -> Size {
+  return pDesktop::size();
+}
+
+auto Desktop::workspace() -> Geometry {
+  return pDesktop::workspace();
+}
diff --git a/hiro/core/font.cpp b/hiro/core/font.cpp
new file mode 100644
index 00000000..0ca2deaf
--- /dev/null
+++ b/hiro/core/font.cpp
@@ -0,0 +1,15 @@
+auto Font::serif(unsigned size, const string& style) -> string {
+  return pFont::serif(size, style);
+}
+
+auto Font::sans(unsigned size, const string& style) -> string {
+  return pFont::sans(size, style);
+}
+
+auto Font::monospace(unsigned size, const string& style) -> string {
+  return pFont::monospace(size, style);
+}
+
+auto Font::size(const string& font, const string& text) -> Size {
+  return pFont::size(font, text);
+}
diff --git a/hiro/core/geometry.cpp b/hiro/core/geometry.cpp
new file mode 100644
index 00000000..21cd32f5
--- /dev/null
+++ b/hiro/core/geometry.cpp
@@ -0,0 +1,112 @@
+Geometry::Geometry() {
+  setGeometry(0, 0, 0, 0);
+}
+
+Geometry::Geometry(Position position, Size size) {
+  setGeometry(position, size);
+}
+
+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));
+}
+
+auto Geometry::operator==(const Geometry& source) const -> bool {
+  return x() == source.x() && y() == source.y() && width() == source.width() && height() == source.height();
+}
+
+auto Geometry::operator!=(const Geometry& source) const -> bool {
+  return !operator==(source);
+}
+
+auto Geometry::height() const -> signed {
+  return state.height;
+}
+
+auto Geometry::position() const -> Position {
+  return {state.x, state.y};
+}
+
+auto Geometry::setHeight(signed height) -> type& {
+  state.height = height;
+  return *this;
+}
+
+auto Geometry::setGeometry(Geometry geometry) -> type& {
+  return setGeometry(geometry.x(), geometry.y(), geometry.width(), geometry.height());
+}
+
+auto Geometry::setGeometry(Position position, Size size) -> type& {
+  setGeometry(position.x(), position.y(), size.width(), size.height());
+  return *this;
+}
+
+auto Geometry::setGeometry(signed x, signed y, signed width, signed height) -> type& {
+  state.x = x;
+  state.y = y;
+  state.width = width;
+  state.height = height;
+  return *this;
+}
+
+auto Geometry::setPosition(Position position) -> type& {
+  return setPosition(position.x(), position.y());
+}
+
+auto Geometry::setPosition(signed x, signed y) -> type& {
+  state.x = x;
+  state.y = y;
+  return *this;
+}
+
+auto Geometry::setSize(Size size) -> type& {
+  return setSize(size.width(), size.height());
+}
+
+auto Geometry::setSize(signed width, signed height) -> type& {
+  state.width = width;
+  state.height = height;
+  return *this;
+}
+
+auto Geometry::setWidth(signed width) -> type& {
+  state.width = width;
+  return *this;
+}
+
+auto Geometry::setX(signed x) -> type& {
+  state.x = x;
+  return *this;
+}
+
+auto Geometry::setY(signed y) -> type& {
+  state.y = y;
+  return *this;
+}
+
+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;
+}
+
+auto Geometry::x() const -> signed {
+  return state.x;
+}
+
+auto Geometry::y() const -> signed {
+  return state.y;
+}
diff --git a/hiro/core/hotkey.cpp b/hiro/core/hotkey.cpp
new file mode 100644
index 00000000..432fa659
--- /dev/null
+++ b/hiro/core/hotkey.cpp
@@ -0,0 +1,53 @@
+auto mHotkey::allocate() -> pObject* {
+  return new pHotkey(*this);
+}
+
+//
+
+auto mHotkey::doPress() const -> void {
+  if(state.onPress) return state.onPress();
+}
+
+auto mHotkey::doRelease() const -> void {
+  if(state.onRelease) return state.onRelease();
+}
+
+auto mHotkey::onPress(const function<void ()>& function) -> type& {
+  state.onPress = function;
+  return *this;
+}
+
+auto mHotkey::onRelease(const function<void ()>& function) -> type& {
+  state.onRelease = function;
+  return *this;
+}
+
+auto mHotkey::parent() const -> wObject {
+  return state.parent;
+}
+
+auto mHotkey::remove() -> type& {
+//todo: remove from Keyboard::hotkeys
+  return *this;
+}
+
+auto mHotkey::sequence() const -> string {
+  return state.sequence;
+}
+
+auto mHotkey::setParent(sObject parent) -> type& {
+  state.parent = parent;
+  return *this;
+}
+
+auto mHotkey::setSequence(const string& sequence) -> type& {
+  state.active = false;
+  state.sequence = sequence;
+  state.keys.reset();
+  for(auto& key : sequence.split("+")) {
+    if(auto position = Keyboard::keys.find(key)) {
+      state.keys.append(*position);
+    }
+  }
+  return *this;
+}
diff --git a/hiro/core/keyboard.cpp b/hiro/core/keyboard.cpp
new file mode 100644
index 00000000..4ab5d1ff
--- /dev/null
+++ b/hiro/core/keyboard.cpp
@@ -0,0 +1,74 @@
+const vector<string> Keyboard::keys = {
+  //physical keyboard buttons
+  "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
+  "PrintScreen", "ScrollLock", "Pause",
+  "Insert", "Delete", "Home", "End", "PageUp", "PageDown",
+  "Up", "Down", "Left", "Right",
+  "Grave", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "Dash", "Equal", "Backspace",
+  "Tab", "CapsLock", "LeftEnter", "LeftShift", "RightShift",
+  "LeftControl", "RightControl", "LeftAlt", "RightAlt", "LeftSuper", "RightSuper", "Menu", "Space",
+  "OpenBracket", "CloseBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash",
+  "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
+  "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+  "NumLock", "Divide", "Multiply", "Subtract", "Add", "RightEnter", "Point",
+  "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Zero",
+
+  //group aliases
+  "Shift",    //"LeftShift" | "RightShift"
+  "Control",  //"LeftControl" | "RightControl"
+  "Alt",      //"LeftAlt" | "RightAlt"
+  "Super",    //"LeftSuper" | "RightSuper"
+  "Enter",    //"LeftEnter" | "RightEnter"
+};
+
+auto Keyboard::append(sHotkey hotkey) -> void {
+  state.hotkeys.append(hotkey);
+}
+
+auto Keyboard::hotkey(unsigned position) -> sHotkey {
+  if(position >= hotkeys()) throw;
+  return state.hotkeys[position];
+}
+
+auto Keyboard::hotkeys() -> unsigned {
+  return state.hotkeys.size();
+}
+
+auto Keyboard::poll() -> vector<bool> {
+  auto pressed = pKeyboard::poll();
+
+  for(auto& hotkey : state.hotkeys) {
+    bool active = hotkey->state.sequence.size() > 0;
+    for(auto& key : hotkey->state.keys) {
+      if(pressed[key]) continue;
+      active = false;
+      break;
+    }
+    if(auto parent = hotkey->state.parent.acquire()) {
+      //todo: set active = false if parent no longer exists
+      active &= parent->focused();
+    }
+    if(hotkey->state.active != active) {
+      hotkey->state.active = active;
+      if( active) hotkey->doPress();
+      if(!active) hotkey->doRelease();
+    }
+  }
+
+  return pressed;
+}
+
+auto Keyboard::pressed(const string& key) -> bool {
+  if(auto code = keys.find(key)) return pKeyboard::pressed(*code);
+  return false;
+}
+
+auto Keyboard::released(const string& key) -> bool {
+  return !pressed(key);
+}
+
+auto Keyboard::remove(sHotkey hotkey) -> void {
+  if(auto offset = state.hotkeys.find(hotkey)) {
+    state.hotkeys.remove(*offset);
+  }
+}
diff --git a/hiro/core/layout.cpp b/hiro/core/layout.cpp
new file mode 100644
index 00000000..73a66996
--- /dev/null
+++ b/hiro/core/layout.cpp
@@ -0,0 +1,58 @@
+auto mLayout::allocate() -> pObject* {
+  return new pLayout(*this);
+}
+
+auto mLayout::destruct() -> void {
+  for(auto& sizable : state.sizables) sizable->destruct();
+  mSizable::destruct();
+}
+
+//
+
+auto mLayout::append(shared_pointer<mSizable> sizable) -> type& {
+  state.sizables.append(sizable);
+  sizable->setParent(this, sizables() - 1);
+  setGeometry(geometry());
+  return *this;
+}
+
+auto mLayout::remove() -> type& {
+  if(auto tabFrameItem = parentTabFrameItem()) tabFrameItem->remove(tabFrameItem->layout());
+  if(auto layout = parentLayout()) layout->remove(layout->sizable(offset()));
+  if(auto window = parentWindow()) window->remove(window->layout());
+  setParent();
+  for(auto& sizable : state.sizables) sizable->setParent(this, sizable->offset());
+  return *this;
+}
+
+auto mLayout::remove(shared_pointer<mSizable> sizable) -> type& {
+  auto offset = sizable->offset();
+  sizable->setParent();
+  state.sizables.remove(offset);
+  for(auto n : range(offset, sizables())) {
+    state.sizables[n]->offset(-1);
+  }
+  setGeometry(geometry());
+  return *this;
+}
+
+auto mLayout::reset() -> type& {
+  while(state.sizables) remove(state.sizables.last());
+  return *this;
+}
+
+auto mLayout::setParent(mObject* parent, signed offset) -> type& {
+  for(auto& sizable : state.sizables) sizable->destruct();
+  mObject::setParent(parent, offset);
+  for(auto& sizable : state.sizables) sizable->setParent(this, sizable->offset());
+  return *this;
+}
+
+auto mLayout::sizable(unsigned position) const -> shared_pointer<mSizable> {
+  if(position >= sizables()) throw;
+  return state.sizables[position];
+}
+
+auto mLayout::sizables() const -> unsigned {
+  return state.sizables.size();
+}
diff --git a/hiro/core/menu-bar.cpp b/hiro/core/menu-bar.cpp
new file mode 100644
index 00000000..c8559ddc
--- /dev/null
+++ b/hiro/core/menu-bar.cpp
@@ -0,0 +1,47 @@
+auto mMenuBar::allocate() -> pObject* {
+  return new pMenuBar(*this);
+}
+
+auto mMenuBar::destruct() -> void {
+  for(auto& menu : state.menus) menu->destruct();
+  mObject::destruct();
+}
+
+//
+
+auto mMenuBar::append(shared_pointer<mMenu> menu) -> type& {
+  state.menus.append(menu);
+  menu->setParent(this, menus() - 1);
+  signal(append, menu);
+  return *this;
+}
+
+auto mMenuBar::menu(unsigned position) const -> shared_pointer<mMenu> {
+  if(position >= menus()) throw;
+  return state.menus[position];
+}
+
+auto mMenuBar::menus() const -> unsigned {
+  return state.menus.size();
+}
+
+auto mMenuBar::remove() -> type& {
+  if(auto window = parentWindow()) window->remove(window->menuBar());
+  return *this;
+}
+
+auto mMenuBar::remove(shared_pointer<mMenu> menu) -> type& {
+  signed offset = menu->offset();
+  signal(remove, *menu);
+  state.menus.remove(offset);
+  for(auto n : range(offset, menus())) {
+    state.menus[n]->offset(-1);
+  }
+  menu->setParent();
+  return *this;
+}
+
+auto mMenuBar::reset() -> type& {
+  while(state.menus) remove(state.menus.last());
+  return *this;
+}
diff --git a/hiro/core/message-window.cpp b/hiro/core/message-window.cpp
new file mode 100644
index 00000000..287e336d
--- /dev/null
+++ b/hiro/core/message-window.cpp
@@ -0,0 +1,38 @@
+MessageWindow::MessageWindow(const string& text) {
+  state.text = text;
+}
+
+auto MessageWindow::error(Buttons buttons) -> Response {
+  state.buttons = buttons;
+  return pMessageWindow::error(state);
+}
+
+auto MessageWindow::information(Buttons buttons) -> Response {
+  state.buttons = buttons;
+  return pMessageWindow::information(state);
+}
+
+auto MessageWindow::question(Buttons buttons) -> Response {
+  state.buttons = buttons;
+  return pMessageWindow::question(state);
+}
+
+auto MessageWindow::setParent(shared_pointer<mWindow> parent) -> type& {
+  state.parent = parent;
+  return *this;
+}
+
+auto MessageWindow::setText(const string& text) -> type& {
+  state.text = text;
+  return *this;
+}
+
+auto MessageWindow::setTitle(const string& title) -> type& {
+  state.title = title;
+  return *this;
+}
+
+auto MessageWindow::warning(MessageWindow::Buttons buttons) -> Response {
+  state.buttons = buttons;
+  return pMessageWindow::warning(state);
+}
diff --git a/hiro/core/monitor.cpp b/hiro/core/monitor.cpp
new file mode 100644
index 00000000..6ca1984a
--- /dev/null
+++ b/hiro/core/monitor.cpp
@@ -0,0 +1,11 @@
+auto Monitor::count() -> unsigned {
+  return pMonitor::count();
+}
+
+auto Monitor::geometry(unsigned monitor) -> Geometry {
+  return pMonitor::geometry(monitor);
+}
+
+auto Monitor::primary() -> unsigned {
+  return pMonitor::primary();
+}
diff --git a/hiro/core/mouse.cpp b/hiro/core/mouse.cpp
new file mode 100644
index 00000000..49fe1574
--- /dev/null
+++ b/hiro/core/mouse.cpp
@@ -0,0 +1,11 @@
+auto Mouse::position() -> Position {
+  return pMouse::position();
+}
+
+auto Mouse::pressed(Mouse::Button button) -> bool {
+  return pMouse::pressed(button);
+}
+
+auto Mouse::released(Mouse::Button button) -> bool {
+  return !pressed(button);
+}
diff --git a/hiro/core/object.cpp b/hiro/core/object.cpp
new file mode 100644
index 00000000..3b97fe28
--- /dev/null
+++ b/hiro/core/object.cpp
@@ -0,0 +1,233 @@
+mObject::mObject() {
+  Application::initialize();
+}
+
+mObject::~mObject() {
+}
+
+auto mObject::allocate() -> pObject* {
+  return new pObject(*this);
+}
+
+auto mObject::construct() -> void {
+  if(delegate) return;
+  delegate = allocate();
+  signal(construct);
+}
+
+auto mObject::destruct() -> void {
+  if(!delegate) return;
+  signal(destruct);
+  delete delegate;
+  delegate = nullptr;
+}
+
+//
+
+//this is used to control dynamic allocation of pObject delegates
+//an mObject is abstract only if it has no top-level object (eg a Button not attached to any Window)
+//if the mObject is not abstract, the pObject delegate is allocated immediately
+//otherwise, the pObject is not allocated until it is attached to a non-abstract parent
+auto mObject::abstract() const -> bool {
+  if(dynamic_cast<const mWindow*>(this)) return false;
+  if(dynamic_cast<const mPopupMenu*>(this)) return false;
+  if(auto object = parent()) return object->abstract();
+  return true;
+}
+
+auto mObject::enabled(bool recursive) const -> bool {
+  if(!recursive || !state.enabled) return state.enabled;
+  if(auto object = parent()) return object->enabled(true);
+  return true;
+}
+
+auto mObject::focused() const -> bool {
+  if(signal(focused)) return true;
+  return false;
+}
+
+auto mObject::font(bool recursive) const -> string {
+  if(!recursive || state.font) return state.font;
+  if(auto object = parent()) return object->font(true);
+  return Application::font();
+}
+
+auto mObject::offset() const -> signed {
+  return state.offset;
+}
+
+auto mObject::offset(signed displacement) -> type& {
+  state.offset += displacement;
+  return *this;
+}
+
+auto mObject::parent() const -> mObject* {
+  return state.parent;
+}
+
+auto mObject::parentComboButton(bool recursive) const -> mComboButton* {
+  if(auto comboButton = dynamic_cast<mComboButton*>(parent())) return comboButton;
+  if(recursive) {
+    if(auto object = parent()) return object->parentComboButton(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentFrame(bool recursive) const -> mFrame* {
+  if(auto frame = dynamic_cast<mFrame*>(parent())) return frame;
+  if(recursive) {
+    if(auto object = parent()) return object->parentFrame(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentIconView(bool recursive) const -> mIconView* {
+  if(auto iconView = dynamic_cast<mIconView*>(parent())) return iconView;
+  if(recursive) {
+    if(auto object = parent()) return object->parentIconView(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentLayout(bool recursive) const -> mLayout* {
+  if(auto layout = dynamic_cast<mLayout*>(parent())) return layout;
+  if(recursive) {
+    if(auto object = parent()) return object->parentLayout(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentListView(bool recursive) const -> mListView* {
+  if(auto listView = dynamic_cast<mListView*>(parent())) return listView;
+  if(recursive) {
+    if(auto object = parent()) return object->parentListView(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentMenu(bool recursive) const -> mMenu* {
+  if(auto menu = dynamic_cast<mMenu*>(parent())) return menu;
+  if(recursive) {
+    if(auto object = parent()) return object->parentMenu(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentMenuBar(bool recursive) const -> mMenuBar* {
+  if(auto menuBar = dynamic_cast<mMenuBar*>(parent())) return menuBar;
+  if(recursive) {
+    if(auto object = parent()) return object->parentMenuBar(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentPopupMenu(bool recursive) const -> mPopupMenu* {
+  if(auto popupMenu = dynamic_cast<mPopupMenu*>(parent())) return popupMenu;
+  if(recursive) {
+    if(auto object = parent()) return object->parentPopupMenu(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentSizable(bool recursive) const -> mSizable* {
+  if(auto sizable = dynamic_cast<mSizable*>(parent())) return sizable;
+  if(recursive) {
+    if(auto object = parent()) return object->parentSizable(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentTabFrame(bool recursive) const -> mTabFrame* {
+  if(auto tabFrame = dynamic_cast<mTabFrame*>(parent())) return tabFrame;
+  if(recursive) {
+    if(auto object = parent()) return object->parentTabFrame(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentTabFrameItem(bool recursive) const -> mTabFrameItem* {
+  if(auto tabFrameItem = dynamic_cast<mTabFrameItem*>(parent())) return tabFrameItem;
+  if(recursive) {
+    if(auto object = parent()) return object->parentTabFrameItem(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentTreeView(bool recursive) const -> mTreeView* {
+  if(auto treeView = dynamic_cast<mTreeView*>(parent())) return treeView;
+  if(recursive) {
+    if(auto object = parent()) return object->parentTreeView(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentTreeViewItem(bool recursive) const -> mTreeViewItem* {
+  if(auto treeViewItem = dynamic_cast<mTreeViewItem*>(parent())) return treeViewItem;
+  if(recursive) {
+    if(auto object = parent()) return object->parentTreeViewItem(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentWidget(bool recursive) const -> mWidget* {
+  if(auto widget = dynamic_cast<mWidget*>(parent())) return widget;
+  if(recursive) {
+    if(auto object = parent()) return object->parentWidget(true);
+  }
+  return nullptr;
+}
+
+auto mObject::parentWindow(bool recursive) const -> mWindow* {
+  if(auto window = dynamic_cast<mWindow*>(parent())) return window;
+  if(recursive) {
+    if(auto object = parent()) return object->parentWindow(true);
+  }
+  return nullptr;
+}
+
+auto mObject::remove() -> type& {
+  signal(remove);
+  return *this;
+}
+
+auto mObject::reset() -> type& {
+  signal(reset);
+  return *this;
+}
+
+auto mObject::setEnabled(bool enabled) -> type& {
+  state.enabled = enabled;
+  signal(setEnabled, this->enabled(true));
+  return *this;
+}
+
+auto mObject::setFocused() -> type& {
+  signal(setFocused);
+  return *this;
+}
+
+auto mObject::setFont(const string& font) -> type& {
+  state.font = font;
+  signal(setFont, this->font(true));
+  return *this;
+}
+
+auto mObject::setParent(mObject* parent, signed offset) -> type& {
+  destruct();
+  state.parent = parent;
+  state.offset = offset;
+  if(!abstract()) construct();
+  return *this;
+}
+
+auto mObject::setVisible(bool visible) -> type& {
+  state.visible = visible;
+  signal(setVisible, this->visible(true));
+  return *this;
+}
+
+auto mObject::visible(bool recursive) const -> bool {
+  if(!recursive || !state.visible) return state.visible;
+  if(auto object = parent()) return object->visible(true);
+  return true;
+}
diff --git a/hiro/core/popup-menu.cpp b/hiro/core/popup-menu.cpp
new file mode 100644
index 00000000..6e74fd7f
--- /dev/null
+++ b/hiro/core/popup-menu.cpp
@@ -0,0 +1,47 @@
+auto mPopupMenu::allocate() -> pObject* {
+  return new pPopupMenu(*this);
+}
+
+auto mPopupMenu::destruct() -> void {
+  for(auto& action : state.actions) action->destruct();
+  mObject::destruct();
+}
+
+//
+
+auto mPopupMenu::action(unsigned position) const -> sAction {
+  if(position >= actions()) throw;
+  return state.actions[position];
+}
+
+auto mPopupMenu::actions() const -> unsigned {
+  return state.actions.size();
+}
+
+auto mPopupMenu::append(sAction action) -> type& {
+  state.actions.append(action);
+  action->setParent(this, actions() - 1);
+  signal(append, action);
+  return *this;
+}
+
+auto mPopupMenu::remove(sAction action) -> type& {
+  signed offset = action->offset();
+  signal(remove, action);
+  state.actions.remove(offset);
+  for(auto n : range(offset, actions())) {
+    state.actions[n]->offset(-1);
+  }
+  action->setParent();
+  return *this;
+}
+
+auto mPopupMenu::reset() -> type& {
+  while(state.actions) remove(state.actions.last());
+  return *this;
+}
+
+auto mPopupMenu::setVisible(bool visible) -> type& {
+  signal(setVisible, visible);
+  return *this;
+}
diff --git a/hiro/core/position.cpp b/hiro/core/position.cpp
new file mode 100644
index 00000000..f878fc01
--- /dev/null
+++ b/hiro/core/position.cpp
@@ -0,0 +1,43 @@
+Position::Position() {
+  setPosition(0, 0);
+}
+
+Position::Position(signed x, signed y) {
+  setPosition(x, y);
+}
+
+auto Position::operator==(const Position& source) const -> bool {
+  return x() == source.x() && y() == source.y();
+}
+
+auto Position::operator!=(const Position& source) const -> bool {
+  return !operator==(source);
+}
+
+auto Position::setPosition(Position position) -> type& {
+  return setPosition(position.x(), position.y());
+}
+
+auto Position::setPosition(signed x, signed y) -> type& {
+  state.x = x;
+  state.y = y;
+  return *this;
+}
+
+auto Position::setX(signed x) -> type& {
+  state.x = x;
+  return *this;
+}
+
+auto Position::setY(signed y) -> type& {
+  state.y = y;
+  return *this;
+}
+
+auto Position::x() const -> signed {
+  return state.x;
+}
+
+auto Position::y() const -> signed {
+  return state.y;
+}
diff --git a/hiro/core/sizable.cpp b/hiro/core/sizable.cpp
new file mode 100644
index 00000000..210226fd
--- /dev/null
+++ b/hiro/core/sizable.cpp
@@ -0,0 +1,17 @@
+auto mSizable::allocate() -> pObject* {
+  return new pSizable(*this);
+}
+
+auto mSizable::geometry() const -> Geometry {
+  return state.geometry;
+}
+
+auto mSizable::minimumSize() const -> Size {
+  return signal(minimumSize);
+}
+
+auto mSizable::setGeometry(Geometry geometry) -> type& {
+  state.geometry = geometry;
+  signal(setGeometry, geometry);
+  return *this;
+}
diff --git a/hiro/core/size.cpp b/hiro/core/size.cpp
new file mode 100644
index 00000000..950189b4
--- /dev/null
+++ b/hiro/core/size.cpp
@@ -0,0 +1,43 @@
+Size::Size() {
+  setSize(0, 0);
+}
+
+Size::Size(signed width, signed height) {
+  setSize(width, height);
+}
+
+auto Size::operator==(const Size& source) const -> bool {
+  return width() == source.width() && height() == source.height();
+}
+
+auto Size::operator!=(const Size& source) const -> bool {
+  return !operator==(source);
+}
+
+auto Size::height() const -> signed {
+  return state.height;
+}
+
+auto Size::setHeight(signed height) -> type& {
+  state.height = height;
+  return *this;
+}
+
+auto Size::setSize(Size size) -> type& {
+  return setSize(size.width(), size.height());
+}
+
+auto Size::setSize(signed width, signed height) -> type& {
+  state.width = width;
+  state.height = height;
+  return *this;
+}
+
+auto Size::setWidth(signed width) -> type& {
+  state.width = width;
+  return *this;
+}
+
+auto Size::width() const -> signed {
+  return state.width;
+}
diff --git a/hiro/core/status-bar.cpp b/hiro/core/status-bar.cpp
new file mode 100644
index 00000000..2f5cf46b
--- /dev/null
+++ b/hiro/core/status-bar.cpp
@@ -0,0 +1,20 @@
+auto mStatusBar::allocate() -> pObject* {
+  return new pStatusBar(*this);
+}
+
+//
+
+auto mStatusBar::remove() -> type& {
+  if(auto window = parentWindow()) window->remove(window->statusBar());
+  return *this;
+}
+
+auto mStatusBar::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mStatusBar::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/timer.cpp b/hiro/core/timer.cpp
new file mode 100644
index 00000000..e8488c5e
--- /dev/null
+++ b/hiro/core/timer.cpp
@@ -0,0 +1,24 @@
+auto mTimer::allocate() -> pObject* {
+  return new pTimer(*this);
+}
+
+//
+
+auto mTimer::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mTimer::interval() const -> unsigned {
+  return state.interval;
+}
+
+auto mTimer::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mTimer::setInterval(unsigned interval) -> type& {
+  state.interval = interval;
+  signal(setInterval, interval);
+  return *this;
+}
diff --git a/hiro/core/widget/button.cpp b/hiro/core/widget/button.cpp
new file mode 100644
index 00000000..6382bf0f
--- /dev/null
+++ b/hiro/core/widget/button.cpp
@@ -0,0 +1,54 @@
+auto mButton::allocate() -> pObject* {
+  return new pButton(*this);
+}
+
+//
+
+auto mButton::bordered() const -> bool {
+  return state.bordered;
+}
+
+auto mButton::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mButton::icon() const -> image {
+  return state.icon;
+}
+
+auto mButton::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mButton::orientation() const -> Orientation {
+  return state.orientation;
+}
+
+auto mButton::setBordered(bool bordered) -> type& {
+  state.bordered = bordered;
+  signal(setBordered, bordered);
+  return *this;
+}
+
+auto mButton::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mButton::setOrientation(Orientation orientation) -> type& {
+  state.orientation = orientation;
+  signal(setOrientation, orientation);
+  return *this;
+}
+
+auto mButton::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mButton::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/canvas.cpp b/hiro/core/widget/canvas.cpp
new file mode 100644
index 00000000..4499e0e1
--- /dev/null
+++ b/hiro/core/widget/canvas.cpp
@@ -0,0 +1,125 @@
+auto mCanvas::allocate() -> pObject* {
+  return new pCanvas(*this);
+}
+
+//
+
+auto mCanvas::color() const -> Color {
+  return state.color;
+}
+
+auto mCanvas::data() -> uint32_t* {
+  return state.data.data();
+}
+
+auto mCanvas::droppable() const -> bool {
+  return state.droppable;
+}
+
+auto mCanvas::doDrop(lstring names) const -> void {
+  if(state.onDrop) return state.onDrop(names);
+}
+
+auto mCanvas::doMouseLeave() const -> void {
+  if(state.onMouseLeave) return state.onMouseLeave();
+}
+
+auto mCanvas::doMouseMove(Position position) const -> void {
+  if(state.onMouseMove) return state.onMouseMove(position);
+}
+
+auto mCanvas::doMousePress(Mouse::Button button) const -> void {
+  if(state.onMousePress) return state.onMousePress(button);
+}
+
+auto mCanvas::doMouseRelease(Mouse::Button button) const -> void {
+  if(state.onMouseRelease) return state.onMouseRelease(button);
+}
+
+auto mCanvas::gradient() const -> vector<Color> {
+  return state.gradient;
+}
+
+auto mCanvas::icon() const -> image {
+  return state.icon;
+}
+
+auto mCanvas::onDrop(const function<void (lstring)>& function) -> type& {
+  state.onDrop = function;
+  return *this;
+}
+
+auto mCanvas::onMouseLeave(const function<void ()>& function) -> type& {
+  state.onMouseLeave = function;
+  return *this;
+}
+
+auto mCanvas::onMouseMove(const function<void (Position)>& function) -> type& {
+  state.onMouseMove = function;
+  return *this;
+}
+
+auto mCanvas::onMousePress(const function<void (Mouse::Button)>& function) -> type& {
+  state.onMousePress = function;
+  return *this;
+}
+
+auto mCanvas::onMouseRelease(const function<void (Mouse::Button)>& function) -> type& {
+  state.onMouseRelease = function;
+  return *this;
+}
+
+auto mCanvas::setColor(Color color) -> type& {
+  state.size = {};
+  state.color = color;
+  signal(setColor, color);
+  return *this;
+}
+
+auto mCanvas::setData(Size size) -> type& {
+  state.size = size;
+  state.data.resize(size.width() * size.height());
+  memory::fill(state.data.data(), size.width() * size.height() * sizeof(uint32_t));
+  signal(setData, size);
+  return *this;
+}
+
+auto mCanvas::setDroppable(bool droppable) -> type& {
+  state.droppable = droppable;
+  signal(setDroppable, droppable);
+  return *this;
+}
+
+auto mCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> type& {
+  state.size = {};
+  state.gradient[0] = topLeft;
+  state.gradient[1] = topRight;
+  state.gradient[2] = bottomLeft;
+  state.gradient[3] = bottomRight;
+  signal(setGradient, topLeft, topRight, bottomLeft, bottomRight);
+  return *this;
+}
+
+auto mCanvas::setHorizontalGradient(Color left, Color right) -> type& {
+  return setGradient(left, right, left, right);
+}
+
+auto mCanvas::setIcon(const image& icon) -> type& {
+  state.size = {(signed)icon.width, (signed)icon.height};
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mCanvas::setVerticalGradient(Color top, Color bottom) -> type& {
+  return setGradient(top, top, bottom, bottom);
+}
+
+auto mCanvas::size() const -> Size {
+  return state.size;
+}
+
+auto mCanvas::update() -> type& {
+  signal(update);
+  return *this;
+}
diff --git a/hiro/core/widget/check-button.cpp b/hiro/core/widget/check-button.cpp
new file mode 100644
index 00000000..16411d80
--- /dev/null
+++ b/hiro/core/widget/check-button.cpp
@@ -0,0 +1,64 @@
+auto mCheckButton::allocate() -> pObject* {
+  return new pCheckButton(*this);
+}
+
+//
+
+auto mCheckButton::bordered() const -> bool {
+  return state.bordered;
+}
+
+auto mCheckButton::checked() const -> bool {
+  return state.checked;
+}
+
+auto mCheckButton::doToggle() const -> void {
+  if(state.onToggle) return state.onToggle();
+}
+
+auto mCheckButton::icon() const -> image {
+  return state.icon;
+}
+
+auto mCheckButton::onToggle(const function<void ()>& function) -> type& {
+  state.onToggle = function;
+  return *this;
+}
+
+auto mCheckButton::orientation() const -> Orientation {
+  return state.orientation;
+}
+
+auto mCheckButton::setBordered(bool bordered) -> type& {
+  state.bordered = bordered;
+  signal(setBordered, bordered);
+  return *this;
+}
+
+auto mCheckButton::setChecked(bool checked) -> type& {
+  state.checked = checked;
+  signal(setChecked, checked);
+  return *this;
+}
+
+auto mCheckButton::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mCheckButton::setOrientation(Orientation orientation) -> type& {
+  state.orientation = orientation;
+  signal(setOrientation, orientation);
+  return *this;
+}
+
+auto mCheckButton::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mCheckButton::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/check-label.cpp b/hiro/core/widget/check-label.cpp
new file mode 100644
index 00000000..7f072545
--- /dev/null
+++ b/hiro/core/widget/check-label.cpp
@@ -0,0 +1,34 @@
+auto mCheckLabel::allocate() -> pObject* {
+  return new pCheckLabel(*this);
+}
+
+//
+
+auto mCheckLabel::checked() const -> bool {
+  return state.checked;
+}
+
+auto mCheckLabel::doToggle() const -> void {
+  if(state.onToggle) return state.onToggle();
+}
+
+auto mCheckLabel::onToggle(const function<void ()>& function) -> type& {
+  state.onToggle = function;
+  return *this;
+}
+
+auto mCheckLabel::setChecked(bool checked) -> type& {
+  state.checked = checked;
+  signal(setChecked, checked);
+  return *this;
+}
+
+auto mCheckLabel::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mCheckLabel::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/combo-button-item.cpp b/hiro/core/widget/combo-button-item.cpp
new file mode 100644
index 00000000..e2b7422b
--- /dev/null
+++ b/hiro/core/widget/combo-button-item.cpp
@@ -0,0 +1,43 @@
+auto mComboButtonItem::allocate() -> pObject* {
+  return new pComboButtonItem(*this);
+}
+
+//
+
+auto mComboButtonItem::icon() const -> image {
+  return state.icon;
+}
+
+auto mComboButtonItem::remove() -> type& {
+  if(auto comboButton = parentComboButton()) comboButton->remove(*this);
+  return *this;
+}
+
+auto mComboButtonItem::selected() const -> bool {
+  if(auto comboButton = parentComboButton()) return comboButton->state.selected == offset();
+  return false;
+}
+
+auto mComboButtonItem::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mComboButtonItem::setSelected() -> type& {
+  if(auto parent = parentComboButton()) {
+    parent->state.selected = offset();
+    signal(setSelected);
+  }
+  return *this;
+}
+
+auto mComboButtonItem::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mComboButtonItem::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/combo-button.cpp b/hiro/core/widget/combo-button.cpp
new file mode 100644
index 00000000..daf3f68a
--- /dev/null
+++ b/hiro/core/widget/combo-button.cpp
@@ -0,0 +1,61 @@
+auto mComboButton::allocate() -> pObject* {
+  return new pComboButton(*this);
+}
+
+auto mComboButton::destruct() -> void {
+  for(auto& item : state.items) item->destruct();
+  mWidget::destruct();
+}
+
+//
+
+auto mComboButton::append(sComboButtonItem item) -> type& {
+  state.items.append(item);
+  item->setParent(this, items() - 1);
+  signal(append, item);
+  if(state.selected < 0) item->setSelected();
+  return *this;
+}
+
+auto mComboButton::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mComboButton::item(unsigned position) const -> sComboButtonItem {
+  if(position < items()) return state.items[position];
+  return {};
+}
+
+auto mComboButton::items() const -> unsigned {
+  return state.items.size();
+}
+
+auto mComboButton::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mComboButton::remove(sComboButtonItem item) -> type& {
+  signal(remove, item);
+  state.items.remove(item->offset());
+  for(auto n : range(item->offset(), items())) {
+    state.items[n]->offset(-1);
+  }
+  item->setParent();
+  return *this;
+}
+
+auto mComboButton::reset() -> type& {
+  signal(reset);
+  for(auto& item : state.items) {
+    item->setParent();
+  }
+  state.items.reset();
+  state.selected = -1;
+  return *this;
+}
+
+auto mComboButton::selected() const -> sComboButtonItem {
+  if(state.selected >= 0) return state.items[state.selected];
+  return {};
+}
diff --git a/hiro/core/widget/console.cpp b/hiro/core/widget/console.cpp
new file mode 100644
index 00000000..2db0408c
--- /dev/null
+++ b/hiro/core/widget/console.cpp
@@ -0,0 +1,54 @@
+auto mConsole::allocate() -> pObject* {
+  return new pConsole(*this);
+}
+
+//
+
+auto mConsole::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mConsole::doActivate(string command) const -> void {
+  if(state.onActivate) return state.onActivate(command);
+}
+
+auto mConsole::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mConsole::onActivate(const function<void (string)>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mConsole::print(const string& text) -> type& {
+  signal(print, text);
+  return *this;
+}
+
+auto mConsole::prompt() const -> string {
+  return state.prompt;
+}
+
+auto mConsole::reset() -> type& {
+  signal(reset);
+  return *this;
+}
+
+auto mConsole::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mConsole::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
+
+auto mConsole::setPrompt(const string& prompt) -> type& {
+  state.prompt = prompt;
+  signal(setPrompt, prompt);
+  return *this;
+}
diff --git a/hiro/core/widget/frame.cpp b/hiro/core/widget/frame.cpp
new file mode 100644
index 00000000..b6abe85c
--- /dev/null
+++ b/hiro/core/widget/frame.cpp
@@ -0,0 +1,42 @@
+auto mFrame::allocate() -> pObject* {
+  return new pFrame(*this);
+}
+
+auto mFrame::destruct() -> void {
+  if(auto& layout = state.layout) layout->destruct();
+  mWidget::destruct();
+}
+
+//
+
+auto mFrame::append(shared_pointer<mLayout> layout) -> type& {
+  if(auto& layout = state.layout) remove(layout);
+  state.layout = layout;
+  layout->setParent(this, 0);
+  return *this;
+}
+
+auto mFrame::layout() const -> shared_pointer<mLayout> {
+  return state.layout;
+}
+
+auto mFrame::remove(shared_pointer<mLayout> layout) -> type& {
+  layout->setParent();
+  state.layout.reset();
+  return *this;
+}
+
+auto mFrame::reset() -> type& {
+  if(auto& layout = state.layout) remove(layout);
+  return *this;
+}
+
+auto mFrame::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mFrame::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/hex-edit.cpp b/hiro/core/widget/hex-edit.cpp
new file mode 100644
index 00000000..3a9d5a10
--- /dev/null
+++ b/hiro/core/widget/hex-edit.cpp
@@ -0,0 +1,89 @@
+auto mHexEdit::allocate() -> pObject* {
+  return new pHexEdit(*this);
+}
+
+//
+
+auto mHexEdit::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mHexEdit::columns() const -> unsigned {
+  return state.columns;
+}
+
+auto mHexEdit::doRead(unsigned offset) const -> uint8_t {
+  if(state.onRead) return state.onRead(offset);
+  return 0x00;
+}
+
+auto mHexEdit::doWrite(unsigned offset, uint8_t data) const -> void {
+  if(state.onWrite) return state.onWrite(offset, data);
+}
+
+auto mHexEdit::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mHexEdit::length() const -> unsigned {
+  return state.length;
+}
+
+auto mHexEdit::offset() const -> unsigned {
+  return state.offset;
+}
+
+auto mHexEdit::onRead(const function<uint8_t (unsigned)>& function) -> type& {
+  state.onRead = function;
+  return *this;
+}
+
+auto mHexEdit::onWrite(const function<void (unsigned, uint8_t)>& function) -> type& {
+  state.onWrite = function;
+  return *this;
+}
+
+auto mHexEdit::rows() const -> unsigned {
+  return state.rows;
+}
+
+auto mHexEdit::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mHexEdit::setColumns(unsigned columns) -> type& {
+  state.columns = columns;
+  signal(setColumns, columns);
+  return *this;
+}
+
+auto mHexEdit::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
+
+auto mHexEdit::setLength(unsigned length) -> type& {
+  state.length = length;
+  signal(setLength, length);
+  return *this;
+}
+
+auto mHexEdit::setOffset(unsigned offset) -> type& {
+  state.offset = offset;
+  signal(setOffset, offset);
+  return *this;
+}
+
+auto mHexEdit::setRows(unsigned rows) -> type& {
+  state.rows = rows;
+  signal(setRows, rows);
+  return *this;
+}
+
+auto mHexEdit::update() -> type& {
+  signal(update);
+  return *this;
+}
diff --git a/hiro/core/widget/horizontal-scroller.cpp b/hiro/core/widget/horizontal-scroller.cpp
new file mode 100644
index 00000000..6af6856e
--- /dev/null
+++ b/hiro/core/widget/horizontal-scroller.cpp
@@ -0,0 +1,34 @@
+auto mHorizontalScroller::allocate() -> pObject* {
+  return new pHorizontalScroller(*this);
+}
+
+//
+
+auto mHorizontalScroller::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mHorizontalScroller::length() const -> unsigned {
+  return state.length;
+}
+
+auto mHorizontalScroller::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mHorizontalScroller::position() const -> unsigned {
+  return state.position;
+}
+
+auto mHorizontalScroller::setLength(unsigned length) -> type& {
+  state.length = length;
+  signal(setLength, length);
+  return *this;
+}
+
+auto mHorizontalScroller::setPosition(unsigned position) -> type& {
+  state.position = position;
+  signal(setPosition, position);
+  return *this;
+}
diff --git a/hiro/core/widget/horizontal-slider.cpp b/hiro/core/widget/horizontal-slider.cpp
new file mode 100644
index 00000000..f9a9c04d
--- /dev/null
+++ b/hiro/core/widget/horizontal-slider.cpp
@@ -0,0 +1,34 @@
+auto mHorizontalSlider::allocate() -> pObject* {
+  return new pHorizontalSlider(*this);
+}
+
+//
+
+auto mHorizontalSlider::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mHorizontalSlider::length() const -> unsigned {
+  return state.length;
+}
+
+auto mHorizontalSlider::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mHorizontalSlider::position() const -> unsigned {
+  return state.position;
+}
+
+auto mHorizontalSlider::setLength(unsigned length) -> type& {
+  state.length = length;
+  signal(setLength, length);
+  return *this;
+}
+
+auto mHorizontalSlider::setPosition(unsigned position) -> type& {
+  state.position = position;
+  signal(setPosition, position);
+  return *this;
+}
diff --git a/hiro/core/widget/icon-view-item.cpp b/hiro/core/widget/icon-view-item.cpp
new file mode 100644
index 00000000..db6dac33
--- /dev/null
+++ b/hiro/core/widget/icon-view-item.cpp
@@ -0,0 +1,40 @@
+auto mIconViewItem::allocate() -> pObject* {
+  return new pIconViewItem(*this);
+}
+
+//
+
+auto mIconViewItem::icon() const -> image {
+  return state.icon;
+}
+
+auto mIconViewItem::remove() -> type& {
+  if(auto iconView = parentIconView()) iconView->remove(*this);
+  return *this;
+}
+
+auto mIconViewItem::selected() const -> bool {
+  return state.selected;
+}
+
+auto mIconViewItem::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mIconViewItem::setSelected(bool selected) -> type& {
+  state.selected = selected;
+  signal(setSelected, selected);
+  return *this;
+}
+
+auto mIconViewItem::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mIconViewItem::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/icon-view.cpp b/hiro/core/widget/icon-view.cpp
new file mode 100644
index 00000000..283e1cc9
--- /dev/null
+++ b/hiro/core/widget/icon-view.cpp
@@ -0,0 +1,148 @@
+auto mIconView::allocate() -> pObject* {
+  return new pIconView(*this);
+}
+
+auto mIconView::destruct() -> void {
+  for(auto& item : state.items) item->destruct();
+  mWidget::destruct();
+}
+
+//
+
+auto mIconView::append(shared_pointer<mIconViewItem> item) -> type& {
+  state.items.append(item);
+  item->setParent(this, items() - 1);
+  signal(append, item);
+  return *this;
+}
+
+auto mIconView::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mIconView::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mIconView::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mIconView::doContext() const -> void {
+  if(state.onContext) return state.onContext();
+}
+
+auto mIconView::flow() const -> Orientation {
+  return state.flow;
+}
+
+auto mIconView::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mIconView::item(unsigned position) const -> shared_pointer<mIconViewItem> {
+  if(position >= items()) throw;
+  return state.items[position];
+}
+
+auto mIconView::items() const -> unsigned {
+  return state.items.size();
+}
+
+auto mIconView::multiSelect() const -> bool {
+  return state.multiSelect;
+}
+
+auto mIconView::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mIconView::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mIconView::onContext(const function<void ()>& function) -> type& {
+  state.onContext = function;
+  return *this;
+}
+
+auto mIconView::orientation() const -> Orientation {
+  return state.orientation;
+}
+
+auto mIconView::remove(shared_pointer<mIconViewItem> item) -> type& {
+  signal(remove, item);
+  state.items.remove(item->offset());
+  for(auto n : range(item->offset(), items())) {
+    state.items[n]->offset(-1);
+  }
+  item->setParent();
+  return *this;
+}
+
+auto mIconView::reset() -> type& {
+  signal(reset);
+  for(auto& item : state.items) item->setParent();
+  state.items.reset();
+  return *this;
+}
+
+auto mIconView::selected() const -> maybe<unsigned> {
+  for(auto& item : state.items) {
+    if(item->selected()) return (unsigned)item->offset();
+  }
+  return nothing;
+}
+
+auto mIconView::selectedItems() const -> vector<unsigned> {
+  vector<unsigned> result;
+  for(auto& item : state.items) {
+    if(item->selected()) result.append(item->offset());
+  }
+  return result;
+}
+
+auto mIconView::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mIconView::setFlow(Orientation flow) -> type& {
+  state.flow = flow;
+  signal(setFlow, flow);
+  return *this;
+}
+
+auto mIconView::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
+
+auto mIconView::setMultiSelect(bool multiSelect) -> type& {
+  state.multiSelect = multiSelect;
+  signal(setMultiSelect, multiSelect);
+  return *this;
+}
+
+auto mIconView::setOrientation(Orientation orientation) -> type& {
+  state.orientation = orientation;
+  signal(setOrientation, orientation);
+  return *this;
+}
+
+auto mIconView::setSelected(const vector<signed>& selections) -> type& {
+  bool selectAll = selections(0, 0) == ~0;
+  for(auto& item : state.items) item->state.selected = selectAll;
+  if(selectAll) return signal(setItemSelectedAll), *this;
+  if(!selections) return signal(setItemSelectedNone), *this;
+  for(auto& position : selections) {
+    if(position >= items()) continue;
+    state.items[position]->state.selected = true;
+  }
+  signal(setItemSelected, selections);
+  return *this;
+}
diff --git a/hiro/core/widget/label.cpp b/hiro/core/widget/label.cpp
new file mode 100644
index 00000000..d0e2df91
--- /dev/null
+++ b/hiro/core/widget/label.cpp
@@ -0,0 +1,37 @@
+auto mLabel::allocate() -> pObject* {
+  return new pLabel(*this);
+}
+
+//
+
+auto mLabel::horizontalAlignment() const -> double {
+  return state.horizontalAlignment;
+}
+
+auto mLabel::setHorizontalAlignment(double alignment) -> type& {
+  alignment = max(0.0, min(1.0, alignment));
+  state.horizontalAlignment = alignment;
+  signal(setHorizontalAlignment, alignment);
+  return *this;
+}
+
+auto mLabel::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mLabel::setVerticalAlignment(double alignment) -> type& {
+  alignment = max(0.0, min(1.0, alignment));
+  state.verticalAlignment = alignment;
+  signal(setVerticalAlignment, alignment);
+  return *this;
+}
+
+auto mLabel::text() const -> string {
+  return state.text;
+}
+
+auto mLabel::verticalAlignment() const -> double {
+  return state.verticalAlignment;
+}
diff --git a/hiro/core/widget/line-edit.cpp b/hiro/core/widget/line-edit.cpp
new file mode 100644
index 00000000..96672fe5
--- /dev/null
+++ b/hiro/core/widget/line-edit.cpp
@@ -0,0 +1,63 @@
+auto mLineEdit::allocate() -> pObject* {
+  return new pLineEdit(*this);
+}
+
+//
+
+auto mLineEdit::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mLineEdit::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mLineEdit::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mLineEdit::editable() const -> bool {
+  return state.editable;
+}
+
+auto mLineEdit::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mLineEdit::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mLineEdit::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mLineEdit::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mLineEdit::setEditable(bool editable) -> type& {
+  state.editable = editable;
+  signal(setEditable, editable);
+  return *this;
+}
+
+auto mLineEdit::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
+
+auto mLineEdit::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mLineEdit::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/list-view-column.cpp b/hiro/core/widget/list-view-column.cpp
new file mode 100644
index 00000000..cb180748
--- /dev/null
+++ b/hiro/core/widget/list-view-column.cpp
@@ -0,0 +1,135 @@
+auto mListViewColumn::allocate() -> pObject* {
+  return new pListViewColumn(*this);
+}
+
+//
+
+auto mListViewColumn::active() const -> bool {
+  if(auto listView = parentListView()) return listView->state.activeColumn == offset();
+  return false;
+}
+
+auto mListViewColumn::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mListViewColumn::editable() const -> bool {
+  return state.editable;
+}
+
+auto mListViewColumn::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mListViewColumn::horizontalAlignment() const -> double {
+  return state.horizontalAlignment;
+}
+
+auto mListViewColumn::icon() const -> image {
+  return state.icon;
+}
+
+auto mListViewColumn::remove() -> type& {
+  if(auto listView = parentListView()) listView->remove(*this);
+  return *this;
+}
+
+auto mListViewColumn::resizable() const -> bool {
+  return state.resizable;
+}
+
+auto mListViewColumn::setActive() -> type& {
+  if(auto listView = parentListView()) listView->state.activeColumn = offset();
+  signal(setActive);
+  return *this;
+}
+
+auto mListViewColumn::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mListViewColumn::setEditable(bool editable) -> type& {
+  state.editable = editable;
+  signal(setEditable, editable);
+  return *this;
+}
+
+auto mListViewColumn::setFont(const string& font) -> type& {
+  state.font = font;
+  signal(setFont, this->font(true));
+  return *this;
+}
+
+auto mListViewColumn::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
+
+auto mListViewColumn::setHorizontalAlignment(double alignment) -> type& {
+  alignment = max(0.0, min(1.0, alignment));
+  state.horizontalAlignment = alignment;
+  signal(setHorizontalAlignment, alignment);
+  return *this;
+}
+
+auto mListViewColumn::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mListViewColumn::setResizable(bool resizable) -> type& {
+  state.resizable = resizable;
+  signal(setResizable, resizable);
+  return *this;
+}
+
+auto mListViewColumn::setSortable(bool sortable) -> type& {
+  state.sortable = sortable;
+  signal(setSortable, sortable);
+  return *this;
+}
+
+auto mListViewColumn::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mListViewColumn::setVerticalAlignment(double alignment) -> type& {
+  alignment = max(0.0, min(1.0, alignment));
+  state.verticalAlignment = alignment;
+  signal(setVerticalAlignment, alignment);
+  return *this;
+}
+
+auto mListViewColumn::setVisible(bool visible) -> type& {
+  state.visible = visible;
+  signal(setVisible, visible);
+  return *this;
+}
+
+auto mListViewColumn::setWidth(signed width) -> type& {
+  state.width = width;
+  signal(setWidth, width);
+  return *this;
+}
+
+auto mListViewColumn::sortable() const -> bool {
+  return state.sortable;
+}
+
+auto mListViewColumn::text() const -> string {
+  return state.text;
+}
+
+auto mListViewColumn::verticalAlignment() const -> double {
+  return state.verticalAlignment;
+}
+
+auto mListViewColumn::width() const -> signed {
+  return state.width;
+}
diff --git a/hiro/core/widget/list-view-item.cpp b/hiro/core/widget/list-view-item.cpp
new file mode 100644
index 00000000..bdc5ad88
--- /dev/null
+++ b/hiro/core/widget/list-view-item.cpp
@@ -0,0 +1,65 @@
+auto mListViewItem::allocate() -> pObject* {
+  return new pListViewItem(*this);
+}
+
+//
+
+auto mListViewItem::checked() const -> bool {
+  return state.checked;
+}
+
+auto mListViewItem::icon(unsigned column) const -> image {
+  return state.icon(column, {});
+}
+
+auto mListViewItem::remove() -> type& {
+  if(auto listView = parentListView()) listView->remove(*this);
+  return *this;
+}
+
+auto mListViewItem::selected() const -> bool {
+  return state.selected;
+}
+
+auto mListViewItem::setChecked(bool checked) -> type& {
+  state.checked = checked;
+  signal(setChecked, checked);
+  return *this;
+}
+
+auto mListViewItem::setFocused() -> type& {
+  signal(setFocused);
+  return *this;
+}
+
+auto mListViewItem::setIcon(unsigned column, const image& icon) -> type& {
+  state.icon(column) = icon;
+  signal(setIcon, column, icon);
+  return *this;
+}
+
+auto mListViewItem::setSelected(bool selected) -> type& {
+  state.selected = selected;
+  signal(setSelected, selected);
+  return *this;
+}
+
+auto mListViewItem::setText(const lstring& text) -> type& {
+  state.text = text;
+  if(auto listView = parentListView()) {
+    for(auto column : range(listView->columns())) {
+      setText(column, text(column, ""));
+    }
+  }
+  return *this;
+}
+
+auto mListViewItem::setText(unsigned column, const string& text) -> type& {
+  state.text(column) = text;
+  signal(setText, column, text);
+  return *this;
+}
+
+auto mListViewItem::text(unsigned column) const -> string {
+  return state.text(column, "");
+}
diff --git a/hiro/core/widget/list-view.cpp b/hiro/core/widget/list-view.cpp
new file mode 100644
index 00000000..7c9e915d
--- /dev/null
+++ b/hiro/core/widget/list-view.cpp
@@ -0,0 +1,228 @@
+auto mListView::allocate() -> pObject* {
+  return new pListView(*this);
+}
+
+auto mListView::destruct() -> void {
+  for(auto& item : state.items) item->destruct();
+  for(auto& column : state.columns) column->destruct();
+  mWidget::destruct();
+}
+
+//
+
+auto mListView::append(sListViewColumn column) -> type& {
+  state.columns.append(column);
+  column->setParent(this, columns() - 1);
+  signal(append, column);
+  return *this;
+}
+
+auto mListView::append(sListViewItem item) -> type& {
+  state.items.append(item);
+  item->setParent(this, items() - 1);
+  signal(append, item);
+  return *this;
+}
+
+auto mListView::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mListView::checkable() const -> bool {
+  return state.checkable;
+}
+
+auto mListView::checked() const -> vector<sListViewItem> {
+  vector<sListViewItem> items;
+  for(auto& item : state.items) {
+    if(item->checked()) items.append(item);
+  }
+  return items;
+}
+
+auto mListView::column(unsigned position) const -> sListViewColumn {
+  if(position < columns()) return state.columns[position];
+  return {};
+}
+
+auto mListView::columns() const -> unsigned {
+  return state.columns.size();
+}
+
+auto mListView::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mListView::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mListView::doContext() const -> void {
+  if(state.onContext) return state.onContext();
+}
+
+auto mListView::doEdit(sListViewItem item, sListViewColumn column) const -> void {
+  if(state.onEdit) return state.onEdit(item, column);
+}
+
+auto mListView::doSort(sListViewColumn column) const -> void {
+  if(state.onSort) return state.onSort(column);
+}
+
+auto mListView::doToggle(sListViewItem item) const -> void {
+  if(state.onToggle) return state.onToggle(item);
+}
+
+auto mListView::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mListView::gridVisible() const -> bool {
+  return state.gridVisible;
+}
+
+auto mListView::headerVisible() const -> bool {
+  return state.headerVisible;
+}
+
+auto mListView::item(unsigned position) const -> sListViewItem {
+  if(position < items()) return state.items[position];
+  return {};
+}
+
+auto mListView::items() const -> unsigned {
+  return state.items.size();
+}
+
+auto mListView::multiSelect() const -> bool {
+  return state.multiSelect;
+}
+
+auto mListView::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mListView::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mListView::onContext(const function<void ()>& function) -> type& {
+  state.onContext = function;
+  return *this;
+}
+
+auto mListView::onEdit(const function<void (sListViewItem, sListViewColumn)>& function) -> type& {
+  state.onEdit = function;
+  return *this;
+}
+
+auto mListView::onSort(const function<void (sListViewColumn)>& function) -> type& {
+  state.onSort = function;
+  return *this;
+}
+
+auto mListView::onToggle(const function<void (sListViewItem)>& function) -> type& {
+  state.onToggle = function;
+  return *this;
+}
+
+auto mListView::remove(sListViewColumn column) -> type& {
+  signal(remove, column);
+  for(auto& item : state.items) item->setParent();
+  state.items.reset();
+  state.columns.remove(column->offset());
+  for(auto n : range(column->offset(), columns())) {
+    state.columns[n]->offset(-1);
+  }
+  column->setParent();
+  return *this;
+}
+
+auto mListView::remove(sListViewItem item) -> type& {
+  signal(remove, item);
+  state.items.remove(item->offset());
+  for(auto n : range(item->offset(), items())) {
+    state.items[n]->offset(-1);
+  }
+  item->setParent();
+  return *this;
+}
+
+auto mListView::reset() -> type& {
+  signal(reset);
+  for(auto& column : state.columns) column->setParent();
+  state.columns.reset();
+  for(auto& item : state.items) item->setParent();
+  state.items.reset();
+  return *this;
+}
+
+auto mListView::resizeColumns() -> type& {
+  signal(resizeColumns);
+  return *this;
+}
+
+auto mListView::selected() const -> sListViewItem {
+  for(auto& item : state.items) {
+    if(item->selected()) return item;
+  }
+  return {};
+}
+
+auto mListView::selectedItems() const -> vector<sListViewItem> {
+  vector<sListViewItem> items;
+  for(auto& item : state.items) {
+    if(item->selected()) items.append(item);
+  }
+  return items;
+}
+
+auto mListView::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mListView::setCheckable(bool checkable) -> type& {
+  state.checkable = checkable;
+  signal(setCheckable, checkable);
+  return *this;
+}
+
+auto mListView::setChecked(bool checked) -> type& {
+  for(auto& item : state.items) item->state.checked = checked;
+  signal(setChecked, checked);
+  return *this;
+}
+
+auto mListView::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
+
+auto mListView::setGridVisible(bool visible) -> type& {
+  state.gridVisible = visible;
+  signal(setGridVisible, visible);
+  return *this;
+}
+
+auto mListView::setHeaderVisible(bool visible) -> type& {
+  state.headerVisible = visible;
+  signal(setHeaderVisible, visible);
+  return *this;
+}
+
+auto mListView::setMultiSelect(bool multiSelect) -> type& {
+  state.multiSelect = multiSelect;
+  signal(setMultiSelect, multiSelect);
+  return *this;
+}
+
+auto mListView::setSelected(bool selected) -> type& {
+  for(auto& item : state.items) item->state.selected = selected;
+  signal(setSelected, selected);
+  return *this;
+}
diff --git a/hiro/core/widget/progress-bar.cpp b/hiro/core/widget/progress-bar.cpp
new file mode 100644
index 00000000..726cfaec
--- /dev/null
+++ b/hiro/core/widget/progress-bar.cpp
@@ -0,0 +1,15 @@
+auto mProgressBar::allocate() -> pObject* {
+  return new pProgressBar(*this);
+}
+
+//
+
+auto mProgressBar::position() const -> unsigned {
+  return state.position;
+}
+
+auto mProgressBar::setPosition(unsigned position) -> type& {
+  state.position = position;
+  signal(setPosition, position);
+  return *this;
+}
diff --git a/hiro/core/widget/radio-button.cpp b/hiro/core/widget/radio-button.cpp
new file mode 100644
index 00000000..cf64cfd8
--- /dev/null
+++ b/hiro/core/widget/radio-button.cpp
@@ -0,0 +1,81 @@
+auto mRadioButton::allocate() -> pObject* {
+  return new pRadioButton(*this);
+}
+
+//
+
+auto mRadioButton::group(const vector<shared_pointer_weak<mRadioButton>>& group) -> void {
+  for(auto& weak : group) {
+    if(auto item = weak.acquire()) item->state.group = group;
+  }
+  for(auto& weak : group) {
+    if(auto item = weak.acquire()) {
+      if(item->self()) item->self()->setGroup(group);
+    }
+  }
+  if(group.size()) {
+    if(auto item = group.first().acquire()) item->setChecked();
+  }
+}
+
+auto mRadioButton::bordered() const -> bool {
+  return state.bordered;
+}
+
+auto mRadioButton::checked() const -> bool {
+  return state.checked;
+}
+
+auto mRadioButton::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mRadioButton::icon() const -> image {
+  return state.icon;
+}
+
+auto mRadioButton::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mRadioButton::orientation() const -> Orientation {
+  return state.orientation;
+}
+
+auto mRadioButton::setBordered(bool bordered) -> type& {
+  state.bordered = bordered;
+  signal(setBordered, bordered);
+  return *this;
+}
+
+auto mRadioButton::setChecked() -> type& {
+  for(auto& weak : state.group) {
+    if(auto item = weak.acquire()) item->state.checked = false;
+  }
+  state.checked = true;
+  signal(setChecked);
+  return *this;
+}
+
+auto mRadioButton::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mRadioButton::setOrientation(Orientation orientation) -> type& {
+  state.orientation = orientation;
+  signal(setOrientation, orientation);
+  return *this;
+}
+
+auto mRadioButton::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mRadioButton::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/radio-label.cpp b/hiro/core/widget/radio-label.cpp
new file mode 100644
index 00000000..9142643f
--- /dev/null
+++ b/hiro/core/widget/radio-label.cpp
@@ -0,0 +1,51 @@
+auto mRadioLabel::allocate() -> pObject* {
+  return new pRadioLabel(*this);
+}
+
+//
+
+auto mRadioLabel::group(const vector<shared_pointer_weak<mRadioLabel>>& group) -> void {
+  for(auto& weak : group) {
+    if(auto item = weak.acquire()) item->state.group = group;
+  }
+  for(auto& weak : group) {
+    if(auto item = weak.acquire()) {
+      if(item->self()) item->self()->setGroup(group);
+    }
+  }
+  if(group.size()) {
+    if(auto item = group.first().acquire()) item->setChecked();
+  }
+}
+
+auto mRadioLabel::checked() const -> bool {
+  return state.checked;
+}
+
+auto mRadioLabel::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mRadioLabel::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mRadioLabel::setChecked() -> type& {
+  for(auto& weak : state.group) {
+    if(auto item = weak.acquire()) item->state.checked = false;
+  }
+  state.checked = true;
+  signal(setChecked);
+  return *this;
+}
+
+auto mRadioLabel::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mRadioLabel::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/source-edit.cpp b/hiro/core/widget/source-edit.cpp
new file mode 100644
index 00000000..d9dd0f8e
--- /dev/null
+++ b/hiro/core/widget/source-edit.cpp
@@ -0,0 +1,52 @@
+auto mSourceEdit::allocate() -> pObject* {
+  return new pSourceEdit(*this);
+}
+
+//
+
+auto mSourceEdit::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mSourceEdit::doMove() const -> void {
+  if(state.onMove) return state.onMove();
+}
+
+auto mSourceEdit::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mSourceEdit::onMove(const function<void ()>& function) -> type& {
+  state.onMove = function;
+  return *this;
+}
+
+auto mSourceEdit::position() const -> unsigned {
+  return state.position;
+}
+
+auto mSourceEdit::setPosition(signed position) -> type& {
+  state.position = position;
+  signal(setPosition, position);
+  return *this;
+}
+
+auto mSourceEdit::setSelected(Position selected) -> type& {
+  if(selected.x() < 0) return *this;
+  if(selected.y() < 0) selected.setY(-1);
+  else if(selected.x() > selected.y()) return *this;
+  state.selected = selected;
+  signal(setSelected, selected);
+  return *this;
+}
+
+auto mSourceEdit::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mSourceEdit::text() const -> string {
+  return signal(text);
+}
diff --git a/hiro/core/widget/tab-frame-item.cpp b/hiro/core/widget/tab-frame-item.cpp
new file mode 100644
index 00000000..0eae926a
--- /dev/null
+++ b/hiro/core/widget/tab-frame-item.cpp
@@ -0,0 +1,95 @@
+auto mTabFrameItem::allocate() -> pObject* {
+  return new pTabFrameItem(*this);
+}
+
+auto mTabFrameItem::destruct() -> void {
+  if(auto& layout = state.layout) layout->destruct();
+  mObject::destruct();
+}
+
+//
+
+auto mTabFrameItem::append(sLayout layout) -> type& {
+  if(auto& layout = state.layout) remove(layout);
+  state.layout = layout;
+  layout->setParent(this, 0);
+  return *this;
+}
+
+auto mTabFrameItem::closable() const -> bool {
+  return state.closable;
+}
+
+auto mTabFrameItem::icon() const -> image {
+  return state.icon;
+}
+
+auto mTabFrameItem::layout() const -> sLayout {
+  return state.layout;
+}
+
+auto mTabFrameItem::movable() const -> bool {
+  return state.movable;
+}
+
+auto mTabFrameItem::remove() -> type& {
+  if(auto tabFrame = parentTabFrame()) tabFrame->remove(*this);
+  return *this;
+}
+
+auto mTabFrameItem::remove(sLayout layout) -> type& {
+  layout->setParent();
+  state.layout.reset();
+  return *this;
+}
+
+auto mTabFrameItem::reset() -> type& {
+  if(auto layout = state.layout) remove(layout);
+  return *this;
+}
+
+auto mTabFrameItem::selected() const -> bool {
+  if(auto tabFrame = parentTabFrame()) return offset() == tabFrame->state.selected;
+  return false;
+}
+
+auto mTabFrameItem::setClosable(bool closable) -> type& {
+  state.closable = closable;
+  signal(setClosable, closable);
+  return *this;
+}
+
+auto mTabFrameItem::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mTabFrameItem::setMovable(bool movable) -> type& {
+  state.movable = movable;
+  signal(setMovable, movable);
+  return *this;
+}
+
+auto mTabFrameItem::setParent(mObject* parent, signed offset) -> type& {
+  if(auto layout = state.layout) layout->destruct();
+  mObject::setParent(parent, offset);
+  if(auto layout = state.layout) layout->setParent(this, layout->offset());
+  return *this;
+}
+
+auto mTabFrameItem::setSelected() -> type& {
+  if(auto tabFrame = parentTabFrame()) tabFrame->state.selected = offset();
+  signal(setSelected);
+  return *this;
+}
+
+auto mTabFrameItem::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mTabFrameItem::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/tab-frame.cpp b/hiro/core/widget/tab-frame.cpp
new file mode 100644
index 00000000..c50ff819
--- /dev/null
+++ b/hiro/core/widget/tab-frame.cpp
@@ -0,0 +1,90 @@
+auto mTabFrame::allocate() -> pObject* {
+  return new pTabFrame(*this);
+}
+
+auto mTabFrame::destruct() -> void {
+  for(auto& item : state.items) item->destruct();
+  mWidget::destruct();
+}
+
+//
+
+auto mTabFrame::append(sTabFrameItem item) -> type& {
+  state.items.append(item);
+  item->setParent(this, items() - 1);
+  signal(append, item);
+  return *this;
+}
+
+auto mTabFrame::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mTabFrame::doClose(sTabFrameItem item) const -> void {
+  if(state.onClose) return state.onClose(item);
+}
+
+auto mTabFrame::doMove(sTabFrameItem from, sTabFrameItem to) const -> void {
+  if(state.onMove) return state.onMove(from, to);
+}
+
+auto mTabFrame::edge() const -> Edge {
+  return state.edge;
+}
+
+auto mTabFrame::item(unsigned position) const -> sTabFrameItem {
+  if(position < items()) return state.items[position];
+  return {};
+}
+
+auto mTabFrame::items() const -> unsigned {
+  return state.items.size();
+}
+
+auto mTabFrame::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mTabFrame::onClose(const function<void (sTabFrameItem)>& function) -> type& {
+  state.onClose = function;
+  return *this;
+}
+
+auto mTabFrame::onMove(const function<void (sTabFrameItem, sTabFrameItem)>& function) -> type& {
+  state.onMove = function;
+  return *this;
+}
+
+auto mTabFrame::remove(sTabFrameItem item) -> type& {
+  auto offset = item->offset();
+  item->setParent();
+  signal(remove, item);
+  state.items.remove(item->offset());
+  for(auto n : range(offset, items())) {
+    state.items[n]->offset(-1);
+  }
+  return *this;
+}
+
+auto mTabFrame::reset() -> type& {
+  while(state.items) remove(state.items.last());
+  return *this;
+}
+
+auto mTabFrame::selected() const -> sTabFrameItem {
+  return state.items[state.selected];
+}
+
+auto mTabFrame::setEdge(Edge edge) -> type& {
+  state.edge = edge;
+  signal(setEdge, edge);
+  return *this;
+}
+
+auto mTabFrame::setParent(mObject* parent, signed offset) -> type& {
+  for(auto& item : state.items) item->destruct();
+  mObject::setParent(parent, offset);
+  for(auto& item : state.items) item->setParent(this, item->offset());
+  return *this;
+}
diff --git a/hiro/core/widget/text-edit.cpp b/hiro/core/widget/text-edit.cpp
new file mode 100644
index 00000000..ae0982b4
--- /dev/null
+++ b/hiro/core/widget/text-edit.cpp
@@ -0,0 +1,83 @@
+auto mTextEdit::allocate() -> pObject* {
+  return new pTextEdit(*this);
+}
+
+//
+
+auto mTextEdit::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mTextEdit::cursorPosition() const -> unsigned {
+  return state.cursorPosition;
+}
+
+auto mTextEdit::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mTextEdit::doMove() const -> void {
+  if(state.onMove) return state.onMove();
+}
+
+auto mTextEdit::editable() const -> bool {
+  return state.editable;
+}
+
+auto mTextEdit::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mTextEdit::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mTextEdit::onMove(const function<void ()>& function) -> type& {
+  state.onMove = function;
+  return *this;
+}
+
+auto mTextEdit::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mTextEdit::setCursorPosition(unsigned position) -> type& {
+  state.cursorPosition = position;
+  signal(setCursorPosition, position);
+  return *this;
+}
+
+auto mTextEdit::setEditable(bool editable) -> type& {
+  state.editable = editable;
+  signal(setEditable, editable);
+  return *this;
+}
+
+auto mTextEdit::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
+
+auto mTextEdit::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mTextEdit::setWordWrap(bool wordWrap) -> type& {
+  state.wordWrap = wordWrap;
+  signal(setWordWrap, wordWrap);
+  return *this;
+}
+
+auto mTextEdit::text() const -> string {
+  return signal(text);
+}
+
+auto mTextEdit::wordWrap() const -> bool {
+  return state.wordWrap;
+}
diff --git a/hiro/core/widget/tree-view-item.cpp b/hiro/core/widget/tree-view-item.cpp
new file mode 100644
index 00000000..40995fdf
--- /dev/null
+++ b/hiro/core/widget/tree-view-item.cpp
@@ -0,0 +1,98 @@
+auto mTreeViewItem::allocate() -> pObject* {
+  return new pTreeViewItem(*this);
+}
+
+auto mTreeViewItem::destruct() -> void {
+  for(auto& item : state.items) item->destruct();
+  mObject::destruct();
+}
+
+//
+
+auto mTreeViewItem::append(sTreeViewItem item) -> type& {
+  state.items.append(item);
+  item->setParent(this, items() - 1);
+  signal(append, item);
+  return *this;
+}
+
+auto mTreeViewItem::checked() const -> bool {
+  return state.checked;
+}
+
+auto mTreeViewItem::icon() const -> image {
+  return state.icon;
+}
+
+auto mTreeViewItem::item(const string& path) const -> sTreeViewItem {
+  if(path.empty()) return {};
+  auto paths = path.split("/");
+  unsigned position = decimal(paths.takeFirst());
+  if(position >= items()) return {};
+  if(paths.empty()) return state.items[position];
+  return state.items[position]->item(paths.merge("/"));
+}
+
+auto mTreeViewItem::items() const -> unsigned {
+  return state.items.size();
+}
+
+auto mTreeViewItem::path() const -> string {
+  if(auto treeViewItem = parentTreeViewItem()) return {treeViewItem->path(), "/", offset()};
+  return {offset()};
+}
+
+auto mTreeViewItem::remove() -> type& {
+  if(auto treeView = parentTreeView()) treeView->remove(*this);
+  if(auto treeViewItem = parentTreeViewItem()) treeViewItem->remove(*this);
+  return *this;
+}
+
+auto mTreeViewItem::remove(sTreeViewItem item) -> type& {
+  signal(remove, item);
+  state.items.remove(item->offset());
+  for(auto n : range(item->offset(), items())) {
+    state.items[n]->offset(-1);
+  }
+  item->setParent();
+  return *this;
+}
+
+auto mTreeViewItem::selected() const -> bool {
+  if(auto treeView = parentTreeView(true)) {
+    return path() == treeView->state.selectedPath;
+  }
+  return false;
+}
+
+auto mTreeViewItem::setChecked(bool checked) -> type& {
+  state.checked = checked;
+  signal(setChecked, checked);
+  return *this;
+}
+
+auto mTreeViewItem::setFocused() -> type& {
+  signal(setFocused);
+  return *this;
+}
+
+auto mTreeViewItem::setIcon(const image& icon) -> type& {
+  state.icon = icon;
+  signal(setIcon, icon);
+  return *this;
+}
+
+auto mTreeViewItem::setSelected() -> type& {
+  signal(setSelected);
+  return *this;
+}
+
+auto mTreeViewItem::setText(const string& text) -> type& {
+  state.text = text;
+  signal(setText, text);
+  return *this;
+}
+
+auto mTreeViewItem::text() const -> string {
+  return state.text;
+}
diff --git a/hiro/core/widget/tree-view.cpp b/hiro/core/widget/tree-view.cpp
new file mode 100644
index 00000000..217638e2
--- /dev/null
+++ b/hiro/core/widget/tree-view.cpp
@@ -0,0 +1,128 @@
+auto mTreeView::allocate() -> pObject* {
+  return new pTreeView(*this);
+}
+
+auto mTreeView::destruct() -> void {
+  for(auto& item : state.items) item->destruct();
+  mWidget::destruct();
+}
+
+//
+
+auto mTreeView::append(sTreeViewItem item) -> type& {
+  state.items.append(item);
+  item->setParent(this, items() - 1);
+  signal(append, item);
+  return *this;
+}
+
+auto mTreeView::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mTreeView::checkable() const -> bool {
+  return state.checkable;
+}
+
+auto mTreeView::collapse() -> type& {
+  signal(collapse);
+  return *this;
+}
+
+auto mTreeView::doActivate() const -> void {
+  if(state.onActivate) return state.onActivate();
+}
+
+auto mTreeView::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mTreeView::doContext() const -> void {
+  if(state.onContext) return state.onContext();
+}
+
+auto mTreeView::doToggle(sTreeViewItem item) const -> void {
+  if(state.onToggle) return state.onToggle(item);
+}
+
+auto mTreeView::expand() -> type& {
+  signal(expand);
+  return *this;
+}
+
+auto mTreeView::foregroundColor() const -> Color {
+  return state.foregroundColor;
+}
+
+auto mTreeView::item(const string& path) const -> sTreeViewItem {
+  if(path.empty()) return {};
+  auto paths = path.split("/");
+  unsigned position = decimal(paths.takeFirst());
+  if(position >= items()) return {};
+  if(paths.empty()) return state.items[position];
+  return state.items[position]->item(paths.merge("/"));
+}
+
+auto mTreeView::items() const -> unsigned {
+  return state.items.size();
+}
+
+auto mTreeView::onActivate(const function<void ()>& function) -> type& {
+  state.onActivate = function;
+  return *this;
+}
+
+auto mTreeView::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mTreeView::onContext(const function<void ()>& function) -> type& {
+  state.onContext = function;
+  return *this;
+}
+
+auto mTreeView::onToggle(const function<void (sTreeViewItem)>& function) -> type& {
+  state.onToggle = function;
+  return *this;
+}
+
+auto mTreeView::remove(sTreeViewItem item) -> type& {
+  signal(remove, item);
+  state.items.remove(item->offset());
+  for(auto n : range(item->offset(), items())) {
+    state.items[n]->offset(-1);
+  }
+  item->setParent();
+  return *this;
+}
+
+auto mTreeView::reset() -> type& {
+  state.selectedPath.reset();
+  signal(reset);
+  for(auto& item : state.items) item->setParent();
+  state.items.reset();
+  return *this;
+}
+
+auto mTreeView::selected() const -> sTreeViewItem {
+  return item(state.selectedPath);
+}
+
+auto mTreeView::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mTreeView::setCheckable(bool checkable) -> type& {
+  state.checkable = checkable;
+  signal(setCheckable, checkable);
+  return *this;
+}
+
+auto mTreeView::setForegroundColor(Color color) -> type& {
+  state.foregroundColor = color;
+  signal(setForegroundColor, color);
+  return *this;
+}
diff --git a/hiro/core/widget/vertical-scroller.cpp b/hiro/core/widget/vertical-scroller.cpp
new file mode 100644
index 00000000..fb7f41ad
--- /dev/null
+++ b/hiro/core/widget/vertical-scroller.cpp
@@ -0,0 +1,34 @@
+auto mVerticalScroller::allocate() -> pObject* {
+  return new pVerticalScroller(*this);
+}
+
+//
+
+auto mVerticalScroller::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mVerticalScroller::length() const -> unsigned {
+  return state.length;
+}
+
+auto mVerticalScroller::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mVerticalScroller::position() const -> unsigned {
+  return state.position;
+}
+
+auto mVerticalScroller::setLength(unsigned length) -> type& {
+  state.length = length;
+  signal(setLength, length);
+  return *this;
+}
+
+auto mVerticalScroller::setPosition(unsigned position) -> type& {
+  state.position = position;
+  signal(setPosition, position);
+  return *this;
+}
diff --git a/hiro/core/widget/vertical-slider.cpp b/hiro/core/widget/vertical-slider.cpp
new file mode 100644
index 00000000..e9f51a8c
--- /dev/null
+++ b/hiro/core/widget/vertical-slider.cpp
@@ -0,0 +1,34 @@
+auto mVerticalSlider::allocate() -> pObject* {
+  return new pVerticalSlider(*this);
+}
+
+//
+
+auto mVerticalSlider::doChange() const -> void {
+  if(state.onChange) return state.onChange();
+}
+
+auto mVerticalSlider::length() const -> unsigned {
+  return state.length;
+}
+
+auto mVerticalSlider::onChange(const function<void ()>& function) -> type& {
+  state.onChange = function;
+  return *this;
+}
+
+auto mVerticalSlider::position() const -> unsigned {
+  return state.position;
+}
+
+auto mVerticalSlider::setLength(unsigned length) -> type& {
+  state.length = length;
+  signal(setLength, length);
+  return *this;
+}
+
+auto mVerticalSlider::setPosition(unsigned position) -> type& {
+  state.position = position;
+  signal(setPosition, position);
+  return *this;
+}
diff --git a/hiro/core/widget/viewport.cpp b/hiro/core/widget/viewport.cpp
new file mode 100644
index 00000000..b60ec9d9
--- /dev/null
+++ b/hiro/core/widget/viewport.cpp
@@ -0,0 +1,64 @@
+auto mViewport::allocate() -> pObject* {
+  return new pViewport(*this);
+}
+
+//
+
+auto mViewport::doDrop(lstring names) const -> void {
+  if(state.onDrop) return state.onDrop(names);
+}
+
+auto mViewport::doMouseLeave() const -> void {
+  if(state.onMouseLeave) return state.onMouseLeave();
+}
+
+auto mViewport::doMouseMove(Position position) const -> void {
+  if(state.onMouseMove) return state.onMouseMove(position);
+}
+
+auto mViewport::doMousePress(Mouse::Button button) const -> void {
+  if(state.onMousePress) return state.onMousePress(button);
+}
+
+auto mViewport::doMouseRelease(Mouse::Button button) const -> void {
+  if(state.onMouseRelease) return state.onMouseRelease(button);
+}
+
+auto mViewport::droppable() const -> bool {
+  return state.droppable;
+}
+
+auto mViewport::handle() const -> uintptr_t {
+  return signal(handle);
+}
+
+auto mViewport::onDrop(const function<void (lstring)>& function) -> type& {
+  state.onDrop = function;
+  return *this;
+}
+
+auto mViewport::onMouseLeave(const function<void ()>& function) -> type& {
+  state.onMouseLeave = function;
+  return *this;
+}
+
+auto mViewport::onMouseMove(const function<void (Position)>& function) -> type& {
+  state.onMouseMove = function;
+  return *this;
+}
+
+auto mViewport::onMousePress(const function<void (Mouse::Button)>& function) -> type& {
+  state.onMousePress = function;
+  return *this;
+}
+
+auto mViewport::onMouseRelease(const function<void (Mouse::Button)>& function) -> type& {
+  state.onMouseRelease = function;
+  return *this;
+}
+
+auto mViewport::setDroppable(bool droppable) -> type& {
+  state.droppable = droppable;
+  signal(setDroppable, droppable);
+  return *this;
+}
diff --git a/hiro/core/widget/widget.cpp b/hiro/core/widget/widget.cpp
new file mode 100644
index 00000000..599fdac4
--- /dev/null
+++ b/hiro/core/widget/widget.cpp
@@ -0,0 +1,20 @@
+auto mWidget::allocate() -> pObject* {
+  return new pWidget(*this);
+}
+
+//
+
+auto mWidget::doSize() const -> void {
+  if(state.onSize) return state.onSize();
+}
+
+auto mWidget::onSize(const function<void ()>& function) -> type& {
+  state.onSize = function;
+  return *this;
+}
+
+auto mWidget::remove() -> type& {
+  if(auto layout = parentLayout()) layout->remove(layout->sizable(offset()));
+  setParent();
+  return *this;
+}
diff --git a/hiro/core/window.cpp b/hiro/core/window.cpp
new file mode 100644
index 00000000..964ae940
--- /dev/null
+++ b/hiro/core/window.cpp
@@ -0,0 +1,271 @@
+auto mWindow::allocate() -> pObject* {
+  return new pWindow(*this);
+}
+
+auto mWindow::destruct() -> void {
+  if(auto& layout = state.layout) layout->destruct();
+  if(auto& menuBar = state.menuBar) menuBar->destruct();
+  if(auto& statusBar = state.statusBar) statusBar->destruct();
+  mObject::destruct();
+}
+
+//
+
+auto mWindow::append(shared_pointer<mLayout> layout) -> type& {
+  if(auto& layout = state.layout) remove(layout);
+  state.layout = layout;
+  layout->setGeometry(geometry().setPosition(0, 0));
+  layout->setParent(this, 0);
+  layout->setGeometry(geometry().setPosition(0, 0));
+  return *this;
+}
+
+auto mWindow::append(shared_pointer<mMenuBar> menuBar) -> type& {
+  if(auto& menuBar = state.menuBar) remove(menuBar);
+  menuBar->setParent(this, 0);
+  state.menuBar = menuBar;
+  signal(append, menuBar);
+  return *this;
+}
+
+auto mWindow::append(shared_pointer<mStatusBar> statusBar) -> type& {
+  if(auto& statusBar = state.statusBar) remove(statusBar);
+  statusBar->setParent(this, 0);
+  state.statusBar = statusBar;
+  signal(append, statusBar);
+  return *this;
+}
+
+auto mWindow::backgroundColor() const -> Color {
+  return state.backgroundColor;
+}
+
+auto mWindow::doClose() const -> void {
+  if(state.onClose) return state.onClose();
+}
+
+auto mWindow::doDrop(lstring names) const -> void {
+  if(state.onDrop) return state.onDrop(names);
+}
+
+auto mWindow::doKeyPress(signed key) const -> void {
+  if(state.onKeyPress) return state.onKeyPress(key);
+}
+
+auto mWindow::doKeyRelease(signed key) const -> void {
+  if(state.onKeyRelease) return state.onKeyRelease(key);
+}
+
+auto mWindow::doMove() const -> void {
+  if(state.onMove) return state.onMove();
+}
+
+auto mWindow::doSize() const -> void {
+  if(state.onSize) return state.onSize();
+}
+
+auto mWindow::droppable() const -> bool {
+  return state.droppable;
+}
+
+auto mWindow::frameGeometry() const -> Geometry {
+  Geometry margin = signal(frameMargin);
+  return {
+    state.geometry.x() - margin.x(), state.geometry.y() - margin.y(),
+    state.geometry.width() + margin.width(), state.geometry.height() + margin.height()
+  };
+}
+
+auto mWindow::fullScreen() const -> bool {
+  return state.fullScreen;
+}
+
+auto mWindow::geometry() const -> Geometry {
+  return state.geometry;
+}
+
+auto mWindow::layout() const -> shared_pointer<mLayout> {
+  return state.layout;
+}
+
+auto mWindow::menuBar() const -> shared_pointer<mMenuBar> {
+  return state.menuBar;
+}
+
+auto mWindow::modal() const -> bool {
+  return state.modal;
+}
+
+auto mWindow::onClose(const function<void ()>& function) -> type& {
+  state.onClose = function;
+  return *this;
+}
+
+auto mWindow::onDrop(const function<void (lstring)>& function) -> type& {
+  state.onDrop = function;
+  return *this;
+}
+
+auto mWindow::onKeyPress(const function<void (signed)>& function) -> type& {
+  state.onKeyPress = function;
+  return *this;
+}
+
+auto mWindow::onKeyRelease(const function<void (signed)>& function) -> type& {
+  state.onKeyRelease = function;
+  return *this;
+}
+
+auto mWindow::onMove(const function<void ()>& function) -> type& {
+  state.onMove = function;
+  return *this;
+}
+
+auto mWindow::onSize(const function<void ()>& function) -> type& {
+  state.onSize = function;
+  return *this;
+}
+
+auto mWindow::remove(shared_pointer<mLayout> layout) -> type& {
+  layout->setParent();
+  state.layout.reset();
+  return *this;
+}
+
+auto mWindow::remove(shared_pointer<mMenuBar> menuBar) -> type& {
+  signal(remove, menuBar);
+  menuBar->reset();
+  menuBar->setParent();
+  state.menuBar.reset();
+  return *this;
+}
+
+auto mWindow::remove(shared_pointer<mStatusBar> statusBar) -> type& {
+  signal(remove, statusBar);
+  statusBar->setParent();
+  state.statusBar.reset();
+  return *this;
+}
+
+auto mWindow::reset() -> type& {
+  if(auto& layout = state.layout) remove(layout);
+  if(auto& menuBar = state.menuBar) remove(menuBar);
+  if(auto& statusBar = state.statusBar) remove(statusBar);
+  return *this;
+}
+
+auto mWindow::resizable() const -> bool {
+  return state.resizable;
+}
+
+auto mWindow::setBackgroundColor(Color color) -> type& {
+  state.backgroundColor = color;
+  signal(setBackgroundColor, color);
+  return *this;
+}
+
+auto mWindow::setCentered() -> type& {
+  Geometry workspace = Desktop::workspace();
+  Geometry geometry = frameGeometry();
+  signed x = workspace.x();
+  signed y = workspace.y();
+  if(workspace.width() > geometry.width()) x += (workspace.width() - geometry.width()) / 2;
+  if(workspace.height() > geometry.height()) y += (workspace.height() - geometry.height()) / 2;
+  return setFrameGeometry({x, y, geometry.width(), geometry.height()});
+}
+
+auto mWindow::setCentered(shared_pointer<mWindow> parent) -> type& {
+  if(!parent) return setCentered();
+  Geometry workspace = parent->frameGeometry();
+  Geometry geometry = frameGeometry();
+  signed x = workspace.x();
+  signed y = workspace.y();
+  if(workspace.width() > geometry.width()) x += (workspace.width() - geometry.width()) / 2;
+  if(workspace.height() > geometry.height()) y += (workspace.height() - geometry.height()) / 2;
+  return setFrameGeometry({x, y, geometry.width(), geometry.height()});
+}
+
+auto mWindow::setDroppable(bool droppable) -> type& {
+  state.droppable = droppable;
+  signal(setDroppable, droppable);
+  return *this;
+}
+
+auto mWindow::setFrameGeometry(Geometry geometry) -> type& {
+  Geometry margin = signal(frameMargin);
+  return setGeometry({
+    geometry.x() + margin.x(), geometry.y() + margin.y(),
+    geometry.width() - margin.width(), geometry.height() - margin.height()
+  });
+}
+
+auto mWindow::setFramePosition(Position position) -> type& {
+  Geometry margin = signal(frameMargin);
+  return setGeometry({
+    position.x() + margin.x(), position.y() + margin.y(),
+    state.geometry.width(), state.geometry.height()
+  });
+}
+
+auto mWindow::setFrameSize(Size size) -> type& {
+  Geometry margin = signal(frameMargin);
+  return setGeometry({
+    state.geometry.x(), state.geometry.y(),
+    size.width() - margin.width(), size.height() - margin.height()
+  });
+}
+
+auto mWindow::setFullScreen(bool fullScreen) -> type& {
+  state.fullScreen = fullScreen;
+  signal(setFullScreen, fullScreen);
+  return *this;
+}
+
+auto mWindow::setGeometry(Geometry geometry) -> type& {
+  state.geometry = geometry;
+  signal(setGeometry, geometry);
+  if(auto& layout = state.layout) {
+    layout->setGeometry(geometry.setPosition(0, 0));
+  }
+  return *this;
+}
+
+auto mWindow::setModal(bool modal) -> type& {
+  state.modal = modal;
+  signal(setModal, modal);
+  return *this;
+}
+
+auto mWindow::setPosition(Position position) -> type& {
+  return setGeometry({
+    position.x(), position.y(),
+    state.geometry.width(), state.geometry.height()
+  });
+}
+
+auto mWindow::setResizable(bool resizable) -> type& {
+  state.resizable = resizable;
+  signal(setResizable, resizable);
+  return *this;
+}
+
+auto mWindow::setSize(Size size) -> type& {
+  return setGeometry({
+    state.geometry.x(), state.geometry.y(),
+    size.width(), size.height()
+  });
+}
+
+auto mWindow::setTitle(const string& title) -> type& {
+  state.title = title;
+  signal(setTitle, title);
+  return *this;
+}
+
+auto mWindow::statusBar() const -> shared_pointer<mStatusBar> {
+  return state.statusBar;
+}
+
+auto mWindow::title() const -> string {
+  return state.title;
+}
diff --git a/hiro/extension/browser-dialog.cpp b/hiro/extension/browser-dialog.cpp
new file mode 100644
index 00000000..232cb2b6
--- /dev/null
+++ b/hiro/extension/browser-dialog.cpp
@@ -0,0 +1,231 @@
+struct BrowserDialogWindow {
+  BrowserDialogWindow(BrowserDialog::State& state) : state(state) {}
+  auto accept() -> void;
+  auto activate() -> void;
+  auto change() -> void;
+  auto isFolder(const string& name) -> bool;
+  auto run() -> lstring;
+  auto setPath(string path) -> void;
+
+private:
+  Window window;
+    VerticalLayout layout{&window};
+      HorizontalLayout pathLayout{&layout, Size{~0, 0}, 8};
+        LineEdit pathName{&pathLayout, Size{~0, 0}, 0};
+        Button pathHome{&pathLayout, Size{0, 0}, 0};
+        Button pathRefresh{&pathLayout, Size{0, 0}, 0};
+        Button pathUp{&pathLayout, Size{0, 0}, 0};
+      ListView view{&layout, Size{~0, ~0}, 8};
+      HorizontalLayout controlLayout{&layout, Size{~0, 0}};
+        ComboButton filterList{&controlLayout, Size{120, 0}, 8};
+        LineEdit fileName{&controlLayout, Size{~0, 0}, 8};
+        Button acceptButton{&controlLayout, Size{80, 0}, 8};
+        Button cancelButton{&controlLayout, Size{80, 0}, 8};
+
+  BrowserDialog::State& state;
+};
+
+//accept button clicked, or enter pressed on file name line edit
+//also called by list view activate after special case handling
+auto BrowserDialogWindow::accept() -> void {
+  auto selectedItems = view.selectedItems();
+
+  if(state.action == "openFile" && selectedItems) {
+    string name = selectedItems.first()->text(0);
+    if(isFolder(name)) return setPath({state.path, name});
+    state.response.append(string{state.path, name});
+  }
+
+  if(state.action == "openFiles") {
+    for(auto selectedItem : selectedItems) {
+      string name = selectedItem->text(0);
+      state.response.append(string{state.path, name, isFolder(name) ? "/" : ""});
+    }
+  }
+
+  if(state.action == "saveFile") {
+    string name = fileName.text();
+    if(!name && selectedItems) name = selectedItems.first()->text(0);
+    if(!name || isFolder(name)) return;
+    if(file::exists({state.path, name})) {
+      if(MessageDialog("File already exists; overwrite it?").question() != 0) return;
+    }
+    state.response.append(string{state.path, name});
+  }
+
+  if(state.action == "selectFolder" && selectedItems) {
+    string name = selectedItems.first()->text(0);
+    if(isFolder(name)) state.response.append(string{state.path, name, "/"});
+  }
+
+  if(state.response) window.setModal(false);
+}
+
+//list view item double-clicked, or enter pressed on selected list view item
+auto BrowserDialogWindow::activate() -> void {
+  auto selectedItem = view.selected();
+  if(state.action == "saveFile" && selectedItem) {
+    string name = selectedItem->text(0);
+    if(isFolder(name)) return setPath({state.path, name});
+    fileName.setText(isFolder(name) ? "" : name);
+  }
+  if(state.action == "selectFolder" && selectedItem) {
+    string name = selectedItem->text(0);
+    if(isFolder(name)) return setPath({state.path, name});
+  }
+  accept();
+}
+
+//list view item changed
+auto BrowserDialogWindow::change() -> void {
+  fileName.setText("");
+  if(state.action == "saveFile") {
+    if(auto selectedItem = view.selected()) {
+      string name = selectedItem->text(0);
+      if(!isFolder(name)) fileName.setText(name);
+    }
+  }
+}
+
+auto BrowserDialogWindow::isFolder(const string& name) -> bool {
+  return directory::exists({state.path, name});
+}
+
+auto BrowserDialogWindow::run() -> lstring {
+  state.response.reset();
+
+  layout.setMargin(8);
+  pathName.onActivate([&] { setPath(pathName.text()); });
+  pathHome.onActivate([&] { setPath(userpath()); });
+  pathHome.setBordered(false).setIcon(Icon::Go::Home);
+  pathRefresh.onActivate([&] { setPath(state.path); });
+  pathRefresh.setBordered(false).setIcon(Icon::Action::Refresh);
+  pathUp.onActivate([&] { setPath(state.path.dirname()); });
+  pathUp.setBordered(false).setIcon(Icon::Go::Up);
+  view.onActivate([&] { activate(); });
+  view.onChange([&] { change(); });
+  view.setMultiSelect(state.action == "openFiles");
+  filterList.onChange([&] { setPath(state.path); });
+  for(auto& filter : state.filters) {
+    auto part = filter.split<1>("|");
+    filterList.append(ComboButtonItem().setText(part.first()));
+  }
+  filterList.setVisible(state.action != "selectFolder");
+  fileName.onActivate([&] { accept(); });
+  fileName.setVisible(state.action == "saveFile");
+  acceptButton.onActivate([&] { accept(); });
+  if(state.action == "openFile" || state.action == "openFiles") acceptButton.setText("Open");
+  if(state.action == "saveFile") acceptButton.setText("Save");
+  if(state.action == "selectFolder") acceptButton.setText("Select");
+  cancelButton.onActivate([&] { window.setModal(false); });
+  cancelButton.setText("Cancel");
+
+  setPath(state.path);
+
+  window.onClose([&] { window.setModal(false); });
+  window.onSize([&] { view.resizeColumns(); });
+  window.setTitle(state.title);
+  window.setSize({640, 480});
+  window.setCentered(state.parent);
+  window.setVisible();
+  view.resizeColumns();
+  window.setModal();
+  window.setVisible(false);
+
+  return state.response;
+}
+
+auto BrowserDialogWindow::setPath(string path) -> void {
+  path.transform("\\", "/");
+  if(!path.endsWith("/")) path.append("/");
+  pathName.setText(state.path = path);
+
+  view.reset();
+  view.append(ListViewColumn().setWidth(~0));
+  view.append(ListViewColumn().setWidth( 0).setForegroundColor({192, 128, 128}));
+
+  for(auto& folder : directory::folders(path)) {
+    ListViewItem item{&view};
+    item.setIcon(0, Icon::Emblem::Folder);
+    item.setText(0, folder.rtrim("/"));
+    item.setText(1, octal<3>(storage::mode({path, folder}) & 0777));
+  }
+
+  if(state.action != "selectFolder") {  //don't show files during folder selection
+    string filter = "*";
+    if(auto selected = filterList.selected()) {
+      auto part = state.filters[selected->offset()].split<1>("|");
+      filter = part.last();
+    }
+
+    for(auto& file : directory::files(path)) {
+      if(!file.match(filter)) continue;
+      ListViewItem item{&view};
+      item.setIcon(0, Icon::Emblem::File);
+      item.setText(0, file);
+      item.setText(1, octal<3>(storage::mode({path, file}) & 0777));
+    }
+  }
+
+  if(view.items()) view.setSelected({0});
+  Application::processEvents();
+  view.resizeColumns().setFocused().doChange();
+}
+
+//
+
+BrowserDialog::BrowserDialog() {
+}
+
+auto BrowserDialog::openFile() -> string {
+  state.action = "openFile";
+  if(!state.title) state.title = "Open File";
+  if(auto result = _run()) return result.first();
+  return {};
+}
+
+auto BrowserDialog::openFiles() -> lstring {
+  state.action = "openFiles";
+  if(!state.title) state.title = "Open Files";
+  if(auto result = _run()) return result;
+  return {};
+};
+
+auto BrowserDialog::saveFile() -> string {
+  state.action = "saveFile";
+  if(!state.title) state.title = "Save File";
+  if(auto result = _run()) return result.first();
+  return {};
+}
+
+auto BrowserDialog::selectFolder() -> string {
+  state.action = "selectFolder";
+  if(!state.title) state.title = "Select Folder";
+  if(auto result = _run()) return result.first();
+  return {};
+}
+
+auto BrowserDialog::setFilters(const lstring& filters) -> type& {
+  state.filters = filters;
+  return *this;
+}
+
+auto BrowserDialog::setParent(const shared_pointer<mWindow>& parent) -> type& {
+  state.parent = parent;
+  return *this;
+}
+
+auto BrowserDialog::setPath(const string& path) -> type& {
+  state.path = path;
+  return *this;
+}
+
+auto BrowserDialog::setTitle(const string& title) -> type& {
+  state.title = title;
+  return *this;
+}
+
+auto BrowserDialog::_run() -> lstring {
+  if(!state.path) state.path = userpath();
+  return BrowserDialogWindow(state).run();
+}
diff --git a/hiro/extension/browser-dialog.hpp b/hiro/extension/browser-dialog.hpp
new file mode 100644
index 00000000..510ab697
--- /dev/null
+++ b/hiro/extension/browser-dialog.hpp
@@ -0,0 +1,29 @@
+struct BrowserDialogWindow;
+
+struct BrowserDialog {
+  using type = BrowserDialog;
+
+  BrowserDialog();
+  auto openFile() -> nall::string;      //one existing file
+  auto openFiles() -> nall::lstring;    //any existing files or folders
+  auto saveFile() -> nall::string;      //one file
+  auto selectFolder() -> nall::string;  //one existing folder
+  auto setFilters(const nall::lstring& filters = {"All|*"}) -> type&;
+  auto setParent(const nall::shared_pointer<mWindow>& parent) -> type&;
+  auto setPath(const nall::string& path = "") -> type&;
+  auto setTitle(const nall::string& title = "") -> type&;
+
+private:
+  struct State {
+    nall::string action;
+    nall::lstring filters = {"*"};
+    nall::shared_pointer<mWindow> parent;
+    nall::string path;
+    nall::lstring response;
+    nall::string title;
+  } state;
+
+  auto _run() -> nall::lstring;
+
+  friend class BrowserDialogWindow;
+};
diff --git a/hiro/extension/extension.cpp b/hiro/extension/extension.cpp
new file mode 100644
index 00000000..aa46d451
--- /dev/null
+++ b/hiro/extension/extension.cpp
@@ -0,0 +1,11 @@
+#include "extension.hpp"
+using namespace nall;
+
+namespace hiro {
+  #include "../resource/resource.cpp"
+  #include "fixed-layout.cpp"
+  #include "horizontal-layout.cpp"
+  #include "vertical-layout.cpp"
+  #include "browser-dialog.cpp"
+  #include "message-dialog.cpp"
+}
diff --git a/hiro/extension/extension.hpp b/hiro/extension/extension.hpp
new file mode 100644
index 00000000..f3655abe
--- /dev/null
+++ b/hiro/extension/extension.hpp
@@ -0,0 +1,9 @@
+namespace hiro {
+  #include "../resource/resource.hpp"
+  #include "fixed-layout.hpp"
+  #include "horizontal-layout.hpp"
+  #include "vertical-layout.hpp"
+  #include "shared.hpp"
+  #include "browser-dialog.hpp"
+  #include "message-dialog.hpp"
+}
diff --git a/hiro/extension/fixed-layout.cpp b/hiro/extension/fixed-layout.cpp
new file mode 100644
index 00000000..bb976269
--- /dev/null
+++ b/hiro/extension/fixed-layout.cpp
@@ -0,0 +1,51 @@
+auto mFixedLayout::append(shared_pointer<mSizable> sizable, Geometry geometry) -> type& {
+  properties.append({geometry});
+  mLayout::append(sizable);
+  sizable->setGeometry(geometry);
+  return *this;
+}
+
+auto mFixedLayout::minimumSize() const -> Size {
+  signed width = Size::Minimum, height = Size::Minimum;
+  for(auto n : range(sizables())) {
+    width  = max(width,  sizable(n)->minimumSize().width());
+    height = max(height, sizable(n)->minimumSize().height());
+  }
+  return {width, height};
+}
+
+auto mFixedLayout::remove(shared_pointer<mSizable> sizable) -> type& {
+  properties.remove(sizable->offset());
+  mLayout::remove(sizable);
+  return *this;
+}
+
+auto mFixedLayout::reset() -> type& {
+  mLayout::reset();
+  properties.reset();
+  return *this;
+}
+
+auto mFixedLayout::setEnabled(bool enabled) -> type& {
+  mLayout::setEnabled(enabled);
+  for(auto n : range(sizables())) {
+    sizable(n)->setEnabled(sizable(n)->enabled(true));
+  }
+  return *this;
+}
+
+auto mFixedLayout::setFont(const string& font) -> type& {
+  mLayout::setFont(font);
+  for(auto n : range(sizables())) {
+    sizable(n)->setFont(sizable(n)->font(true));
+  }
+  return *this;
+}
+
+auto mFixedLayout::setVisible(bool visible) -> type& {
+  mLayout::setVisible(visible);
+  for(auto n : range(sizables())) {
+    sizable(n)->setVisible(sizable(n)->visible(true));
+  }
+  return *this;
+}
diff --git a/hiro/extension/fixed-layout.hpp b/hiro/extension/fixed-layout.hpp
new file mode 100644
index 00000000..37f50ada
--- /dev/null
+++ b/hiro/extension/fixed-layout.hpp
@@ -0,0 +1,18 @@
+struct mFixedLayout : mLayout {
+  using type = mFixedLayout;
+  using mLayout::append;
+  using mLayout::remove;
+
+  auto append(nall::shared_pointer<mSizable> sizable, Geometry geometry) -> type&;
+  auto minimumSize() const -> Size override;
+  auto remove(nall::shared_pointer<mSizable> sizable) -> type& override;
+  auto reset() -> type& override;
+  auto setEnabled(bool enabled = true) -> type& override;
+  auto setFont(const nall::string& font = "") -> type& override;
+  auto setVisible(bool visible = true) ->type& override;
+
+  struct Properties {
+    Geometry geometry;
+  };
+  nall::vector<Properties> properties;
+};
diff --git a/hiro/extension/horizontal-layout.cpp b/hiro/extension/horizontal-layout.cpp
new file mode 100644
index 00000000..4d50d4c5
--- /dev/null
+++ b/hiro/extension/horizontal-layout.cpp
@@ -0,0 +1,125 @@
+auto mHorizontalLayout::append(shared_pointer<mSizable> sizable, Size size, signed spacing) -> type& {
+  properties.append({size.width(), size.height(), spacing < 0 ? settings.spacing : spacing});
+  mLayout::append(sizable);
+  return *this;
+}
+
+auto mHorizontalLayout::minimumSize() const -> Size {
+  signed width = 0, height = 0;
+
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    if(child.width == Size::Minimum || child.width == Size::Maximum) {
+      width += sizable(n)->minimumSize().width();
+    } else {
+      width += child.width;
+    }
+    if(&child != &properties.last()) width += child.spacing;
+  }
+
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    if(child.height == Size::Minimum || child.height == Size::Maximum) {
+      height = max(height, sizable(n)->minimumSize().height());
+      continue;
+    }
+    height = max(height, child.height);
+  }
+
+  return {settings.margin * 2 + width, settings.margin * 2 + height};
+}
+
+auto mHorizontalLayout::remove(shared_pointer<mSizable> sizable) -> type& {
+  properties.remove(sizable->offset());
+  mLayout::remove(sizable);
+  return *this;
+}
+
+auto mHorizontalLayout::reset() -> type& {
+  mLayout::reset();
+  properties.reset();
+  return *this;
+}
+
+auto mHorizontalLayout::setAlignment(double alignment) -> type& {
+  settings.alignment = max(0.0, min(1.0, alignment));
+  return *this;
+}
+
+auto mHorizontalLayout::setEnabled(bool enabled) -> type& {
+  mLayout::setEnabled(enabled);
+  for(auto n : range(sizables())) {
+    sizable(n)->setEnabled(sizable(n)->enabled(true));
+  }
+  return *this;
+}
+
+auto mHorizontalLayout::setFont(const string& font) -> type& {
+  mLayout::setFont(font);
+  for(auto n : range(sizables())) {
+    sizable(n)->setFont(sizable(n)->font(true));
+  }
+  return *this;
+}
+
+auto mHorizontalLayout::setGeometry(Geometry containerGeometry) -> type& {
+  mLayout::setGeometry(containerGeometry);
+
+  auto properties = this->properties;
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    if(child.width  == Size::Minimum) child.width  = sizable(n)->minimumSize().width();
+    if(child.height == Size::Minimum) child.height = sizable(n)->minimumSize().height();
+  }
+
+  Geometry geometry = containerGeometry;
+  geometry.setX     (geometry.x()      + settings.margin    );
+  geometry.setY     (geometry.y()      + settings.margin    );
+  geometry.setWidth (geometry.width()  - settings.margin * 2);
+  geometry.setHeight(geometry.height() - settings.margin * 2);
+
+  signed minimumWidth = 0, maximumWidthCounter = 0;
+  for(auto& child : properties) {
+    if(child.width == Size::Maximum) maximumWidthCounter++;
+    if(child.width != Size::Maximum) minimumWidth += child.width;
+    if(&child != &properties.last()) minimumWidth += child.spacing;
+  }
+
+  for(auto& child : properties) {
+    if(child.width  == Size::Maximum) child.width  = (geometry.width() - minimumWidth) / maximumWidthCounter;
+    if(child.height == Size::Maximum) child.height = geometry.height();
+  }
+
+  signed maximumHeight = 0;
+  for(auto& child : properties) maximumHeight = max(maximumHeight, child.height);
+
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    signed pivot = (maximumHeight - child.height) * settings.alignment;
+    Geometry childGeometry = {geometry.x(), geometry.y() + pivot, child.width, child.height};
+    if(childGeometry.width()  < 1) childGeometry.setWidth (1);
+    if(childGeometry.height() < 1) childGeometry.setHeight(1);
+    sizable(n)->setGeometry(childGeometry);
+
+    geometry.setX    (geometry.x()     + child.width + child.spacing);
+    geometry.setWidth(geometry.width() - child.width + child.spacing);
+  }
+}
+
+auto mHorizontalLayout::setMargin(signed margin) -> type& {
+  settings.margin = margin;
+  return *this;
+}
+
+auto mHorizontalLayout::setSpacing(signed spacing) -> type& {
+  settings.spacing = spacing;
+  return *this;
+}
+
+auto mHorizontalLayout::setVisible(bool visible) -> type& {
+  mLayout::setVisible(visible);
+  for(auto n : range(sizables())) {
+    sizable(n)->setVisible(sizable(n)->visible(true));
+  }
+  return *this;
+}
diff --git a/hiro/extension/horizontal-layout.hpp b/hiro/extension/horizontal-layout.hpp
new file mode 100644
index 00000000..9a941dc2
--- /dev/null
+++ b/hiro/extension/horizontal-layout.hpp
@@ -0,0 +1,30 @@
+struct mHorizontalLayout : mLayout {
+  using type = mHorizontalLayout;
+  using mLayout::append;
+  using mLayout::remove;
+
+  auto append(nall::shared_pointer<mSizable> sizable, Size size, signed spacing = 5) -> type&;
+  auto minimumSize() const -> Size override;
+  auto remove(nall::shared_pointer<mSizable> sizable) -> type& override;
+  auto reset() -> type& override;
+  auto setAlignment(double alignment = 0.5) -> type&;
+  auto setEnabled(bool enabled = true) -> type& override;
+  auto setFont(const nall::string& font = "") -> type& override;
+  auto setGeometry(Geometry geometry) -> type& override;
+  auto setMargin(signed margin = 0) -> type&;
+  auto setSpacing(signed spacing = 5) -> type&;
+  auto setVisible(bool visible = true) -> type&;
+
+  struct Settings {
+    double alignment = 0.5;
+    signed margin = 0;
+    signed spacing = 5;
+  } settings;
+
+  struct Property {
+    signed width;
+    signed height;
+    signed spacing;
+  };
+  nall::vector<Property> properties;
+};
diff --git a/hiro/extension/message-dialog.cpp b/hiro/extension/message-dialog.cpp
new file mode 100644
index 00000000..2745e37a
--- /dev/null
+++ b/hiro/extension/message-dialog.cpp
@@ -0,0 +1,77 @@
+MessageDialog::MessageDialog(const string& text) {
+  state.text = text;
+}
+
+auto MessageDialog::error(const lstring& buttons) -> signed {
+  state.buttons = buttons;
+  state.icon = Icon::Prompt::Error;
+  return _run();
+}
+
+auto MessageDialog::information(const lstring& buttons) -> signed {
+  state.buttons = buttons;
+  state.icon = Icon::Prompt::Information;
+  return _run();
+}
+
+auto MessageDialog::question(const lstring& buttons) -> signed {
+  state.buttons = buttons;
+  state.icon = Icon::Prompt::Question;
+  return _run();
+}
+
+auto MessageDialog::setParent(shared_pointer<mWindow> parent) -> type& {
+  state.parent = parent;
+  return *this;
+}
+
+auto MessageDialog::setText(const string& text) -> type& {
+  state.text = text;
+  return *this;
+}
+
+auto MessageDialog::setTitle(const string& title) -> type& {
+  state.title = title;
+  return *this;
+}
+
+auto MessageDialog::warning(const lstring& buttons) -> signed {
+  state.buttons = buttons;
+  state.icon = Icon::Prompt::Warning;
+  return _run();
+}
+
+auto MessageDialog::_run() -> signed {
+  Window window;
+    VerticalLayout layout{&window};
+      HorizontalLayout messageLayout{&layout, Size{~0, 0}, 8};
+        Canvas messageIcon{&messageLayout, Size{16, 16}, 8};
+        Label messageText{&messageLayout, Size{~0, 0}};
+      HorizontalLayout controlLayout{&layout, Size{~0, 0}};
+        Widget controlSpacer{&controlLayout, Size{~0, 0}};
+
+  layout.setMargin(8);
+  messageIcon.setIcon(state.icon);
+  messageText.setText(state.text);
+  for(auto n : range(state.buttons)) {
+    Button button{&controlLayout, Size{80, 0}, 8};
+    button.onActivate([&, n] { state.response = n; window.setModal(false); });
+    button.setText(state.buttons[n]);
+    button.setFocused();  //the last button will have effective focus
+  }
+
+  signed widthMessage = 8 + 16 + 8 + Font::size(Font::sans(), state.text).width() + 8;
+  signed widthButtons = 8 + state.buttons.size() * 88;
+  signed width = max(320, widthMessage, widthButtons);
+
+  window.onClose([&] { state.response = -1; window.setModal(false); });
+  window.setTitle(state.title);
+  window.setResizable(false);
+  window.setSize({width, layout.minimumSize().height()});
+  window.setCentered(state.parent);
+  window.setVisible();
+  window.setModal();
+  window.setVisible(false);
+
+  return state.response;
+}
diff --git a/hiro/extension/message-dialog.hpp b/hiro/extension/message-dialog.hpp
new file mode 100644
index 00000000..0f3627c2
--- /dev/null
+++ b/hiro/extension/message-dialog.hpp
@@ -0,0 +1,24 @@
+struct MessageDialog {
+  using type = MessageDialog;
+
+  MessageDialog(const nall::string& text = "");
+  auto error(const nall::lstring& buttons = {"Ok"}) -> signed;
+  auto information(const nall::lstring& buttons = {"Ok"}) -> signed;
+  auto question(const nall::lstring& buttons = {"Yes", "No"}) -> signed;
+  auto setParent(nall::shared_pointer<mWindow> parent = {}) -> type&;
+  auto setText(const nall::string& text = "") -> type&;
+  auto setTitle(const nall::string& title = "") -> type&;
+  auto warning(const nall::lstring& buttons = {"Ok"}) -> signed;
+
+private:
+  struct State {
+    nall::lstring buttons;
+    nall::vector<uint8_t> icon;
+    nall::shared_pointer<mWindow> parent;
+    signed response = -1;
+    nall::string text;
+    nall::string title;
+  } state;
+
+  auto _run() -> signed;
+};
diff --git a/hiro/extension/shared.hpp b/hiro/extension/shared.hpp
new file mode 100644
index 00000000..0572e862
--- /dev/null
+++ b/hiro/extension/shared.hpp
@@ -0,0 +1,768 @@
+#define Declare(Name) \
+  using type = Name; \
+  Name() : s##Name(new m##Name, [](m##Name* p) { \
+    p->unbind(); \
+    delete p; \
+  }) { \
+    (*this)->bind(*this); \
+  } \
+  template<typename T, typename... P> Name(T* parent, P&&... p) : Name() { \
+    if(parent && *parent) (*parent)->append(*this, std::forward<P>(p)...); \
+  } \
+  auto self() const -> m##Name& { return (m##Name&)operator*(); } \
+
+#define DeclareObject(Name) \
+  Declare(Name) \
+  auto enabled(bool recursive = false) const -> bool { return self().enabled(recursive); } \
+  auto focused() const -> bool { return self().focused(); } \
+  auto font(bool recursive = false) const -> string { return self().font(recursive); } \
+  auto remove() -> type& { return self().remove(), *this; } \
+  auto setEnabled(bool enabled = true) -> type& { return self().setEnabled(enabled), *this; } \
+  auto setFocused() -> type& { return self().setFocused(), *this; } \
+  auto setFont(const string& font = "") -> type& { return self().setFont(font), *this; } \
+  auto setVisible(bool visible = true) -> type& { return self().setVisible(visible), *this; } \
+  auto visible(bool recursive = false) const -> bool { return self().visible(recursive); } \
+
+#define DeclareAction(Name) \
+  DeclareObject(Name) \
+
+#define DeclareSizable(Name) \
+  DeclareObject(Name) \
+  auto geometry() const -> Geometry { return self().geometry(); } \
+  auto minimumSize() const -> Size { return self().minimumSize(); } \
+  auto setGeometry(Geometry geometry) -> type& { return self().setGeometry(geometry), *this; } \
+
+#define DeclareLayout(Name) \
+  DeclareSizable(Name) \
+  auto append(sSizable sizable) -> type& { return self().append(sizable), *this; } \
+  auto remove(sSizable sizable) -> type& { return self().remove(sizable), *this; } \
+  auto reset() -> type& { return self().reset(), *this; } \
+  auto sizable(unsigned position) -> sSizable { return self().sizable(position); } \
+  auto sizables() const -> unsigned { return self().sizables(); } \
+
+#define DeclareWidget(Name) \
+  DeclareSizable(Name) \
+  auto doSize() const -> void { return self().doSize(); } \
+  auto onSize(const function<void ()>& function = {}) -> type& { return self().onSize(function), *this; } \
+
+struct Object : sObject {
+  DeclareObject(Object)
+};
+
+struct Hotkey : sHotkey {
+  DeclareObject(Hotkey)
+
+  auto doPress() const -> void { return self().doPress(); }
+  auto doRelease() const -> void { return self().doRelease(); }
+  auto onPress(const function<void ()>& function = {}) -> type& { return self().onPress(function), *this; }
+  auto onRelease(const function<void ()>& function = {}) -> type& { return self().onRelease(function), *this; }
+  auto parent() const -> wObject { return self().parent(); }
+  auto sequence() const -> string { return self().sequence(); }
+  auto setParent(sObject object) -> type& { return self().setParent(object), *this; }
+  auto setSequence(const string& sequence = "") -> type& { return self().setSequence(sequence), *this; }
+};
+
+struct Timer : sTimer {
+  DeclareObject(Timer)
+
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto interval() const -> unsigned { return self().interval(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto setInterval(unsigned interval = 0) -> type& { return self().setInterval(interval), *this; }
+};
+
+struct Window : sWindow {
+  DeclareObject(Window)
+
+  auto append(sLayout layout) -> type& { return self().append(layout), *this; }
+  auto append(sMenuBar menuBar) -> type& { return self().append(menuBar), *this; }
+  auto append(sStatusBar statusBar) -> type& { return self().append(statusBar), *this; }
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto doClose() const -> void { return self().doClose(); }
+  auto doDrop(lstring names) const -> void { return self().doDrop(names); }
+  auto doKeyPress(signed key) const -> void { return self().doKeyPress(key); }
+  auto doKeyRelease(signed key) const -> void { return self().doKeyRelease(key); }
+  auto doMove() const -> void { return self().doMove(); }
+  auto doSize() const -> void { return self().doSize(); }
+  auto droppable() const -> bool { return self().droppable(); }
+  auto frameGeometry() const -> Geometry { return self().frameGeometry(); }
+  auto fullScreen() const -> bool { return self().fullScreen(); }
+  auto geometry() const -> Geometry { return self().geometry(); }
+  auto layout() const -> sLayout { return self().layout(); }
+  auto menuBar() const -> sMenuBar { return self().menuBar(); }
+  auto modal() const -> bool { return self().modal(); }
+  auto onClose(const function<void ()>& function = {}) -> type& { return self().onClose(function), *this; }
+  auto onDrop(const function<void (nall::lstring)>& function = {}) -> type& { return self().onDrop(function), *this; }
+  auto onKeyPress(const function<void (signed)>& function = {}) -> type& { return self().onKeyPress(function), *this; }
+  auto onKeyRelease(const function<void (signed)>& function = {}) -> type& { return self().onKeyRelease(function), *this; }
+  auto onMove(const function<void ()>& function = {}) -> type& { return self().onMove(function), *this; }
+  auto onSize(const function<void ()>& function = {}) -> type& { return self().onSize(function), *this; }
+  auto remove(sLayout layout) -> type& { return self().remove(layout), *this; }
+  auto remove(sMenuBar menuBar) -> type& { return self().remove(menuBar), *this; }
+  auto remove(sStatusBar statusBar) -> type& { return self().remove(statusBar), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto resizable() const -> bool { return self().resizable(); }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setCentered() -> type& { return self().setCentered(), *this; }
+  auto setCentered(sWindow parent) -> type& { return self().setCentered(parent), *this; }
+  auto setDroppable(bool droppable = true) -> type& { return self().setDroppable(droppable), *this; }
+  auto setFrameGeometry(Geometry geometry) -> type& { return self().setFrameGeometry(geometry), *this; }
+  auto setFramePosition(Position position) -> type& { return self().setFramePosition(position), *this; }
+  auto setFrameSize(Size size) -> type& { return self().setFrameSize(size), *this; }
+  auto setFullScreen(bool fullScreen = true) -> type& { return self().setFullScreen(fullScreen), *this; }
+  auto setGeometry(Geometry geometry) -> type& { return self().setGeometry(geometry), *this; }
+  auto setModal(bool modal = true) -> type& { return self().setModal(modal), *this; }
+  auto setPosition(Position position) -> type& { return self().setPosition(position), *this; }
+  auto setResizable(bool resizable = true) -> type& { return self().setResizable(resizable), *this; }
+  auto setSize(Size size) -> type& { return self().setSize(size), *this; }
+  auto setTitle(const string& title = "") -> type& { return self().setTitle(title), *this; }
+  auto statusBar() const -> sStatusBar { return self().statusBar(); }
+  auto title() const -> string { return self().title(); }
+};
+
+struct StatusBar : sStatusBar {
+  DeclareObject(StatusBar)
+
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct MenuBar : sMenuBar {
+  DeclareObject(MenuBar)
+
+  auto append(sMenu menu) -> type& { return self().append(menu), *this; }
+  auto menu(unsigned position) const -> sMenu { return self().menu(position); }
+  auto menus() const -> unsigned { return self().menus(); }
+  auto remove(sMenu menu) -> type& { return self().remove(menu), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+};
+
+struct PopupMenu : sPopupMenu {
+  DeclareObject(PopupMenu)
+
+  auto action(unsigned position) const -> sAction { return self().action(position); }
+  auto actions() const -> unsigned { return self().actions(); }
+  auto append(sAction action) -> type& { return self().append(action), *this; }
+  auto remove(sAction action) -> type& { return self().remove(action), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+};
+
+struct Action : sAction {
+  DeclareAction(Action)
+};
+
+struct Menu : sMenu {
+  DeclareAction(Menu)
+
+  auto action(unsigned position) const -> sAction { return self().action(position); }
+  auto actions() const -> unsigned { return self().actions(); }
+  auto append(sAction action) -> type& { return self().append(action), *this; }
+  auto icon() const -> image { return self().icon(); }
+  auto remove(sAction action) -> type& { return self().remove(action), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct MenuSeparator : sMenuSeparator {
+  DeclareAction(MenuSeparator)
+};
+
+struct MenuItem : sMenuItem {
+  DeclareAction(MenuItem)
+
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto icon() const -> image { return self().icon(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct MenuCheckItem : sMenuCheckItem {
+  DeclareAction(MenuCheckItem)
+
+  auto checked() const -> bool { return self().checked(); }
+  auto doToggle() const -> void { return self().doToggle(); }
+  auto onToggle(const function<void ()>& function = {}) -> type& { return self().onToggle(function), *this; }
+  auto setChecked(bool checked = true) -> type& { return self().setChecked(checked), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct MenuRadioItem : sMenuRadioItem {
+  DeclareAction(MenuRadioItem)
+
+  auto checked() const -> bool { return self().checked(); }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto setChecked() -> type& { return self().setChecked(), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+
+  static auto group(const vector<wMenuRadioItem>& group) -> void { return mMenuRadioItem::group(group); }
+};
+
+struct Sizable : sSizable {
+  DeclareSizable(Sizable)
+};
+
+struct Layout : sLayout {
+  DeclareLayout(Layout)
+};
+
+struct Widget : sWidget {
+  DeclareWidget(Widget)
+};
+
+struct Button : sButton {
+  DeclareWidget(Button)
+
+  auto bordered() const -> bool { return self().bordered(); }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto icon() const -> image { return self().icon(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto orientation() const -> Orientation { return self().orientation(); }
+  auto setBordered(bool bordered = true) -> type& { return self().setBordered(bordered), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type& { return self().setOrientation(orientation), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct Canvas : sCanvas {
+  DeclareWidget(Canvas)
+
+  auto color() const -> Color { return self().color(); }
+  auto data() -> uint32_t* { return self().data(); }
+  auto droppable() const -> bool { return self().droppable(); }
+  auto doDrop(lstring names) -> void { return self().doDrop(names); }
+  auto doMouseLeave() const -> void { return self().doMouseLeave(); }
+  auto doMouseMove(Position position) const -> void { return self().doMouseMove(position); }
+  auto doMousePress(Mouse::Button button) const -> void { return self().doMousePress(button); }
+  auto doMouseRelease(Mouse::Button button) const -> void { return self().doMouseRelease(button); }
+  auto gradient() const -> vector<Color> { return self().gradient(); }
+  auto icon() const -> image { return self().icon(); }
+  auto onDrop(const function<void (lstring)>& function = {}) -> type& { return self().onDrop(function), *this; }
+  auto onMouseLeave(const function<void ()>& function = {}) -> type& { return self().onMouseLeave(function), *this; }
+  auto onMouseMove(const function<void (Position)>& function = {}) -> type& { return self().onMouseMove(function), *this; }
+  auto onMousePress(const function<void (Mouse::Button)>& function = {}) -> type& { return self().onMousePress(function), *this; }
+  auto onMouseRelease(const function<void (Mouse::Button)>& function = {}) -> type& { return self().onMouseRelease(function), *this; }
+  auto setColor(Color color) -> type& { return self().setColor(color), *this; }
+  auto setData(Size size) -> type& { return self().setData(size), *this; }
+  auto setDroppable(bool droppable = true) -> type& { return self().setDroppable(droppable), *this; }
+  auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> type& { return self().setGradient(topLeft, topRight, bottomLeft, bottomRight), *this; }
+  auto setHorizontalGradient(Color left, Color right) -> type& { return self().setGradient(left, right, left, right), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setVerticalGradient(Color top, Color bottom) -> type& { return self().setGradient(top, top, bottom, bottom), *this; }
+  auto size() const -> Size { return self().size(); }
+  auto update() -> type& { return self().update(), *this; }
+};
+
+struct CheckButton : sCheckButton {
+  DeclareWidget(CheckButton)
+
+  auto bordered() const -> bool { return self().bordered(); }
+  auto checked() const -> bool { return self().checked(); }
+  auto doToggle() const -> void { return self().doToggle(); }
+  auto icon() const -> image { return self().icon(); }
+  auto onToggle(const function<void ()>& function = {}) -> type& { return self().onToggle(function), *this; }
+  auto orientation() const -> Orientation { return self().orientation(); }
+  auto setBordered(bool bordered = true) -> type& { return self().setBordered(bordered), *this; }
+  auto setChecked(bool checked = true) -> type& { return self().setChecked(checked), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type& { return self().setOrientation(orientation), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct CheckLabel : sCheckLabel {
+  DeclareWidget(CheckLabel)
+
+  auto checked() const -> bool { return self().checked(); }
+  auto doToggle() const -> void { return self().doToggle(); }
+  auto onToggle(const function<void ()>& function = {}) -> type& { return self().onToggle(function), *this; }
+  auto setChecked(bool checked = true) -> type& { return self().setChecked(checked), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct ComboButton : sComboButton {
+  DeclareWidget(ComboButton)
+
+  auto append(sComboButtonItem item) -> type& { return self().append(item), *this; }
+  auto doChange() const -> void { return self().doChange(); }
+  auto item(unsigned position) const -> sComboButtonItem { return self().item(position); }
+  auto items() const -> unsigned { return self().items(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto remove(sComboButtonItem item) -> type& { return self().remove(item), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto selected() const -> sComboButtonItem { return self().selected(); }
+};
+
+struct ComboButtonItem : sComboButtonItem {
+  DeclareObject(ComboButtonItem)
+
+  auto icon() const -> image { return self().icon(); }
+  auto selected() const -> bool { return self().selected(); }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setSelected() -> type& { return self().setSelected(), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct Console : sConsole {
+  DeclareWidget(Console)
+
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto doActivate(string command) const -> void { return self().doActivate(command); }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto onActivate(const function<void (string)>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto print(const string& text) -> type& { return self().print(text), *this; }
+  auto prompt() const -> string { return self().prompt(); }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+  auto setPrompt(const string& prompt = "") -> type& { return self().setPrompt(prompt), *this; }
+};
+
+struct Frame : sFrame {
+  DeclareWidget(Frame)
+
+  auto append(sLayout layout) -> type& { return self().append(layout), *this; }
+  auto layout() const -> sLayout { return self().layout(); }
+  auto remove(sLayout layout) -> type& { return self().remove(layout), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct HexEdit : sHexEdit {
+  DeclareWidget(HexEdit)
+
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto columns() const -> unsigned { return self().columns(); }
+  auto doRead(unsigned offset) const -> uint8_t { return self().doRead(offset); }
+  auto doWrite(unsigned offset, uint8_t data) const -> void { return self().doWrite(offset, data); }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto length() const -> unsigned { return self().length(); }
+  auto offset() const -> unsigned { return self().offset(); }
+  auto onRead(const function<uint8_t (unsigned)>& function = {}) -> type& { return self().onRead(function), *this; }
+  auto onWrite(const function<void (unsigned, uint8_t)>& function = {}) -> type& { return self().onWrite(function), *this; }
+  auto rows() const -> unsigned { return self().rows(); }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setColumns(unsigned columns = 16) -> type& { return self().setColumns(columns), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+  auto setLength(unsigned length) -> type& { return self().setLength(length), *this; }
+  auto setOffset(unsigned offset) -> type& { return self().setOffset(offset), *this; }
+  auto setRows(unsigned rows = 16) -> type& { return self().setRows(rows), *this; }
+  auto update() -> type& { return self().update(), *this; }
+};
+
+struct HorizontalScroller : sHorizontalScroller {
+  DeclareWidget(HorizontalScroller)
+
+  auto doChange() const -> void { return self().doChange(); }
+  auto length() const -> unsigned { return self().length(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto position() const -> unsigned { return self().position(); }
+  auto setLength(unsigned length = 101) -> type& { return self().setLength(length), *this; }
+  auto setPosition(unsigned position = 0) -> type& { return self().setPosition(position), *this; }
+};
+
+struct HorizontalSlider : sHorizontalSlider {
+  DeclareWidget(HorizontalSlider)
+
+  auto doChange() const -> void { return self().doChange(); }
+  auto length() const -> unsigned { return self().length(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto position() const -> unsigned { return self().position(); }
+  auto setLength(unsigned length = 101) -> type& { return self().setLength(length), *this; }
+  auto setPosition(unsigned position = 0) -> type& { return self().setPosition(position), *this; }
+};
+
+struct IconView : sIconView {
+  DeclareWidget(IconView)
+
+  auto append(sIconViewItem item) -> type& { return self().append(item), *this; }
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto doChange() const -> void { return self().doChange(); }
+  auto doContext() const -> void { return self().doContext(); }
+  auto flow() const -> Orientation { return self().flow(); }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto item(unsigned position) const -> sIconViewItem { return self().item(position); }
+  auto items() const -> unsigned { return self().items(); }
+  auto multiSelect() const -> bool { return self().multiSelect(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto onContext(const function<void ()>& function = {}) -> type& { return self().onContext(function), *this; }
+  auto orientation() const -> Orientation { return self().orientation(); }
+  auto remove(sIconViewItem item) -> type& { return self().remove(item), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto selected() const -> maybe<unsigned> { return self().selected(); }
+  auto selectedItems() const -> vector<unsigned> { return self().selectedItems(); }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setFlow(Orientation orientation = Orientation::Vertical) -> type& { return self().setFlow(orientation), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+  auto setMultiSelect(bool multiSelect = true) -> type& { return self().setMultiSelect(multiSelect), *this; }
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type& { return self().setOrientation(orientation), *this; }
+  auto setSelected(const vector<signed>& selections) -> type& { return self().setSelected(selections), *this; }
+};
+
+struct IconViewItem : sIconViewItem {
+  DeclareObject(IconViewItem)
+
+  auto icon() const -> image { return self().icon(); }
+  auto selected() const -> bool { return self().selected(); }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setSelected(bool selected = true) -> type& { return self().setSelected(selected), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct Label : sLabel {
+  DeclareWidget(Label)
+
+  auto horizontalAlignment() const -> double { return self().horizontalAlignment(); }
+  auto setHorizontalAlignment(double alignment = 0.0) -> type& { return self().setHorizontalAlignment(alignment), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto setVerticalAlignment(double alignment = 0.5) -> type& { return self().setVerticalAlignment(alignment), *this; }
+  auto text() const -> string { return self().text(); }
+  auto verticalAlignment() const -> double { return self().verticalAlignment(); }
+};
+
+struct LineEdit : sLineEdit {
+  DeclareWidget(LineEdit)
+
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto doChange() const -> void { return self().doChange(); }
+  auto editable() const -> bool { return self().editable(); }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setEditable(bool editable = true) -> type& { return self().setEditable(editable), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct ListView : sListView {
+  DeclareWidget(ListView)
+
+  auto append(sListViewColumn column) -> type& { return self().append(column), *this; }
+  auto append(sListViewItem item) -> type& { return self().append(item), *this; }
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto checkable() const -> bool { return self().checkable(); }
+  auto checked() const -> vector<sListViewItem> { return self().checked(); }
+  auto column(unsigned position) -> sListViewColumn { return self().column(position); }
+  auto columns() const -> unsigned { return self().columns(); }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto doChange() const -> void { return self().doChange(); }
+  auto doContext() const -> void { return self().doContext(); }
+  auto doEdit(sListViewItem item, sListViewColumn column) const -> void { return self().doEdit(item, column); }
+  auto doSort(sListViewColumn column) const -> void { return self().doSort(column); }
+  auto doToggle(sListViewItem item) const -> void { return self().doToggle(item); }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto gridVisible() const -> bool { return self().gridVisible(); }
+  auto headerVisible() const -> bool { return self().headerVisible(); }
+  auto item(unsigned position) -> sListViewItem { return self().item(position); }
+  auto items() const -> unsigned { return self().items(); }
+  auto multiSelect() const -> bool { return self().multiSelect(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto onContext(const function<void ()>& function = {}) -> type& { return self().onContext(function), *this; }
+  auto onEdit(const function<void (sListViewItem, sListViewColumn)>& function = {}) -> type& { return self().onEdit(function), *this; }
+  auto onSort(const function<void (sListViewColumn)>& function = {}) -> type& { return self().onSort(function), *this; }
+  auto onToggle(const function<void (sListViewItem)>& function = {}) -> type& { return self().onToggle(function), *this; }
+  auto remove(sListViewColumn column) -> type& { return self().remove(column), *this; }
+  auto remove(sListViewItem item) -> type& { return self().remove(item), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto resizeColumns() -> type& { return self().resizeColumns(), *this; }
+  auto selected() const -> sListViewItem { return self().selected(); }
+  auto selectedItems() const -> vector<sListViewItem> { return self().selectedItems(); }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setCheckable(bool checkable = true) -> type& { return self().setCheckable(checkable), *this; }
+  auto setChecked(bool checked = true) -> type& { return self().setChecked(checked), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+  auto setGridVisible(bool visible = true) -> type& { return self().setGridVisible(visible), *this; }
+  auto setHeaderVisible(bool visible = true) -> type& { return self().setHeaderVisible(visible), *this; }
+  auto setMultiSelect(bool multiSelect = true) -> type& { return self().setMultiSelect(multiSelect), *this; }
+  auto setSelected(bool selected = true) -> type& { return self().setSelected(selected), *this; }
+};
+
+struct ListViewColumn : sListViewColumn {
+  DeclareObject(ListViewColumn)
+
+  auto active() const -> bool { return self().active(); }
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto editable() const -> bool { return self().editable(); }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto horizontalAlignment() const -> double { return self().horizontalAlignment(); }
+  auto icon() const -> image { return self().icon(); }
+  auto resizable() const -> bool { return self().resizable(); }
+  auto setActive() -> type& { return self().setActive(), *this; }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setEditable(bool editable = true) -> type& { return self().setEditable(editable), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+  auto setHorizontalAlignment(double alignment = 0.0) -> type& { return self().setHorizontalAlignment(alignment), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setResizable(bool resizable = true) -> type& { return self().setResizable(resizable), *this; }
+  auto setSortable(bool sortable = true) -> type& { return self().setSortable(sortable), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto setVerticalAlignment(double alignment = 0.5) -> type& { return self().setVerticalAlignment(alignment), *this; }
+  auto setWidth(signed width = 0) -> type& { return self().setWidth(width), *this; }
+  auto sortable() const -> bool { return self().sortable(); }
+  auto text() const -> string { return self().text(); }
+  auto verticalAlignment() const -> double { return self().verticalAlignment(); }
+  auto width() const -> signed { return self().width(); }
+};
+
+struct ListViewItem : sListViewItem {
+  DeclareObject(ListViewItem)
+
+  auto checked() const -> bool { return self().checked(); }
+  auto icon(unsigned column = 0) const -> image { return self().icon(column); }
+  auto selected() const -> bool { return self().selected(); }
+  auto setChecked(bool checked = true) -> type& { return self().setChecked(checked), *this; }
+  auto setIcon(unsigned column, const image& icon = {}) -> type& { return self().setIcon(column, icon), *this; }
+  auto setSelected(bool selected = true) -> type& { return self().setSelected(selected), *this; }
+  auto setText(const lstring& text) -> type& { return self().setText(text), *this; }
+  auto setText(unsigned column, const string& text = "") -> type& { return self().setText(column, text), *this; }
+  auto text(unsigned column = 0) const -> string { return self().text(column); }
+};
+
+struct ProgressBar : sProgressBar {
+  DeclareWidget(ProgressBar)
+
+  auto position() const -> unsigned { return self().position(); }
+  auto setPosition(unsigned position = 0) -> type& { return self().setPosition(position), *this; }
+};
+
+struct RadioButton : sRadioButton {
+  DeclareWidget(RadioButton)
+
+  auto bordered() const -> bool { return self().bordered(); }
+  auto checked() const -> bool { return self().checked(); }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto icon() const -> image { return self().icon(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto orientation() const -> Orientation { return self().orientation(); }
+  auto setBordered(bool bordered = true) -> type& { return self().setBordered(bordered), *this; }
+  auto setChecked() -> type& { return self().setChecked(), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type& { return self().setOrientation(orientation), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+
+  static auto group(const vector<wRadioButton>& group) -> void { return mRadioButton::group(group); }
+};
+
+struct RadioLabel : sRadioLabel {
+  DeclareWidget(RadioLabel)
+
+  auto checked() const -> bool { return self().checked(); }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto setChecked() -> type& { return self().setChecked(), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+
+  static auto group(const vector<wRadioLabel>& group) -> void { return mRadioLabel::group(group); }
+};
+
+struct SourceEdit : sSourceEdit {
+  DeclareWidget(SourceEdit)
+
+  auto doChange() const -> void { return self().doChange(); }
+  auto doMove() const -> void { return self().doMove(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto onMove(const function<void ()>& function = {}) -> type& { return self().onMove(function), *this; }
+  auto position() const -> unsigned { return self().position(); }
+  auto setPosition(signed position) -> type& { return self().setPosition(position), *this; }
+  auto setSelected(Position selected) -> type& { return self().setSelected(selected), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct TabFrame : sTabFrame {
+  DeclareWidget(TabFrame)
+
+  auto append(sTabFrameItem item) -> type& { return self().append(item), *this; }
+  auto doChange() const -> void { return self().doChange(); }
+  auto doClose(sTabFrameItem item) const -> void { return self().doClose(item); }
+  auto doMove(sTabFrameItem from, sTabFrameItem to) const -> void { return self().doMove(from, to); }
+  auto edge() const -> Edge;
+  auto item(unsigned position) const -> sTabFrameItem { return self().item(position); }
+  auto items() const -> unsigned { return self().items(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto onClose(const function<void (sTabFrameItem)>& function = {}) -> type& { return self().onClose(function), *this; }
+  auto onMove(const function<void (sTabFrameItem, sTabFrameItem)>& function = {}) -> type& { return self().onMove(function), *this; }
+  auto remove(sTabFrameItem item) -> type& { return self().remove(item), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto selected() const -> sTabFrameItem { return self().selected(); }
+  auto setEdge(Edge edge = Edge::Top) -> type& { return self().setEdge(edge), *this; }
+};
+
+struct TabFrameItem : sTabFrameItem {
+  DeclareObject(TabFrameItem)
+
+  auto append(sLayout layout) -> type& { return self().append(layout), *this; }
+  auto closable() const -> bool { return self().closable(); }
+  auto icon() const -> image { return self().icon(); }
+  auto layout() const -> sLayout { return self().layout(); }
+  auto movable() const -> bool { return self().movable(); }
+  auto remove(sLayout layout) -> type& { return self().remove(layout), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto selected() const -> bool { return self().selected(); }
+  auto setClosable(bool closable = true) -> type& { return self().setClosable(closable), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setMovable(bool movable = true) -> type& { return self().setMovable(movable), *this; }
+  auto setSelected() -> type& { return self().setSelected(), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct TextEdit : sTextEdit {
+  DeclareWidget(TextEdit)
+
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto cursorPosition() const -> unsigned { return self().cursorPosition(); }
+  auto doChange() const -> void { return self().doChange(); }
+  auto doMove() const -> void { return self().doMove(); }
+  auto editable() const -> bool { return self().editable(); }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto onMove(const function<void ()>& function = {}) -> type& { return self().onMove(function), *this; }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setCursorPosition(unsigned position) -> type& { return self().setCursorPosition(position), *this; }
+  auto setEditable(bool editable = true) -> type& { return self().setEditable(editable), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto setWordWrap(bool wordWrap = true) -> type& { return self().setWordWrap(wordWrap), *this; }
+  auto text() const -> string { return self().text(); }
+  auto wordWrap() const -> bool { return self().wordWrap(); }
+};
+
+struct TreeView : sTreeView {
+  DeclareWidget(TreeView)
+
+  auto append(sTreeViewItem item) -> type& { return self().append(item), *this; }
+  auto backgroundColor() const -> Color { return self().backgroundColor(); }
+  auto checkable() const -> bool { return self().checkable(); }
+  auto collapse() -> type& { return self().collapse(), *this; }
+  auto doActivate() const -> void { return self().doActivate(); }
+  auto doChange() const -> void { return self().doChange(); }
+  auto doContext() const -> void { return self().doContext(); }
+  auto doToggle(sTreeViewItem item) const -> void { return self().doToggle(item); }
+  auto expand() -> type& { return self().expand(), *this; }
+  auto foregroundColor() const -> Color { return self().foregroundColor(); }
+  auto item(const string& path) const -> sTreeViewItem { return self().item(path); }
+  auto items() const -> unsigned { return self().items(); }
+  auto onActivate(const function<void ()>& function = {}) -> type& { return self().onActivate(function), *this; }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto onContext(const function<void ()>& function = {}) -> type& { return self().onContext(function), *this; }
+  auto onToggle(const function<void (sTreeViewItem)>& function = {}) -> type& { return self().onToggle(function), *this; }
+  auto remove(sTreeViewItem item) -> type& { return self().remove(item), *this; }
+  auto reset() -> type& { return self().reset(), *this; }
+  auto selected() const -> sTreeViewItem { return self().selected(); }
+  auto setBackgroundColor(Color color = {}) -> type& { return self().setBackgroundColor(color), *this; }
+  auto setCheckable(bool checkable = true) -> type& { return self().setCheckable(checkable), *this; }
+  auto setForegroundColor(Color color = {}) -> type& { return self().setForegroundColor(color), *this; }
+};
+
+struct TreeViewItem : sTreeViewItem {
+  DeclareObject(TreeViewItem)
+
+  auto append(sTreeViewItem item) -> type& { return self().append(item), *this; }
+  auto checked() const -> bool { return self().checked(); }
+  auto icon() const -> image { return self().icon(); }
+  auto item(const string& path) const -> sTreeViewItem { return self().item(path); }
+  auto items() const -> unsigned { return self().items(); }
+  auto path() const -> string { return self().path(); }
+  auto remove(sTreeViewItem item) -> type& { return self().remove(item), *this; }
+  auto selected() const -> bool { return self().selected(); }
+  auto setChecked(bool checked = true) -> type& { return self().setChecked(checked), *this; }
+  auto setIcon(const image& icon = {}) -> type& { return self().setIcon(icon), *this; }
+  auto setSelected() -> type& { return self().setSelected(), *this; }
+  auto setText(const string& text = "") -> type& { return self().setText(text), *this; }
+  auto text() const -> string { return self().text(); }
+};
+
+struct VerticalScroller : sVerticalScroller {
+  DeclareWidget(VerticalScroller)
+
+  auto doChange() const -> void { return self().doChange(); }
+  auto length() const -> unsigned { return self().length(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto position() const -> unsigned { return self().position(); }
+  auto setLength(unsigned length = 101) -> type& { return self().setLength(length), *this; }
+  auto setPosition(unsigned position = 0) -> type& { return self().setPosition(position), *this; }
+};
+
+struct VerticalSlider : sVerticalSlider {
+  DeclareWidget(VerticalSlider)
+
+  auto doChange() const -> void { return self().doChange(); }
+  auto length() const -> unsigned { return self().length(); }
+  auto onChange(const function<void ()>& function = {}) -> type& { return self().onChange(function), *this; }
+  auto position() const -> unsigned { return self().position(); }
+  auto setLength(unsigned length = 101) -> type& { return self().setLength(length), *this; }
+  auto setPosition(unsigned position = 0) -> type& { return self().setPosition(position), *this; }
+};
+
+struct Viewport : sViewport {
+  DeclareWidget(Viewport)
+
+  auto doDrop(lstring names) const -> void { return self().doDrop(names); }
+  auto doMouseLeave() const -> void { return self().doMouseLeave(); }
+  auto doMouseMove(Position position) const -> void { return self().doMouseMove(position); }
+  auto doMousePress(Mouse::Button button) const -> void { return self().doMousePress(button); }
+  auto doMouseRelease(Mouse::Button button) const -> void { return self().doMouseRelease(button); }
+  auto droppable() const -> bool { return self().droppable(); }
+  auto handle() const -> uintptr_t { return self().handle(); }
+  auto onDrop(const function<void (lstring)>& function = {}) -> type& { return self().onDrop(function), *this; }
+  auto onMouseLeave(const function<void ()>& function = {}) -> type& { return self().onMouseLeave(function), *this; }
+  auto onMouseMove(const function<void (Position)>& function = {}) -> type& { return self().onMouseMove(function), *this; }
+  auto onMousePress(const function<void (Mouse::Button)>& function = {}) -> type& { return self().onMousePress(function), *this; }
+  auto onMouseRelease(const function<void (Mouse::Button)>& function = {}) -> type& { return self().onMouseRelease(function), *this; }
+  auto setDroppable(bool droppable = true) -> type& { return self().setDroppable(droppable), *this; }
+};
+
+using sFixedLayout = shared_pointer<mFixedLayout>;
+using sHorizontalLayout = shared_pointer<mHorizontalLayout>;
+using sVerticalLayout = shared_pointer<mVerticalLayout>;
+
+struct FixedLayout : sFixedLayout {
+  DeclareLayout(FixedLayout)
+
+  auto append(sSizable sizable, Geometry geometry) -> type& { return self().append(sizable, geometry), *this; }
+};
+
+struct HorizontalLayout : sHorizontalLayout {
+  DeclareLayout(HorizontalLayout)
+
+  auto append(sSizable sizable, Size size, signed spacing = 5) -> type& { return self().append(sizable, size, spacing), *this; }
+  auto setAlignment(double alignment = 0.5) -> type& { return self().setAlignment(alignment), *this; }
+  auto setMargin(signed margin = 0) -> type& { return self().setMargin(margin), *this; }
+  auto setSpacing(signed spacing = 5) -> type& { return self().setSpacing(spacing), *this; }
+};
+
+struct VerticalLayout : sVerticalLayout {
+  DeclareLayout(VerticalLayout)
+
+  auto append(sSizable sizable, Size size, signed spacing = 5) -> type& { return self().append(sizable, size, spacing), *this; }
+  auto setAlignment(double alignment = 0.0) -> type& { return self().setAlignment(alignment), *this; }
+  auto setMargin(signed margin = 0) -> type& { return self().setMargin(margin), *this; }
+  auto setSpacing(signed spacing = 5) -> type& { return self().setSpacing(spacing), *this; }
+};
+
+#undef Declare
+#undef DeclareObject
+#undef DeclareAction
+#undef DeclareSizable
+#undef DeclareLayout
+#undef DeclareWidget
diff --git a/hiro/extension/vertical-layout.cpp b/hiro/extension/vertical-layout.cpp
new file mode 100644
index 00000000..1c2c86d1
--- /dev/null
+++ b/hiro/extension/vertical-layout.cpp
@@ -0,0 +1,127 @@
+auto mVerticalLayout::append(shared_pointer<mSizable> sizable, Size size, signed spacing) -> type& {
+  properties.append({size.width(), size.height(), spacing < 0 ? settings.spacing : spacing});
+  mLayout::append(sizable);
+  return *this;
+}
+
+auto mVerticalLayout::minimumSize() const -> Size {
+  signed width = 0, height = 0;
+
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    if(child.width == Size::Minimum || child.width == Size::Maximum) {
+      width = max(width, sizable(n)->minimumSize().width());
+      continue;
+    }
+    width = max(width, child.width);
+  }
+
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    if(child.height == Size::Minimum || child.height == Size::Maximum) {
+      height += sizable(n)->minimumSize().height();
+    } else {
+      height += child.height;
+    }
+    if(&child != &properties.last()) height += child.spacing;
+  }
+
+  return {settings.margin * 2 + width, settings.margin * 2 + height};
+}
+
+auto mVerticalLayout::remove(shared_pointer<mSizable> sizable) -> type& {
+  properties.remove(sizable->offset());
+  mLayout::remove(sizable);
+  return *this;
+}
+
+auto mVerticalLayout::reset() -> type& {
+  mLayout::reset();
+  properties.reset();
+  return *this;
+}
+
+auto mVerticalLayout::setAlignment(double alignment) -> type& {
+  settings.alignment = max(0.0, min(1.0, alignment));
+  return *this;
+}
+
+auto mVerticalLayout::setEnabled(bool enabled) -> type& {
+  mLayout::setEnabled(enabled);
+  for(auto n : range(sizables())) {
+    sizable(n)->setEnabled(sizable(n)->enabled(true));
+  }
+  return *this;
+}
+
+auto mVerticalLayout::setFont(const string& font) -> type& {
+  mLayout::setFont(font);
+  for(auto n : range(sizables())) {
+    sizable(n)->setFont(sizable(n)->font(true));
+  }
+  return *this;
+}
+
+auto mVerticalLayout::setGeometry(Geometry containerGeometry) -> type& {
+  mLayout::setGeometry(containerGeometry);
+
+  auto properties = this->properties;
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    if(child.width  == Size::Minimum) child.width  = sizable(n)->minimumSize().width();
+    if(child.height == Size::Minimum) child.height = sizable(n)->minimumSize().height();
+  }
+
+  Geometry geometry = containerGeometry;
+  geometry.setX     (geometry.x()      + settings.margin    );
+  geometry.setY     (geometry.y()      + settings.margin    );
+  geometry.setWidth (geometry.width()  - settings.margin * 2);
+  geometry.setHeight(geometry.height() - settings.margin * 2);
+
+  signed minimumHeight = 0, maximumHeightCounter = 0;
+  for(auto& child : properties) {
+    if(child.height == Size::Maximum) maximumHeightCounter++;
+    if(child.height != Size::Maximum) minimumHeight += child.height;
+    if(&child != &properties.last()) minimumHeight += child.spacing;
+  }
+
+  for(auto& child : properties) {
+    if(child.width  == Size::Maximum) child.width  = geometry.width();
+    if(child.height == Size::Maximum) child.height = (geometry.height() - minimumHeight) / maximumHeightCounter;
+  }
+
+  signed maximumWidth = 0;
+  for(auto& child : properties) maximumWidth = max(maximumWidth, child.width);
+
+  for(auto n : range(sizables())) {
+    auto& child = properties[sizable(n)->offset()];
+    signed pivot = (maximumWidth - child.width) * settings.alignment;
+    Geometry childGeometry = {geometry.x() + pivot, geometry.y(), child.width, child.height};
+    if(childGeometry.width()  < 1) childGeometry.setWidth (1);
+    if(childGeometry.height() < 1) childGeometry.setHeight(1);
+    sizable(n)->setGeometry(childGeometry);
+
+    geometry.setY     (geometry.y()      + child.height + child.spacing);
+    geometry.setHeight(geometry.height() - child.height + child.spacing);
+  }
+
+  return *this;
+}
+
+auto mVerticalLayout::setMargin(signed margin) -> type& {
+  settings.margin = margin;
+  return *this;
+}
+
+auto mVerticalLayout::setSpacing(signed spacing) -> type& {
+  settings.spacing = spacing;
+  return *this;
+}
+
+auto mVerticalLayout::setVisible(bool visible) -> type& {
+  mLayout::setVisible(visible);
+  for(auto n : range(sizables())) {
+    sizable(n)->setVisible(sizable(n)->visible(true));
+  }
+  return *this;
+}
diff --git a/hiro/extension/vertical-layout.hpp b/hiro/extension/vertical-layout.hpp
new file mode 100644
index 00000000..e3ec666c
--- /dev/null
+++ b/hiro/extension/vertical-layout.hpp
@@ -0,0 +1,30 @@
+struct mVerticalLayout : mLayout {
+  using type = mVerticalLayout;
+  using mLayout::append;
+  using mLayout::remove;
+
+  auto append(nall::shared_pointer<mSizable> sizable, Size size, signed spacing = 5) -> type&;
+  auto minimumSize() const -> Size override;
+  auto remove(nall::shared_pointer<mSizable> sizable) -> type& override;
+  auto reset() -> type& override;
+  auto setAlignment(double alignment = 0.0) -> type&;
+  auto setEnabled(bool enabled = true) -> type& override;
+  auto setFont(const nall::string& font = "") -> type& override;
+  auto setGeometry(Geometry geometry) -> type& override;
+  auto setMargin(signed margin = 0) -> type&;
+  auto setSpacing(signed spacing = 5) -> type&;
+  auto setVisible(bool visible = true) -> type& override;
+
+  struct Settings {
+    double alignment = 0.0;
+    signed margin = 0;
+    signed spacing = 5;
+  } settings;
+
+  struct Properties {
+    signed width;
+    signed height;
+    signed spacing;
+  };
+  nall::vector<Properties> properties;
+};
diff --git a/phoenix/gtk/action/action.cpp b/hiro/gtk/action/action.cpp
similarity index 56%
rename from phoenix/gtk/action/action.cpp
rename to hiro/gtk/action/action.cpp
index 978b9747..f28a97a7 100644
--- a/phoenix/gtk/action/action.cpp
+++ b/hiro/gtk/action/action.cpp
@@ -1,22 +1,26 @@
-namespace phoenix {
+namespace hiro {
 
-void pAction::setEnabled(bool enabled) {
+auto pAction::construct() -> void {
+}
+
+auto pAction::destruct() -> void {
+}
+
+auto pAction::setEnabled(bool enabled) -> void {
   gtk_widget_set_sensitive(widget, enabled);
 }
 
-void pAction::setVisible(bool visible) {
+auto pAction::setFont(const string& font) -> void {
+  pFont::setFont(widget, font);
+}
+
+auto pAction::setVisible(bool visible) -> void {
   gtk_widget_set_visible(widget, visible);
 }
 
-void pAction::constructor() {
-}
-
-void pAction::orphan() {
-}
-
 //GTK+ uses _ for mnemonics, __ for _
 //transform so that & is used for mnemonics, && for &
-string pAction::mnemonic(string text) {
+auto pAction::_mnemonic(string text) -> string {
   text.transform("&_", "\x01\x02");
   text.replace("\x01\x01", "&");
   text.transform("\x01", "_");
@@ -24,8 +28,4 @@ string pAction::mnemonic(string text) {
   return text;
 }
 
-void pAction::setFont(string font) {
-  pFont::setFont(widget, font);
-}
-
 }
diff --git a/hiro/gtk/action/action.hpp b/hiro/gtk/action/action.hpp
new file mode 100644
index 00000000..2a28d5a3
--- /dev/null
+++ b/hiro/gtk/action/action.hpp
@@ -0,0 +1,15 @@
+namespace hiro {
+
+struct pAction : pObject {
+  Declare(Action, Object)
+
+  auto setEnabled(bool enabled) -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setVisible(bool visible) -> void override;
+
+  auto _mnemonic(string text) -> string;
+
+  GtkWidget* widget = nullptr;
+};
+
+}
diff --git a/hiro/gtk/action/menu-check-item.cpp b/hiro/gtk/action/menu-check-item.cpp
new file mode 100644
index 00000000..e262cf8d
--- /dev/null
+++ b/hiro/gtk/action/menu-check-item.cpp
@@ -0,0 +1,34 @@
+namespace hiro {
+
+static auto MenuCheckItem_toggle(GtkCheckMenuItem* gtkCheckMenuItem, pMenuCheckItem* p) -> void {
+  p->state().checked = gtk_check_menu_item_get_active(gtkCheckMenuItem);
+  if(!p->locked()) p->self().doToggle();
+}
+
+auto pMenuCheckItem::construct() -> void {
+  widget = gtk_check_menu_item_new_with_mnemonic("");
+  setChecked(state().checked);
+  setText(state().text);
+  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(MenuCheckItem_toggle), (gpointer)this);
+}
+
+auto pMenuCheckItem::destruct() -> void {
+  gtk_widget_destroy(widget);
+}
+
+auto pMenuCheckItem::orphan() -> void {
+  destruct();
+  construct();
+}
+
+auto pMenuCheckItem::setChecked(bool checked) -> void {
+  lock();
+  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), checked);
+  unlock();
+}
+
+auto pMenuCheckItem::setText(const string& text) -> void {
+  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _mnemonic(text));
+}
+
+}
diff --git a/hiro/gtk/action/menu-check-item.hpp b/hiro/gtk/action/menu-check-item.hpp
new file mode 100644
index 00000000..b342de84
--- /dev/null
+++ b/hiro/gtk/action/menu-check-item.hpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+struct pMenuCheckItem : pAction {
+  Declare(MenuCheckItem, Action)
+  auto orphan() -> void;
+
+  auto setChecked(bool checked) -> void;
+  auto setText(const string& text) -> void;
+};
+
+}
diff --git a/hiro/gtk/action/menu-item.cpp b/hiro/gtk/action/menu-item.cpp
new file mode 100644
index 00000000..39d1dba4
--- /dev/null
+++ b/hiro/gtk/action/menu-item.cpp
@@ -0,0 +1,30 @@
+namespace hiro {
+
+static auto MenuItem_activate(GtkMenuItem*, pMenuItem* p) -> void {
+  p->self().doActivate();
+}
+
+auto pMenuItem::construct() -> void {
+  widget = gtk_image_menu_item_new_with_mnemonic("");
+  g_signal_connect(G_OBJECT(widget), "activate", G_CALLBACK(MenuItem_activate), (gpointer)this);
+  setText(state().text);
+}
+
+auto pMenuItem::destruct() -> void {
+  if(widget) gtk_widget_destroy(widget), widget = nullptr;
+}
+
+auto pMenuItem::setIcon(const image& icon) -> void {
+  if(icon) {
+    GtkImage* gtkImage = CreateImage(icon, true);
+    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), (GtkWidget*)gtkImage);
+  } else {
+    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr);
+  }
+}
+
+auto pMenuItem::setText(const string& text) -> void {
+  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _mnemonic(text));
+}
+
+}
diff --git a/hiro/gtk/action/menu-item.hpp b/hiro/gtk/action/menu-item.hpp
new file mode 100644
index 00000000..bf2e13db
--- /dev/null
+++ b/hiro/gtk/action/menu-item.hpp
@@ -0,0 +1,10 @@
+namespace hiro {
+
+struct pMenuItem : pAction {
+  Declare(MenuItem, Action)
+
+  auto setIcon(const image& icon) -> void;
+  auto setText(const string& text) -> void;
+};
+
+}
diff --git a/hiro/gtk/action/menu-radio-item.cpp b/hiro/gtk/action/menu-radio-item.cpp
new file mode 100644
index 00000000..a3f05076
--- /dev/null
+++ b/hiro/gtk/action/menu-radio-item.cpp
@@ -0,0 +1,75 @@
+namespace hiro {
+
+static auto MenuRadioItem_activate(GtkCheckMenuItem* gtkCheckMenuItem, pMenuRadioItem* p) -> void {
+  p->_doActivate();
+}
+
+auto pMenuRadioItem::construct() -> void {
+  widget = gtk_radio_menu_item_new_with_mnemonic(0, "");
+  setGroup(state().group);
+  setText(state().text);
+  for(auto& weak : state().group) {
+    if(auto item = weak.acquire()) {
+      if(item->self()) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->self()->widget), item->checked());
+    }
+  }
+  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(MenuRadioItem_activate), (gpointer)this);
+}
+
+auto pMenuRadioItem::destruct() -> void {
+  if(widget) gtk_widget_destroy(widget), widget = nullptr;
+}
+
+auto pMenuRadioItem::setChecked() -> void {
+  _parent().lock();
+  for(auto& weak : state().group) {
+    if(auto item = weak.acquire()) {
+      if(item->self()) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->self()->widget), false);
+    }
+  }
+  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true);
+  _parent().unlock();
+}
+
+auto pMenuRadioItem::setGroup(const vector<shared_pointer_weak<mMenuRadioItem>>& group) -> void {
+  _parent().lock();
+  shared_pointer<mMenuRadioItem> first;
+  for(auto& weak : group) {
+    if(!first) {
+      first = weak.acquire();
+      continue;
+    }
+    if(auto item = weak.acquire()) {
+      if(item->self() && first->self()) {
+        GSList* currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(first->self()->widget));
+        if(currentGroup != gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item->self()->widget))) {
+          gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item->self()->widget), currentGroup);
+        }
+      }
+    }
+  }
+  _parent().unlock();
+}
+
+auto pMenuRadioItem::setText(const string& text) -> void {
+  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _mnemonic(text));
+}
+
+auto pMenuRadioItem::_doActivate() -> void {
+  if(!_parent().locked()) {
+    bool wasChecked = state().checked;
+    self().setChecked();
+    if(!wasChecked) self().doActivate();
+  }
+}
+
+auto pMenuRadioItem::_parent() -> pMenuRadioItem& {
+  if(state().group.size()) {
+    if(auto item = state().group.first().acquire()) {
+      if(item->self()) return *item->self();
+    }
+  }
+  return *this;
+}
+
+}
diff --git a/hiro/gtk/action/menu-radio-item.hpp b/hiro/gtk/action/menu-radio-item.hpp
new file mode 100644
index 00000000..f129c1a6
--- /dev/null
+++ b/hiro/gtk/action/menu-radio-item.hpp
@@ -0,0 +1,14 @@
+namespace hiro {
+
+struct pMenuRadioItem : pAction {
+  Declare(MenuRadioItem, Action)
+
+  auto setChecked() -> void;
+  auto setGroup(const vector<shared_pointer_weak<mMenuRadioItem>>& group) -> void;
+  auto setText(const string& text) -> void;
+
+  auto _doActivate() -> void;
+  auto _parent() -> pMenuRadioItem&;
+};
+
+}
diff --git a/hiro/gtk/action/menu-separator.cpp b/hiro/gtk/action/menu-separator.cpp
new file mode 100644
index 00000000..1344e93c
--- /dev/null
+++ b/hiro/gtk/action/menu-separator.cpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+auto pMenuSeparator::construct() -> void {
+  widget = gtk_separator_menu_item_new();
+}
+
+auto pMenuSeparator::destruct() -> void {
+  if(widget) gtk_widget_destroy(widget), widget = nullptr;
+}
+
+}
diff --git a/hiro/gtk/action/menu-separator.hpp b/hiro/gtk/action/menu-separator.hpp
new file mode 100644
index 00000000..5bf18a62
--- /dev/null
+++ b/hiro/gtk/action/menu-separator.hpp
@@ -0,0 +1,7 @@
+namespace hiro {
+
+struct pMenuSeparator : pAction {
+  Declare(MenuSeparator, Action)
+};
+
+}
diff --git a/hiro/gtk/action/menu.cpp b/hiro/gtk/action/menu.cpp
new file mode 100644
index 00000000..527a84ff
--- /dev/null
+++ b/hiro/gtk/action/menu.cpp
@@ -0,0 +1,48 @@
+namespace hiro {
+
+auto pMenu::construct() -> void {
+  gtkMenu = gtk_menu_new();
+  widget = gtk_image_menu_item_new_with_mnemonic("");
+  gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), gtkMenu);
+  setText(state().text);
+
+  for(auto& action : state().actions) append(*action);
+}
+
+auto pMenu::destruct() -> void {
+  gtk_widget_destroy(gtkMenu);
+  gtk_widget_destroy(widget);
+}
+
+auto pMenu::append(sAction action) -> void {
+  if(action->self()) {
+    gtk_menu_shell_append(GTK_MENU_SHELL(gtkMenu), action->self()->widget);
+    action->self()->setFont(action->font(true));
+    action->self()->setVisible(action->visible(true));
+  }
+}
+
+auto pMenu::remove(sAction action) -> void {
+}
+
+auto pMenu::setFont(const string& font) -> void {
+  pAction::setFont(font);
+  for(auto& action : state().actions) {
+    if(action->self()) action->self()->setFont(action->font(true));
+  }
+}
+
+auto pMenu::setIcon(const image& icon) -> void {
+  if(icon) {
+    GtkImage* gtkImage = CreateImage(icon, true);
+    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), (GtkWidget*)gtkImage);
+  } else {
+    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr);
+  }
+}
+
+auto pMenu::setText(const string& text) -> void {
+  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _mnemonic(text));
+}
+
+}
diff --git a/hiro/gtk/action/menu.hpp b/hiro/gtk/action/menu.hpp
new file mode 100644
index 00000000..c0e199a7
--- /dev/null
+++ b/hiro/gtk/action/menu.hpp
@@ -0,0 +1,15 @@
+namespace hiro {
+
+struct pMenu : pAction {
+  Declare(Menu, Action)
+
+  auto append(sAction action) -> void;
+  auto remove(sAction action) -> void;
+  auto setFont(const string& font) -> void override;
+  auto setIcon(const image& icon) -> void;
+  auto setText(const string& text) -> void;
+
+  GtkWidget* gtkMenu = nullptr;
+};
+
+}
diff --git a/hiro/gtk/application.cpp b/hiro/gtk/application.cpp
new file mode 100644
index 00000000..14783989
--- /dev/null
+++ b/hiro/gtk/application.cpp
@@ -0,0 +1,84 @@
+namespace hiro {
+
+XlibDisplay* pApplication::display = nullptr;
+
+void pApplication::run() {
+  if(Application::state.onMain) {
+    while(Application::state.quit == false) {
+      processEvents();
+      Application::doMain();
+    }
+  } else {
+    gtk_main();
+  }
+}
+
+bool pApplication::pendingEvents() {
+  return gtk_events_pending();
+}
+
+void pApplication::processEvents() {
+  while(pendingEvents()) gtk_main_iteration_do(false);
+}
+
+void pApplication::quit() {
+  //if gtk_main() was invoked, call gtk_main_quit()
+  if(gtk_main_level()) gtk_main_quit();
+}
+
+void pApplication::initialize() {
+  display = XOpenDisplay(nullptr);
+
+  settings = new Settings;
+  settings->load();
+
+  //set WM_CLASS to Application::name()
+  if(Application::state.name) gdk_set_program_class(Application::state.name);
+
+  #if 1
+  int argc = 1;
+  char* argv[] = {new char[8], nullptr};
+  strcpy(argv[0], "phoenix");
+  #else
+  //--g-fatal-warnings will force a trap on Gtk-CRITICAL errors
+  //this allows gdb to perform a backtrace to find error origin point
+  int argc = 2;
+  char* argv[] = {new char[8], new char[19], nullptr};
+  strcpy(argv[0], "phoenix");
+  strcpy(argv[1], "--g-fatal-warnings");
+  #endif
+  char** argvp = argv;
+  gtk_init(&argc, &argvp);
+
+  GtkSettings* gtkSettings = gtk_settings_get_default();
+  g_object_set(gtkSettings, "gtk-button-images", true, nullptr);
+
+  gtk_rc_parse_string(R"(
+    style "PhoenixWindow"
+    {
+      GtkWindow::resize-grip-width = 0
+      GtkWindow::resize-grip-height = 0
+    }
+    class "GtkWindow" style "PhoenixWindow"
+
+    style "PhoenixTreeView"
+    {
+      GtkTreeView::vertical-separator = 0
+    }
+    class "GtkTreeView" style "PhoenixTreeView"
+
+    style "PhoenixTabFrameCloseButton"
+    {
+      GtkWidget::focus-line-width = 0
+      GtkWidget::focus-padding = 0
+      GtkButton::default-border = {0, 0, 0, 0}
+      GtkButton::default-outer-border = {0, 0, 0, 0}
+      GtkButton::inner-border = {0, 1, 0, 0}
+    }
+    widget_class "*.<GtkNotebook>.<GtkHBox>.<GtkButton>" style "PhoenixTabFrameCloseButton"
+  )");
+
+  pKeyboard::initialize();
+}
+
+}
diff --git a/hiro/gtk/application.hpp b/hiro/gtk/application.hpp
new file mode 100644
index 00000000..4fd5d573
--- /dev/null
+++ b/hiro/gtk/application.hpp
@@ -0,0 +1,14 @@
+namespace hiro {
+
+struct pApplication {
+  static XlibDisplay* display;
+
+  static void run();
+  static bool pendingEvents();
+  static void processEvents();
+  static void quit();
+
+  static void initialize();
+};
+
+}
diff --git a/phoenix/gtk/browser-window.cpp b/hiro/gtk/browser-window.cpp
similarity index 78%
rename from phoenix/gtk/browser-window.cpp
rename to hiro/gtk/browser-window.cpp
index e2c9b5c5..1ca5ef80 100644
--- a/phoenix/gtk/browser-window.cpp
+++ b/hiro/gtk/browser-window.cpp
@@ -1,21 +1,21 @@
-namespace phoenix {
+namespace hiro {
 
 static void BrowserWindow_addFilters(GtkWidget* dialog, lstring filters) {
   for(auto& filter : filters) {
     GtkFileFilter* gtkFilter = gtk_file_filter_new();
     gtk_file_filter_set_name(gtkFilter, filter);
-    lstring patterns = filter.split<1>("(")(1).rtrim<1>(")").split(",");
-    for(auto& pattern : patterns) gtk_file_filter_add_pattern(gtkFilter, pattern.strip());
+    lstring patterns = filter.split<1>("(")(1).rtrim(")").split(",").strip();
+    for(auto& pattern : patterns) gtk_file_filter_add_pattern(gtkFilter, pattern);
     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), gtkFilter);
   }
 }
 
-string pBrowserWindow::directory(BrowserWindow::State& state) {
+auto pBrowserWindow::directory(BrowserWindow::State& state) -> string {
   string name;
 
   GtkWidget* dialog = gtk_file_chooser_dialog_new(
     state.title ? state.title : "Select Directory",
-    state.parent ? GTK_WINDOW(state.parent->p.widget) : (GtkWindow*)nullptr,
+    state.parent && state.parent->self() ? GTK_WINDOW(state.parent->self()->widget) : (GtkWindow*)nullptr,
     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
@@ -35,12 +35,12 @@ string pBrowserWindow::directory(BrowserWindow::State& state) {
   return name;
 }
 
-string pBrowserWindow::open(BrowserWindow::State& state) {
+auto pBrowserWindow::open(BrowserWindow::State& state) -> string {
   string name;
 
   GtkWidget* dialog = gtk_file_chooser_dialog_new(
     state.title ? state.title : "Open File",
-    state.parent ? GTK_WINDOW(state.parent->p.widget) : (GtkWindow*)nullptr,
+    state.parent && state.parent->self() ? GTK_WINDOW(state.parent->self()->widget) : (GtkWindow*)nullptr,
     GTK_FILE_CHOOSER_ACTION_OPEN,
     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
@@ -60,12 +60,12 @@ string pBrowserWindow::open(BrowserWindow::State& state) {
   return name;
 }
 
-string pBrowserWindow::save(BrowserWindow::State& state) {
+auto pBrowserWindow::save(BrowserWindow::State& state) -> string {
   string name;
 
   GtkWidget* dialog = gtk_file_chooser_dialog_new(
     state.title ? state.title : "Save File",
-    state.parent ? GTK_WINDOW(state.parent->p.widget) : (GtkWindow*)nullptr,
+    state.parent && state.parent->self() ? GTK_WINDOW(state.parent->self()->widget) : (GtkWindow*)nullptr,
     GTK_FILE_CHOOSER_ACTION_SAVE,
     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
diff --git a/hiro/gtk/browser-window.hpp b/hiro/gtk/browser-window.hpp
new file mode 100644
index 00000000..964acea4
--- /dev/null
+++ b/hiro/gtk/browser-window.hpp
@@ -0,0 +1,9 @@
+namespace hiro {
+
+struct pBrowserWindow {
+  static auto directory(BrowserWindow::State& state) -> string;
+  static auto open(BrowserWindow::State& state) -> string;
+  static auto save(BrowserWindow::State& state) -> string;
+};
+
+}
diff --git a/phoenix/gtk/desktop.cpp b/hiro/gtk/desktop.cpp
similarity index 88%
rename from phoenix/gtk/desktop.cpp
rename to hiro/gtk/desktop.cpp
index 700d081d..25b03fee 100644
--- a/phoenix/gtk/desktop.cpp
+++ b/hiro/gtk/desktop.cpp
@@ -1,4 +1,4 @@
-namespace phoenix {
+namespace hiro {
 
 Size pDesktop::size() {
   return {
@@ -27,7 +27,7 @@ Geometry pDesktop::workspace() {
 
   if(result == Success && returnAtom == XA_CARDINAL && format == 32 && items == 4) {
     unsigned long *workarea = (unsigned long*)data;
-    return {(signed)workarea[0], (signed)workarea[1], (unsigned)workarea[2], (unsigned)workarea[3]};
+    return {(signed)workarea[0], (signed)workarea[1], (signed)workarea[2], (signed)workarea[3]};
   }
 
   return {
diff --git a/hiro/gtk/desktop.hpp b/hiro/gtk/desktop.hpp
new file mode 100644
index 00000000..d063450a
--- /dev/null
+++ b/hiro/gtk/desktop.hpp
@@ -0,0 +1,8 @@
+namespace hiro {
+
+struct pDesktop {
+  static Size size();
+  static Geometry workspace();
+};
+
+}
diff --git a/phoenix/gtk/font.cpp b/hiro/gtk/font.cpp
similarity index 95%
rename from phoenix/gtk/font.cpp
rename to hiro/gtk/font.cpp
index 41232cbd..fcd6c23f 100644
--- a/phoenix/gtk/font.cpp
+++ b/hiro/gtk/font.cpp
@@ -1,4 +1,4 @@
-namespace phoenix {
+namespace hiro {
 
 string pFont::serif(unsigned size, string style) {
   if(size == 0) size = 8;
@@ -25,9 +25,7 @@ Size pFont::size(string font, string text) {
 }
 
 PangoFontDescription* pFont::create(string description) {
-  lstring part;
-  part.split<2>(",", description);
-  for(auto& item : part) item.trim(" ");
+  lstring part = description.split<2>(",").strip();
 
   string family = "Sans";
   unsigned size = 8u;
diff --git a/hiro/gtk/font.hpp b/hiro/gtk/font.hpp
new file mode 100644
index 00000000..8dbd17e9
--- /dev/null
+++ b/hiro/gtk/font.hpp
@@ -0,0 +1,16 @@
+namespace hiro {
+
+struct pFont {
+  static string serif(unsigned size, string style);
+  static string sans(unsigned size, string style);
+  static string monospace(unsigned size, string style);
+  static Size size(string font, string text);
+
+  static PangoFontDescription* create(string description);
+  static void free(PangoFontDescription* font);
+  static Size size(PangoFontDescription* font, string text);
+  static void setFont(GtkWidget* widget, string font);
+  static void setFont(GtkWidget* widget, gpointer font);
+};
+
+}
diff --git a/phoenix/gtk/header.hpp b/hiro/gtk/header.hpp
similarity index 56%
rename from phoenix/gtk/header.hpp
rename to hiro/gtk/header.hpp
index 036cabde..8e691ab5 100644
--- a/phoenix/gtk/header.hpp
+++ b/hiro/gtk/header.hpp
@@ -1,8 +1,11 @@
 #include <nall/xorg/guard.hpp>
-#include <gtk/gtk.h>
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <gtksourceview/gtksourceview.h>
+#include <gtksourceview/gtksourcelanguagemanager.h>
+#include <gtksourceview/gtksourcestyleschememanager.h>
 #include <cairo.h>
 #include <X11/Xatom.h>
 #include <nall/xorg/guard.hpp>
diff --git a/hiro/gtk/hotkey.cpp b/hiro/gtk/hotkey.cpp
new file mode 100644
index 00000000..73879ddb
--- /dev/null
+++ b/hiro/gtk/hotkey.cpp
@@ -0,0 +1,9 @@
+namespace hiro {
+
+auto pHotkey::construct() -> void {
+}
+
+auto pHotkey::destruct() -> void {
+}
+
+}
diff --git a/hiro/gtk/hotkey.hpp b/hiro/gtk/hotkey.hpp
new file mode 100644
index 00000000..4f476cdc
--- /dev/null
+++ b/hiro/gtk/hotkey.hpp
@@ -0,0 +1,7 @@
+namespace hiro {
+
+struct pHotkey : pObject {
+  Declare(Hotkey, Object)
+};
+
+}
diff --git a/hiro/gtk/keyboard.cpp b/hiro/gtk/keyboard.cpp
new file mode 100644
index 00000000..0223ccf3
--- /dev/null
+++ b/hiro/gtk/keyboard.cpp
@@ -0,0 +1,344 @@
+namespace hiro {
+
+auto pKeyboard::poll() -> vector<bool> {
+  vector<bool> result;
+  char state[256];
+  XQueryKeymap(pApplication::display, state);
+  for(auto& code : settings->keycodes) {
+    result.append(_pressed(state, code));
+  }
+  return result;
+}
+
+auto pKeyboard::pressed(unsigned code) -> bool {
+  char state[256];
+  XQueryKeymap(pApplication::display, state);
+  return _pressed(state, code);
+}
+
+auto pKeyboard::_pressed(char* state, uint16_t code) -> bool {
+  uint8_t lo = code >> 0;
+  uint8_t hi = code >> 8;
+  if(lo && state[lo >> 3] & (1 << (lo & 7))) return true;
+  if(hi && state[hi >> 3] & (1 << (hi & 7))) return true;
+  return false;
+}
+
+auto pKeyboard::_translate(unsigned code) -> signed {
+  switch(code) {
+  case GDK_Escape: return 0;
+  case GDK_F1: return 0;
+  case GDK_F2: return 0;
+  case GDK_F3: return 0;
+  case GDK_F4: return 0;
+  case GDK_F5: return 0;
+  case GDK_F6: return 0;
+  case GDK_F7: return 0;
+  case GDK_F8: return 0;
+  case GDK_F9: return 0;
+  case GDK_F10: return 0;
+  case GDK_F11: return 0;
+  case GDK_F12: return 0;
+
+  case GDK_Print: return 0;
+  case GDK_Scroll_Lock: return 0;
+  case GDK_Pause: return 0;
+
+  case GDK_Insert: return 0;
+  case GDK_Delete: return 0;
+  case GDK_Home: return 0;
+  case GDK_End: return 0;
+  case GDK_Prior: return 0;
+  case GDK_Next: return 0;
+
+  case GDK_Up: return 0;
+  case GDK_Down: return 0;
+  case GDK_Left: return 0;
+  case GDK_Right: return 0;
+
+  case GDK_grave: return '`';
+  case GDK_1: return '1';
+  case GDK_2: return '2';
+  case GDK_3: return '3';
+  case GDK_4: return '4';
+  case GDK_5: return '5';
+  case GDK_6: return '6';
+  case GDK_7: return '7';
+  case GDK_8: return '8';
+  case GDK_9: return '9';
+  case GDK_0: return '0';
+  case GDK_minus: return '-';
+  case GDK_equal: return '=';
+  case GDK_BackSpace: return '\b';
+
+  case GDK_asciitilde: return '~';
+  case GDK_exclam: return '!';
+  case GDK_at: return '@';
+  case GDK_numbersign: return '#';
+  case GDK_dollar: return '$';
+  case GDK_percent: return '%';
+  case GDK_asciicircum: return '^';
+  case GDK_ampersand: return '&';
+  case GDK_asterisk: return '*';
+  case GDK_parenleft: return '(';
+  case GDK_parenright: return ')';
+  case GDK_underscore: return '_';
+  case GDK_plus: return '+';
+
+  case GDK_Tab: return '\t';
+  case GDK_Caps_Lock: return 0;
+  case GDK_Return: return '\n';
+  case GDK_Shift_L: return 0;
+  case GDK_Shift_R: return 0;
+  case GDK_Control_L: return 0;
+  case GDK_Control_R: return 0;
+  case GDK_Alt_L: return 0;
+  case GDK_Alt_R: return 0;
+  case GDK_Super_L: return 0;
+  case GDK_Super_R: return 0;
+  case GDK_Menu: return 0;
+  case GDK_space: return ' ';
+
+  case GDK_bracketleft: return '[';
+  case GDK_bracketright: return ']';
+  case GDK_backslash: return '\\';
+  case GDK_semicolon: return ';';
+  case GDK_apostrophe: return '\'';
+  case GDK_comma: return ',';
+  case GDK_period: return '.';
+  case GDK_slash: return '/';
+
+  case GDK_braceleft: return '{';
+  case GDK_braceright: return '}';
+  case GDK_bar: return '|';
+  case GDK_colon: return ':';
+  case GDK_quotedbl: return '\"';
+  case GDK_less: return '<';
+  case GDK_greater: return '>';
+  case GDK_question: return '?';
+
+  case GDK_A: return 'A';
+  case GDK_B: return 'B';
+  case GDK_C: return 'C';
+  case GDK_D: return 'D';
+  case GDK_E: return 'E';
+  case GDK_F: return 'F';
+  case GDK_G: return 'G';
+  case GDK_H: return 'H';
+  case GDK_I: return 'I';
+  case GDK_J: return 'J';
+  case GDK_K: return 'K';
+  case GDK_L: return 'L';
+  case GDK_M: return 'M';
+  case GDK_N: return 'N';
+  case GDK_O: return 'O';
+  case GDK_P: return 'P';
+  case GDK_Q: return 'Q';
+  case GDK_R: return 'R';
+  case GDK_S: return 'S';
+  case GDK_T: return 'T';
+  case GDK_U: return 'U';
+  case GDK_V: return 'V';
+  case GDK_W: return 'W';
+  case GDK_X: return 'X';
+  case GDK_Y: return 'Y';
+  case GDK_Z: return 'Z';
+
+  case GDK_a: return 'a';
+  case GDK_b: return 'b';
+  case GDK_c: return 'c';
+  case GDK_d: return 'd';
+  case GDK_e: return 'e';
+  case GDK_f: return 'f';
+  case GDK_g: return 'g';
+  case GDK_h: return 'h';
+  case GDK_i: return 'i';
+  case GDK_j: return 'j';
+  case GDK_k: return 'k';
+  case GDK_l: return 'l';
+  case GDK_m: return 'm';
+  case GDK_n: return 'n';
+  case GDK_o: return 'o';
+  case GDK_p: return 'p';
+  case GDK_q: return 'q';
+  case GDK_r: return 'r';
+  case GDK_s: return 's';
+  case GDK_t: return 't';
+  case GDK_u: return 'u';
+  case GDK_v: return 'v';
+  case GDK_w: return 'w';
+  case GDK_x: return 'x';
+  case GDK_y: return 'y';
+  case GDK_z: return 'z';
+
+  case GDK_Num_Lock: return 0;
+  case GDK_KP_Divide: return '/';
+  case GDK_KP_Multiply: return '*';
+  case GDK_KP_Subtract: return '-';
+  case GDK_KP_Add: return '+';
+  case GDK_KP_Enter: return '\n';
+  case GDK_KP_Decimal: return '.';
+
+  case GDK_KP_1: return '1';
+  case GDK_KP_2: return '2';
+  case GDK_KP_3: return '3';
+  case GDK_KP_4: return '4';
+  case GDK_KP_5: return '5';
+  case GDK_KP_6: return '6';
+  case GDK_KP_7: return '7';
+  case GDK_KP_8: return '8';
+  case GDK_KP_9: return '9';
+  case GDK_KP_0: return '0';
+
+  case GDK_KP_Home: return 0;
+  case GDK_KP_End: return 0;
+  case GDK_KP_Page_Up: return 0;
+  case GDK_KP_Page_Down: return 0;
+  case GDK_KP_Up: return 0;
+  case GDK_KP_Down: return 0;
+  case GDK_KP_Left: return 0;
+  case GDK_KP_Right: return 0;
+  case GDK_KP_Begin: return 0;
+  case GDK_KP_Insert: return 0;
+  case GDK_KP_Delete: return 0;
+  }
+
+  return 0;
+}
+
+auto pKeyboard::initialize() -> void {
+  auto append = [](unsigned lo, unsigned hi = 0) {
+    lo = lo ? (uint8_t)XKeysymToKeycode(pApplication::display, lo) : 0;
+    hi = hi ? (uint8_t)XKeysymToKeycode(pApplication::display, hi) : 0;
+    settings->keycodes.append(lo | (hi << 8));
+  };
+
+  #define map(name, ...) if(key == name) { append(__VA_ARGS__); continue; }
+  for(auto& key : Keyboard::keys) {
+    map("Escape", XK_Escape);
+    map("F1", XK_F1);
+    map("F2", XK_F2);
+    map("F3", XK_F3);
+    map("F4", XK_F4);
+    map("F5", XK_F5);
+    map("F6", XK_F6);
+    map("F7", XK_F7);
+    map("F8", XK_F8);
+    map("F9", XK_F9);
+    map("F10", XK_F10);
+    map("F11", XK_F11);
+    map("F12", XK_F12);
+
+    map("PrintScreen", XK_Print);
+    map("ScrollLock", XK_Scroll_Lock);
+    map("Pause", XK_Pause);
+
+    map("Insert", XK_Insert);
+    map("Delete", XK_Delete);
+    map("Home", XK_Home);
+    map("End", XK_End);
+    map("PageUp", XK_Prior);
+    map("PageDown", XK_Next);
+
+    map("Up", XK_Up);
+    map("Down", XK_Down);
+    map("Left", XK_Left);
+    map("Right", XK_Right);
+
+    map("Grave", XK_asciitilde);
+    map("1", XK_1);
+    map("2", XK_2);
+    map("3", XK_3);
+    map("4", XK_4);
+    map("5", XK_5);
+    map("6", XK_6);
+    map("7", XK_7);
+    map("8", XK_8);
+    map("9", XK_9);
+    map("0", XK_0);
+    map("Dash", XK_minus);
+    map("Equal", XK_equal);
+    map("Backspace", XK_BackSpace);
+
+    map("Tab", XK_Tab);
+    map("CapsLock", XK_Caps_Lock);
+    map("LeftEnter", XK_Return);
+    map("LeftShift", XK_Shift_L);
+    map("RightShift", XK_Shift_R);
+    map("LeftControl", XK_Control_L);
+    map("RightControl", XK_Control_R);
+    map("LeftAlt", XK_Alt_L);
+    map("RightAlt", XK_Alt_R);
+    map("LeftSuper", XK_Super_L);
+    map("RightSuper", XK_Super_R);
+    map("Menu", XK_Menu);
+    map("Space", XK_space);
+
+    map("OpenBracket", XK_bracketleft);
+    map("CloseBracket", XK_bracketright);
+    map("Backslash", XK_backslash);
+    map("Semicolon", XK_semicolon);
+    map("Apostrophe", XK_apostrophe);
+    map("Comma", XK_comma);
+    map("Period", XK_period);
+    map("Slash", XK_slash);
+
+    map("A", XK_A);
+    map("B", XK_B);
+    map("C", XK_C);
+    map("D", XK_D);
+    map("E", XK_E);
+    map("F", XK_F);
+    map("G", XK_G);
+    map("H", XK_H);
+    map("I", XK_I);
+    map("J", XK_J);
+    map("K", XK_K);
+    map("L", XK_L);
+    map("M", XK_M);
+    map("N", XK_N);
+    map("O", XK_O);
+    map("P", XK_P);
+    map("Q", XK_Q);
+    map("R", XK_R);
+    map("S", XK_S);
+    map("T", XK_T);
+    map("U", XK_U);
+    map("V", XK_V);
+    map("W", XK_W);
+    map("X", XK_X);
+    map("Y", XK_Y);
+    map("Z", XK_Z);
+
+    map("NumLock", XK_Num_Lock);
+    map("Divide", XK_KP_Divide);
+    map("Multiply", XK_KP_Multiply);
+    map("Subtract", XK_KP_Subtract);
+    map("Add", XK_KP_Add);
+    map("RightEnter", XK_KP_Enter);
+    map("Point", XK_KP_Decimal);
+
+    map("One", XK_KP_1);
+    map("Two", XK_KP_2);
+    map("Three", XK_KP_3);
+    map("Four", XK_KP_4);
+    map("Five", XK_KP_5);
+    map("Six", XK_KP_6);
+    map("Seven", XK_KP_7);
+    map("Eight", XK_KP_8);
+    map("Nine", XK_KP_9);
+    map("Zero", XK_KP_0);
+
+    map("Shift", XK_Shift_L, XK_Shift_R);
+    map("Control", XK_Control_L, XK_Control_R);
+    map("Alt", XK_Alt_L, XK_Alt_R);
+    map("Super", XK_Super_L, XK_Super_R);
+    map("Enter", XK_Return, XK_KP_Enter);
+
+    print("[phoenix/gtk] error: unhandled key: ", key, "\n");
+    append(0);
+  }
+  #undef map
+}
+
+}
diff --git a/hiro/gtk/keyboard.hpp b/hiro/gtk/keyboard.hpp
new file mode 100644
index 00000000..06e5387c
--- /dev/null
+++ b/hiro/gtk/keyboard.hpp
@@ -0,0 +1,13 @@
+namespace hiro {
+
+struct pKeyboard {
+  static auto poll() -> vector<bool>;
+  static auto pressed(unsigned code) -> bool;
+
+  static auto _pressed(char* state, uint16_t code) -> bool;
+  static auto _translate(unsigned code) -> signed;
+
+  static auto initialize() -> void;
+};
+
+}
diff --git a/hiro/gtk/layout.cpp b/hiro/gtk/layout.cpp
new file mode 100644
index 00000000..607b0cf2
--- /dev/null
+++ b/hiro/gtk/layout.cpp
@@ -0,0 +1,29 @@
+namespace hiro {
+
+auto pLayout::construct() -> void {
+  for(auto& sizable : state().sizables) sizable->construct();
+}
+
+auto pLayout::destruct() -> void {
+  for(auto& sizable : state().sizables) sizable->destruct();
+}
+
+auto pLayout::setEnabled(bool enabled) -> void {
+  for(auto& sizable : state().sizables) {
+    if(sizable->self()) sizable->self()->setEnabled(sizable->enabled(true));
+  }
+}
+
+auto pLayout::setFont(const string& font) -> void {
+  for(auto& sizable : state().sizables) {
+    if(sizable->self()) sizable->self()->setFont(sizable->font(true));
+  }
+}
+
+auto pLayout::setVisible(bool visible) -> void {
+  for(auto& sizable : state().sizables) {
+    if(sizable->self()) sizable->self()->setVisible(sizable->visible(true));
+  }
+}
+
+}
diff --git a/hiro/gtk/layout.hpp b/hiro/gtk/layout.hpp
new file mode 100644
index 00000000..f77bfc84
--- /dev/null
+++ b/hiro/gtk/layout.hpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+struct pLayout : pSizable {
+  Declare(Layout, Sizable)
+
+  auto setEnabled(bool enabled) -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setVisible(bool visible) -> void override;
+};
+
+}
diff --git a/hiro/gtk/menu-bar.cpp b/hiro/gtk/menu-bar.cpp
new file mode 100644
index 00000000..94127d2d
--- /dev/null
+++ b/hiro/gtk/menu-bar.cpp
@@ -0,0 +1,45 @@
+namespace hiro {
+
+auto pMenuBar::construct() -> void {
+}
+
+auto pMenuBar::destruct() -> void {
+}
+
+auto pMenuBar::append(shared_pointer<mMenu> menu) -> void {
+  if(auto parent = _parent()) {
+    parent->_append(*menu);
+    if(menu->self()) {
+      menu->self()->setFont(menu->font(true));
+      menu->self()->setVisible(menu->visible(true));
+    }
+  }
+}
+
+auto pMenuBar::remove(shared_pointer<mMenu> menu) -> void {
+}
+
+auto pMenuBar::setEnabled(bool enabled) -> void {
+  if(auto parent = _parent()) {
+    parent->_setMenuEnabled(enabled);
+  }
+}
+
+auto pMenuBar::setFont(const string& font) -> void {
+  if(auto parent = _parent()) {
+    parent->_setMenuFont(font);
+  }
+}
+
+auto pMenuBar::setVisible(bool visible) -> void {
+  if(auto parent = _parent()) {
+    parent->_setMenuVisible(visible);
+  }
+}
+
+auto pMenuBar::_parent() -> pWindow* {
+  if(auto parent = self().parentWindow()) return parent->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/menu-bar.hpp b/hiro/gtk/menu-bar.hpp
new file mode 100644
index 00000000..7c1897cf
--- /dev/null
+++ b/hiro/gtk/menu-bar.hpp
@@ -0,0 +1,15 @@
+namespace hiro {
+
+struct pMenuBar : pObject {
+  Declare(MenuBar, Object)
+
+  auto append(shared_pointer<mMenu> menu) -> void;
+  auto remove(shared_pointer<mMenu> menu) -> void;
+  auto setEnabled(bool enabled) -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setVisible(bool visible) -> void override;
+
+  auto _parent() -> pWindow*;
+};
+
+}
diff --git a/phoenix/gtk/message-window.cpp b/hiro/gtk/message-window.cpp
similarity index 73%
rename from phoenix/gtk/message-window.cpp
rename to hiro/gtk/message-window.cpp
index 2b542a19..88e3874c 100644
--- a/phoenix/gtk/message-window.cpp
+++ b/hiro/gtk/message-window.cpp
@@ -1,13 +1,13 @@
-namespace phoenix {
+namespace hiro {
 
-static MessageWindow::Response Message(MessageWindow::State& state, GtkMessageType messageStyle) {
+static auto Message(MessageWindow::State& state, GtkMessageType messageStyle) -> MessageWindow::Response {
   GtkWidget* dialog = gtk_message_dialog_new(
-    state.parent ? GTK_WINDOW(state.parent->p.widget) : (GtkWindow*)nullptr,
+    state.parent && state.parent->self() ? GTK_WINDOW(state.parent->self()->widget) : (GtkWindow*)nullptr,
     GTK_DIALOG_MODAL, messageStyle, GTK_BUTTONS_NONE, "%s", (const char*)state.text
   );
 
   if(state.title) gtk_window_set_title(GTK_WINDOW(dialog), state.title);
-  else if(applicationState.name) gtk_window_set_title(GTK_WINDOW(dialog), applicationState.name);
+  else if(Application::state.name) gtk_window_set_title(GTK_WINDOW(dialog), Application::state.name);
 
   switch(state.buttons) {
   case MessageWindow::Buttons::Ok:
@@ -41,19 +41,19 @@ static MessageWindow::Response Message(MessageWindow::State& state, GtkMessageTy
   throw;
 }
 
-MessageWindow::Response pMessageWindow::error(MessageWindow::State& state) {
+auto pMessageWindow::error(MessageWindow::State& state) -> MessageWindow::Response {
   return Message(state, GTK_MESSAGE_ERROR);
 }
 
-MessageWindow::Response pMessageWindow::information(MessageWindow::State& state) {
+auto pMessageWindow::information(MessageWindow::State& state) -> MessageWindow::Response {
   return Message(state, GTK_MESSAGE_INFO);
 }
 
-MessageWindow::Response pMessageWindow::question(MessageWindow::State& state) {
+auto pMessageWindow::question(MessageWindow::State& state) -> MessageWindow::Response {
   return Message(state, GTK_MESSAGE_QUESTION);
 }
 
-MessageWindow::Response pMessageWindow::warning(MessageWindow::State& state) {
+auto pMessageWindow::warning(MessageWindow::State& state) -> MessageWindow::Response {
   return Message(state, GTK_MESSAGE_WARNING);
 }
 
diff --git a/hiro/gtk/message-window.hpp b/hiro/gtk/message-window.hpp
new file mode 100644
index 00000000..4daadc3d
--- /dev/null
+++ b/hiro/gtk/message-window.hpp
@@ -0,0 +1,10 @@
+namespace hiro {
+
+struct pMessageWindow {
+  static auto error(MessageWindow::State& state) -> MessageWindow::Response;
+  static auto information(MessageWindow::State& state) -> MessageWindow::Response;
+  static auto question(MessageWindow::State& state) -> MessageWindow::Response;
+  static auto warning(MessageWindow::State& state) -> MessageWindow::Response;
+};
+
+}
diff --git a/phoenix/gtk/monitor.cpp b/hiro/gtk/monitor.cpp
similarity index 95%
rename from phoenix/gtk/monitor.cpp
rename to hiro/gtk/monitor.cpp
index ccea2ebf..649c0823 100644
--- a/phoenix/gtk/monitor.cpp
+++ b/hiro/gtk/monitor.cpp
@@ -1,4 +1,4 @@
-namespace phoenix {
+namespace hiro {
 
 unsigned pMonitor::count() {
   return gdk_screen_get_n_monitors(gdk_screen_get_default());
diff --git a/hiro/gtk/monitor.hpp b/hiro/gtk/monitor.hpp
new file mode 100644
index 00000000..c616d6a8
--- /dev/null
+++ b/hiro/gtk/monitor.hpp
@@ -0,0 +1,9 @@
+namespace hiro {
+
+struct pMonitor {
+  static unsigned count();
+  static Geometry geometry(unsigned monitor);
+  static unsigned primary();
+};
+
+}
diff --git a/phoenix/gtk/mouse.cpp b/hiro/gtk/mouse.cpp
similarity index 86%
rename from phoenix/gtk/mouse.cpp
rename to hiro/gtk/mouse.cpp
index 2e2a4021..0e6a6c94 100644
--- a/phoenix/gtk/mouse.cpp
+++ b/hiro/gtk/mouse.cpp
@@ -1,6 +1,6 @@
-namespace phoenix {
+namespace hiro {
 
-Position pMouse::position() {
+auto pMouse::position() -> Position {
   XlibWindow root, child;
   int rootx, rooty, winx, winy;
   unsigned int mask;
@@ -8,7 +8,7 @@ Position pMouse::position() {
   return {rootx, rooty};
 }
 
-bool pMouse::pressed(Mouse::Button button) {
+auto pMouse::pressed(Mouse::Button button) -> bool {
   XlibWindow root, child;
   int rootx, rooty, winx, winy;
   unsigned int mask;
diff --git a/hiro/gtk/mouse.hpp b/hiro/gtk/mouse.hpp
new file mode 100644
index 00000000..805671fa
--- /dev/null
+++ b/hiro/gtk/mouse.hpp
@@ -0,0 +1,8 @@
+namespace hiro {
+
+struct pMouse {
+  static auto position() -> Position;
+  static auto pressed(Mouse::Button button) -> bool;
+};
+
+}
diff --git a/hiro/gtk/object.cpp b/hiro/gtk/object.cpp
new file mode 100644
index 00000000..0eb9c64a
--- /dev/null
+++ b/hiro/gtk/object.cpp
@@ -0,0 +1,31 @@
+namespace hiro {
+
+auto pObject::construct() -> void {
+}
+
+auto pObject::destruct() -> void {
+}
+
+auto pObject::focused() const -> bool {
+  return false;
+}
+
+auto pObject::remove() -> void {
+}
+
+auto pObject::reset() -> void {
+}
+
+auto pObject::setEnabled(bool enabled) -> void {
+}
+
+auto pObject::setFocused() -> void {
+}
+
+auto pObject::setFont(const string& font) -> void {
+}
+
+auto pObject::setVisible(bool visible) -> void {
+}
+
+}
diff --git a/hiro/gtk/object.hpp b/hiro/gtk/object.hpp
new file mode 100644
index 00000000..45cfe490
--- /dev/null
+++ b/hiro/gtk/object.hpp
@@ -0,0 +1,27 @@
+namespace hiro {
+
+struct pObject {
+  pObject(mObject& reference) : reference(reference) {}
+  virtual ~pObject() {}
+  auto self() const -> mObject& { return (mObject&)reference; }
+  auto state() const -> mObject::State& { return self().state; }
+  virtual auto construct() -> void;
+  virtual auto destruct() -> void;
+
+  virtual auto focused() const -> bool;
+  virtual auto remove() -> void;
+  virtual auto reset() -> void;
+  virtual auto setEnabled(bool enabled) -> void;
+  virtual auto setFocused() -> void;
+  virtual auto setFont(const string& font) -> void;
+  virtual auto setVisible(bool visible) -> void;
+
+  auto locked() const -> bool { return locks != 0; }
+  auto lock() -> void { ++locks; }
+  auto unlock() -> void { --locks; }
+
+  mObject& reference;
+  signed locks = 0;
+};
+
+}
diff --git a/phoenix/gtk/platform.cpp b/hiro/gtk/platform.cpp
similarity index 63%
rename from phoenix/gtk/platform.cpp
rename to hiro/gtk/platform.cpp
index 9d6cdf70..63868a21 100644
--- a/phoenix/gtk/platform.cpp
+++ b/hiro/gtk/platform.cpp
@@ -1,24 +1,30 @@
 #include "platform.hpp"
-
 #include "utility.cpp"
-#include "settings.cpp"
 
+#include "font.cpp"
 #include "desktop.cpp"
 #include "monitor.cpp"
 #include "keyboard.cpp"
 #include "mouse.cpp"
 #include "browser-window.cpp"
 #include "message-window.cpp"
-#include "font.cpp"
+#include "object.cpp"
+#include "hotkey.cpp"
 #include "timer.cpp"
 #include "window.cpp"
+#include "status-bar.cpp"
+#include "menu-bar.cpp"
+#include "popup-menu.cpp"
 
 #include "action/action.cpp"
 #include "action/menu.cpp"
-#include "action/separator.cpp"
-#include "action/item.cpp"
-#include "action/check-item.cpp"
-#include "action/radio-item.cpp"
+#include "action/menu-separator.cpp"
+#include "action/menu-item.cpp"
+#include "action/menu-check-item.cpp"
+#include "action/menu-radio-item.cpp"
+
+#include "sizable.cpp"
+#include "layout.cpp"
 
 #include "widget/widget.cpp"
 #include "widget/button.cpp"
@@ -26,21 +32,31 @@
 #include "widget/check-button.cpp"
 #include "widget/check-label.cpp"
 #include "widget/combo-button.cpp"
+#include "widget/combo-button-item.cpp"
 #include "widget/console.cpp"
 #include "widget/frame.cpp"
 #include "widget/hex-edit.cpp"
 #include "widget/horizontal-scroller.cpp"
 #include "widget/horizontal-slider.cpp"
+#include "widget/icon-view.cpp"
+#include "widget/icon-view-item.cpp"
 #include "widget/label.cpp"
 #include "widget/line-edit.cpp"
 #include "widget/list-view.cpp"
+#include "widget/list-view-column.cpp"
+#include "widget/list-view-item.cpp"
 #include "widget/progress-bar.cpp"
 #include "widget/radio-button.cpp"
 #include "widget/radio-label.cpp"
+#include "widget/source-edit.cpp"
 #include "widget/tab-frame.cpp"
+#include "widget/tab-frame-item.cpp"
 #include "widget/text-edit.cpp"
+#include "widget/tree-view.cpp"
+#include "widget/tree-view-item.cpp"
 #include "widget/vertical-scroller.cpp"
 #include "widget/vertical-slider.cpp"
 #include "widget/viewport.cpp"
 
 #include "application.cpp"
+#include "settings.cpp"
diff --git a/hiro/gtk/platform.hpp b/hiro/gtk/platform.hpp
new file mode 100644
index 00000000..06b3f5ea
--- /dev/null
+++ b/hiro/gtk/platform.hpp
@@ -0,0 +1,75 @@
+namespace hiro {
+  struct pWindow;
+  struct pMenu;
+  struct pLayout;
+  struct pWidget;
+};
+
+#define Declare(Name, Base) \
+  p##Name(m##Name& reference) : p##Base(reference) {} \
+  auto self() const -> m##Name& { return (m##Name&)reference; } \
+  auto state() const -> m##Name::State& { return self().state; } \
+  auto construct() -> void override; \
+  auto destruct() -> void override; \
+
+#include "font.hpp"
+#include "desktop.hpp"
+#include "monitor.hpp"
+#include "keyboard.hpp"
+#include "mouse.hpp"
+#include "browser-window.hpp"
+#include "message-window.hpp"
+#include "object.hpp"
+#include "hotkey.hpp"
+#include "timer.hpp"
+#include "window.hpp"
+#include "status-bar.hpp"
+#include "menu-bar.hpp"
+#include "popup-menu.hpp"
+
+#include "action/action.hpp"
+#include "action/menu.hpp"
+#include "action/menu-separator.hpp"
+#include "action/menu-item.hpp"
+#include "action/menu-check-item.hpp"
+#include "action/menu-radio-item.hpp"
+
+#include "sizable.hpp"
+#include "layout.hpp"
+
+#include "widget/widget.hpp"
+#include "widget/button.hpp"
+#include "widget/canvas.hpp"
+#include "widget/check-button.hpp"
+#include "widget/check-label.hpp"
+#include "widget/combo-button.hpp"
+#include "widget/combo-button-item.hpp"
+#include "widget/console.hpp"
+#include "widget/frame.hpp"
+#include "widget/hex-edit.hpp"
+#include "widget/horizontal-scroller.hpp"
+#include "widget/horizontal-slider.hpp"
+#include "widget/icon-view.hpp"
+#include "widget/icon-view-item.hpp"
+#include "widget/label.hpp"
+#include "widget/line-edit.hpp"
+#include "widget/list-view.hpp"
+#include "widget/list-view-column.hpp"
+#include "widget/list-view-item.hpp"
+#include "widget/progress-bar.hpp"
+#include "widget/radio-button.hpp"
+#include "widget/radio-label.hpp"
+#include "widget/source-edit.hpp"
+#include "widget/tab-frame.hpp"
+#include "widget/tab-frame-item.hpp"
+#include "widget/text-edit.hpp"
+#include "widget/tree-view.hpp"
+#include "widget/tree-view-item.hpp"
+#include "widget/vertical-scroller.hpp"
+#include "widget/vertical-slider.hpp"
+#include "widget/viewport.hpp"
+
+#undef Declare
+
+#include "application.hpp"
+#include "settings.hpp"
diff --git a/hiro/gtk/popup-menu.cpp b/hiro/gtk/popup-menu.cpp
new file mode 100644
index 00000000..752d0877
--- /dev/null
+++ b/hiro/gtk/popup-menu.cpp
@@ -0,0 +1,32 @@
+namespace hiro {
+
+auto pPopupMenu::construct() -> void {
+  gtkMenu = gtk_menu_new();
+}
+
+auto pPopupMenu::destruct() -> void {
+  gtk_widget_destroy(gtkMenu);
+}
+
+auto pPopupMenu::append(sAction action) -> void {
+  if(action->self()) {
+    gtk_menu_shell_append(GTK_MENU_SHELL(gtkMenu), action->self()->widget);
+    action->self()->setFont(action->font(true));
+    action->self()->setVisible(action->visible(true));
+  }
+}
+
+auto pPopupMenu::remove(sAction action) -> void {
+}
+
+auto pPopupMenu::setFont(const string& font) -> void {
+  for(auto& action : state().actions) {
+    if(action->self()) action->self()->setFont(action->font(true));
+  }
+}
+
+auto pPopupMenu::setVisible(bool visible) -> void {
+  if(visible) gtk_menu_popup(GTK_MENU(gtkMenu), nullptr, nullptr, nullptr, nullptr, 0, gtk_get_current_event_time());
+}
+
+}
diff --git a/hiro/gtk/popup-menu.hpp b/hiro/gtk/popup-menu.hpp
new file mode 100644
index 00000000..03548cfb
--- /dev/null
+++ b/hiro/gtk/popup-menu.hpp
@@ -0,0 +1,14 @@
+namespace hiro {
+
+struct pPopupMenu : pObject {
+  Declare(PopupMenu, Object)
+
+  auto append(sAction action) -> void;
+  auto remove(sAction action) -> void;
+  auto setFont(const string& font) -> void override;
+  auto setVisible(bool visible) -> void;
+
+  GtkWidget* gtkMenu = nullptr;
+};
+
+}
diff --git a/phoenix/gtk/settings.cpp b/hiro/gtk/settings.cpp
similarity index 93%
rename from phoenix/gtk/settings.cpp
rename to hiro/gtk/settings.cpp
index 6c411c36..c1754dae 100644
--- a/phoenix/gtk/settings.cpp
+++ b/hiro/gtk/settings.cpp
@@ -1,6 +1,4 @@
-namespace phoenix {
-
-static Settings* settings = nullptr;
+namespace hiro {
 
 void Settings::load() {
   string path = {userpath(), ".config/phoenix/"};
diff --git a/hiro/gtk/settings.hpp b/hiro/gtk/settings.hpp
new file mode 100644
index 00000000..df69ac2d
--- /dev/null
+++ b/hiro/gtk/settings.hpp
@@ -0,0 +1,26 @@
+namespace hiro {
+
+struct Settings : Configuration::Document {
+  vector<uint16_t> keycodes;
+
+  struct Geometry : Configuration::Node {
+    signed frameX;
+    signed frameY;
+    signed frameWidth;
+    signed frameHeight;
+    signed menuHeight;
+    signed statusHeight;
+  } geometry;
+
+  struct Window : Configuration::Node {
+    unsigned backgroundColor;
+  } window;
+
+  void load();
+  void save();
+  Settings();
+};
+
+static Settings* settings = nullptr;
+
+}
diff --git a/hiro/gtk/sizable.cpp b/hiro/gtk/sizable.cpp
new file mode 100644
index 00000000..c882be2b
--- /dev/null
+++ b/hiro/gtk/sizable.cpp
@@ -0,0 +1,16 @@
+namespace hiro {
+
+auto pSizable::construct() -> void {
+}
+
+auto pSizable::destruct() -> void {
+}
+
+auto pSizable::minimumSize() const -> Size {
+  return {0, 0};
+}
+
+auto pSizable::setGeometry(Geometry geometry) -> void {
+}
+
+}
diff --git a/hiro/gtk/sizable.hpp b/hiro/gtk/sizable.hpp
new file mode 100644
index 00000000..12dfc9f0
--- /dev/null
+++ b/hiro/gtk/sizable.hpp
@@ -0,0 +1,10 @@
+namespace hiro {
+
+struct pSizable : pObject {
+  Declare(Sizable, Object)
+
+  virtual auto minimumSize() const -> Size;
+  virtual auto setGeometry(Geometry geometry) -> void;
+};
+
+}
diff --git a/hiro/gtk/status-bar.cpp b/hiro/gtk/status-bar.cpp
new file mode 100644
index 00000000..02ffafbf
--- /dev/null
+++ b/hiro/gtk/status-bar.cpp
@@ -0,0 +1,38 @@
+namespace hiro {
+
+auto pStatusBar::construct() -> void {
+}
+
+auto pStatusBar::destruct() -> void {
+}
+
+auto pStatusBar::setEnabled(bool enabled) -> void {
+  if(auto parent = _parent()) {
+    parent->_setStatusEnabled(enabled);
+  }
+}
+
+auto pStatusBar::setFont(const string& font) -> void {
+  if(auto parent = _parent()) {
+    parent->_setStatusFont(font);
+  }
+}
+
+auto pStatusBar::setText(const string& text) -> void {
+  if(auto parent = _parent()) {
+    parent->_setStatusText(text);
+  }
+}
+
+auto pStatusBar::setVisible(bool visible) -> void {
+  if(auto parent = _parent()) {
+    parent->_setStatusVisible(visible);
+  }
+}
+
+auto pStatusBar::_parent() -> pWindow* {
+  if(auto parent = self().parentWindow()) return parent->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/status-bar.hpp b/hiro/gtk/status-bar.hpp
new file mode 100644
index 00000000..7f178dc5
--- /dev/null
+++ b/hiro/gtk/status-bar.hpp
@@ -0,0 +1,14 @@
+namespace hiro {
+
+struct pStatusBar : pObject {
+  Declare(StatusBar, Object)
+
+  auto setEnabled(bool enabled) -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setText(const string& text) -> void;
+  auto setVisible(bool visible) -> void override;
+
+  auto _parent() -> pWindow*;
+};
+
+}
diff --git a/hiro/gtk/timer.cpp b/hiro/gtk/timer.cpp
new file mode 100644
index 00000000..49dee689
--- /dev/null
+++ b/hiro/gtk/timer.cpp
@@ -0,0 +1,31 @@
+namespace hiro {
+
+static auto Timer_trigger(pTimer* p) -> signed {
+  //timer may have been disabled prior to triggering, so check state
+  if(p->self().enabled(true)) p->self().doActivate();
+
+  //callback may have disabled timer, so check state again
+  if(p->self().enabled(true)) {
+    g_timeout_add(p->state().interval, (GSourceFunc)Timer_trigger, (gpointer)p);
+  }
+
+  //kill this timer instance (it is spawned above if needed again)
+  return false;
+}
+
+auto pTimer::construct() -> void {
+}
+
+auto pTimer::destruct() -> void {
+}
+
+auto pTimer::setEnabled(bool enabled) -> void {
+  if(enabled) {
+    g_timeout_add(state().interval, (GSourceFunc)Timer_trigger, (gpointer)this);
+  }
+}
+
+auto pTimer::setInterval(unsigned interval) -> void {
+}
+
+}
diff --git a/hiro/gtk/timer.hpp b/hiro/gtk/timer.hpp
new file mode 100644
index 00000000..d09565e0
--- /dev/null
+++ b/hiro/gtk/timer.hpp
@@ -0,0 +1,10 @@
+namespace hiro {
+
+struct pTimer : pObject {
+  Declare(Timer, Object)
+
+  auto setEnabled(bool enabled) -> void;
+  auto setInterval(unsigned interval) -> void;
+};
+
+}
diff --git a/hiro/gtk/utility.cpp b/hiro/gtk/utility.cpp
new file mode 100644
index 00000000..1fbdbd36
--- /dev/null
+++ b/hiro/gtk/utility.cpp
@@ -0,0 +1,49 @@
+namespace hiro {
+
+static auto CreateColor(const Color& color) -> GdkColor {
+  GdkColor gdkColor;
+  gdkColor.pixel = (color.red() << 16) | (color.green() << 8) | (color.blue() << 0);
+  gdkColor.red = (color.red() << 8) | (color.red() << 0);
+  gdkColor.green = (color.green() << 8) | (color.green() << 0);
+  gdkColor.blue = (color.blue() << 8) | (color.blue() << 0);
+  return gdkColor;
+}
+
+static auto CreatePixbuf(const nall::image& image, bool scale = false) -> GdkPixbuf* {
+  nall::image gdkImage = image;
+  gdkImage.transform(0, 32, 255u << 24, 255u << 0, 255u << 8, 255u << 16);
+  if(scale) gdkImage.scale(15, 15);
+
+  GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, gdkImage.width, gdkImage.height);
+  memcpy(gdk_pixbuf_get_pixels(pixbuf), gdkImage.data, gdkImage.width * gdkImage.height * 4);
+
+  return pixbuf;
+}
+
+static auto CreateImage(const nall::image& image, bool scale = false) -> GtkImage* {
+  GdkPixbuf* pixbuf = CreatePixbuf(image, scale);
+  GtkImage* gtkImage = (GtkImage*)gtk_image_new_from_pixbuf(pixbuf);
+  g_object_unref(pixbuf);
+  return gtkImage;
+}
+
+static auto DropPaths(GtkSelectionData* data) -> lstring {
+  gchar** uris = gtk_selection_data_get_uris(data);
+  if(uris == nullptr) return {};
+
+  lstring paths;
+  for(unsigned n = 0; uris[n] != nullptr; n++) {
+    gchar* pathname = g_filename_from_uri(uris[n], nullptr, nullptr);
+    if(pathname == nullptr) continue;
+
+    string path = pathname;
+    g_free(pathname);
+    if(directory::exists(path) && !path.endsWith("/")) path.append("/");
+    paths.append(path);
+  }
+
+  g_strfreev(uris);
+  return paths;
+}
+
+}
diff --git a/hiro/gtk/widget/button.cpp b/hiro/gtk/widget/button.cpp
new file mode 100644
index 00000000..7c1e5872
--- /dev/null
+++ b/hiro/gtk/widget/button.cpp
@@ -0,0 +1,68 @@
+namespace hiro {
+
+static auto Button_activate(GtkButton* gtkButton, pButton* p) -> void { p->_doActivate(); }
+
+auto pButton::construct() -> void {
+  gtkWidget = gtk_button_new();
+  gtkButton = GTK_BUTTON(gtkWidget);
+
+  setBordered(state().bordered);
+  setIcon(state().icon);
+  setOrientation(state().orientation);
+  setText(state().text);
+
+  g_signal_connect(G_OBJECT(gtkButton), "clicked", G_CALLBACK(Button_activate), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pButton::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pButton::minimumSize() const -> Size {
+  Size size = pFont::size(self().font(true), state().text);
+
+  if(state().orientation == Orientation::Horizontal) {
+    size.setWidth(size.width() + state().icon.width);
+    size.setHeight(max(size.height(), state().icon.height));
+  }
+
+  if(state().orientation == Orientation::Vertical) {
+    size.setWidth(max(size.width(), state().icon.width));
+    size.setHeight(size.height() + state().icon.height);
+  }
+
+  return {size.width() + (state().text ? 24 : 12), size.height() + 12};
+}
+
+auto pButton::setBordered(bool bordered) -> void {
+  gtk_button_set_relief(gtkButton, bordered ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE);
+}
+
+auto pButton::setIcon(const image& icon) -> void {
+  if(icon) {
+    auto gtkImage = CreateImage(icon);
+    gtk_button_set_image(gtkButton, (GtkWidget*)gtkImage);
+  } else {
+    gtk_button_set_image(gtkButton, nullptr);
+  }
+}
+
+auto pButton::setOrientation(Orientation orientation) -> void {
+  switch(orientation) {
+  case Orientation::Horizontal: gtk_button_set_image_position(gtkButton, GTK_POS_LEFT); break;
+  case Orientation::Vertical:   gtk_button_set_image_position(gtkButton, GTK_POS_TOP);  break;
+  }
+}
+
+auto pButton::setText(const string& text) -> void {
+  gtk_button_set_label(gtkButton, text);
+  setFont(self().font(true));  //gtk_button_set_label() recreates label, which destroys currently assigned font
+}
+
+auto pButton::_doActivate() -> void {
+  self().doActivate();
+}
+
+}
diff --git a/hiro/gtk/widget/button.hpp b/hiro/gtk/widget/button.hpp
new file mode 100644
index 00000000..9cabea5d
--- /dev/null
+++ b/hiro/gtk/widget/button.hpp
@@ -0,0 +1,17 @@
+namespace hiro {
+
+struct pButton : pWidget {
+  Declare(Button, Widget)
+
+  auto minimumSize() const -> Size override;
+  auto setBordered(bool bordered) -> void;
+  auto setIcon(const image& icon) -> void;
+  auto setOrientation(Orientation orientation) -> void;
+  auto setText(const string& text) -> void;
+
+  auto _doActivate() -> void;
+
+  GtkButton* gtkButton = nullptr;
+};
+
+}
diff --git a/hiro/gtk/widget/canvas.cpp b/hiro/gtk/widget/canvas.cpp
new file mode 100644
index 00000000..e40c3baf
--- /dev/null
+++ b/hiro/gtk/widget/canvas.cpp
@@ -0,0 +1,212 @@
+namespace hiro {
+
+static auto Canvas_drop(GtkWidget* widget, GdkDragContext* context, signed x, signed y,
+GtkSelectionData* data, unsigned type, unsigned timestamp, pCanvas* p) -> void {
+  if(!p->state().droppable) return;
+  lstring paths = DropPaths(data);
+  if(paths.empty()) return;
+  p->self().doDrop(paths);
+}
+
+static auto Canvas_expose(GtkWidget* widget, GdkEventExpose* event, pCanvas* p) -> signed {
+  p->_onExpose(event);
+  return true;
+}
+
+static auto Canvas_mouseLeave(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
+  p->self().doMouseLeave();
+  return true;
+}
+
+static auto Canvas_mouseMove(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
+  p->self().doMouseMove({(signed)event->x, (signed)event->y});
+  return true;
+}
+
+static auto Canvas_mousePress(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
+  switch(event->button) {
+  case 1: p->self().doMousePress(Mouse::Button::Left); break;
+  case 2: p->self().doMousePress(Mouse::Button::Middle); break;
+  case 3: p->self().doMousePress(Mouse::Button::Right); break;
+  }
+  return true;
+}
+
+static auto Canvas_mouseRelease(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
+  switch(event->button) {
+  case 1: p->self().doMouseRelease(Mouse::Button::Left); break;
+  case 2: p->self().doMouseRelease(Mouse::Button::Middle); break;
+  case 3: p->self().doMouseRelease(Mouse::Button::Right); break;
+  }
+  return true;
+}
+
+auto pCanvas::construct() -> void {
+  gtkWidget = gtk_drawing_area_new();
+  gtk_widget_add_events(gtkWidget,
+    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
+
+  setDroppable(state().droppable);
+  _rasterize();
+  _redraw();
+
+  //todo: need to work around GTK+ library bug:
+  //after calling destruct(), construct() with state.droppable == true;
+  //GTK+ will throw SIGBUS inside g_signal_connect_data() on one of the below connections
+
+  g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Canvas_mousePress), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Canvas_mouseRelease), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Canvas_drop), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "expose-event", G_CALLBACK(Canvas_expose), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Canvas_mouseLeave), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Canvas_mouseMove), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pCanvas::destruct() -> void {
+  _release();
+  if(gtkWidget) gtk_widget_destroy(gtkWidget), gtkWidget = nullptr;
+  gtkParent = nullptr;
+}
+
+auto pCanvas::minimumSize() const -> Size {
+  return {max(0, state().size.width()), max(0, state().size.height())};
+}
+
+auto pCanvas::setColor(Color color) -> void {
+  mode = Mode::Color;
+  update();
+}
+
+auto pCanvas::setData(Size size) -> void {
+  mode = Mode::Data;
+  update();
+}
+
+auto pCanvas::setDroppable(bool droppable) -> void {
+  if(droppable) {
+    gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
+    gtk_drag_dest_add_uri_targets(gtkWidget);
+  }
+}
+
+auto pCanvas::setGeometry(Geometry geometry) -> void {
+  update();
+  pWidget::setGeometry(geometry);
+}
+
+auto pCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void {
+  mode = Mode::Gradient;
+  update();
+}
+
+auto pCanvas::setIcon(const image& icon) -> void {
+  mode = Mode::Icon;
+  update();
+}
+
+auto pCanvas::update() -> void {
+  _rasterize();
+  _redraw();
+}
+
+auto pCanvas::_onExpose(GdkEventExpose* expose) -> void {
+  if(surface == nullptr) return;
+
+  signed sx = 0, sy = 0, dx = 0, dy = 0;
+  signed width = surfaceWidth;
+  signed height = surfaceHeight;
+  auto geometry = pSizable::state().geometry;
+
+  if(width <= geometry.width()) {
+    sx = 0;
+    dx = (geometry.width() - width) / 2;
+  } else {
+    sx = (width - geometry.width()) / 2;
+    dx = 0;
+    width = geometry.width();
+  }
+
+  if(height <= geometry.height()) {
+    sy = 0;
+    dy = (geometry.height() - height) / 2;
+  } else {
+    sy = (height - geometry.height()) / 2;
+    dy = 0;
+    height = geometry.height();
+  }
+
+  gdk_draw_pixbuf(gtk_widget_get_window(gtkWidget), nullptr, surface, sx, sy, dx, dy, width, height, GDK_RGB_DITHER_NONE, 0, 0);
+}
+
+auto pCanvas::_rasterize() -> void {
+  signed width = 0;
+  signed height = 0;
+
+  if(mode == Mode::Color || mode == Mode::Gradient) {
+    width = pSizable::state().geometry.width();
+    height = pSizable::state().geometry.height();
+  } else {
+    width = state().size.width();
+    height = state().size.height();
+  }
+
+  if(width <= 0 || height <= 0) return;
+
+  if(width != surfaceWidth || height != surfaceHeight) _release();
+  surfaceWidth = width;
+  surfaceHeight = height;
+
+  if(!surface) surface = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height);
+  uint32_t* buffer = (uint32_t*)gdk_pixbuf_get_pixels(surface);
+
+  if(mode == Mode::Color) {
+    uint32_t color = state().color.value();
+    for(auto n : range(width * height)) buffer[n] = color;
+  }
+
+  if(mode == Mode::Gradient) {
+    image fill;
+    fill.allocate(width, height);
+    fill.gradient(
+      state().gradient[0].value(), state().gradient[1].value(), state().gradient[2].value(), state().gradient[3].value()
+    );
+    memory::copy(buffer, fill.data, fill.size);
+  }
+
+  if(mode == Mode::Icon) {
+    auto icon = state().icon;
+    icon.scale(width, height);
+    icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
+    memory::copy(buffer, icon.data, icon.size);
+  }
+
+  if(mode == Mode::Data) {
+    memory::copy(buffer, state().data.data(), state().data.size() * sizeof(uint32_t));
+  }
+
+  //ARGB -> ABGR conversion
+  for(auto n : range(width * height)) {
+    uint32_t color = *buffer;
+    color = (color & 0xff00ff00) | ((color & 0xff0000) >> 16) | ((color & 0x0000ff) << 16);
+    *buffer++ = color;
+  }
+}
+
+auto pCanvas::_redraw() -> void {
+  if(gtk_widget_get_realized(gtkWidget)) {
+    gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), nullptr, true);
+  }
+}
+
+auto pCanvas::_release() -> void {
+  if(surface) {
+    g_object_unref(surface);
+    surface = nullptr;
+  }
+  surfaceWidth = 0;
+  surfaceHeight = 0;
+}
+
+}
diff --git a/hiro/gtk/widget/canvas.hpp b/hiro/gtk/widget/canvas.hpp
new file mode 100644
index 00000000..99e9b03d
--- /dev/null
+++ b/hiro/gtk/widget/canvas.hpp
@@ -0,0 +1,28 @@
+namespace hiro {
+
+struct pCanvas : pWidget {
+  Declare(Canvas, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setColor(Color color) -> void;
+  auto setData(Size size) -> void;
+  auto setDroppable(bool droppable) -> void;
+  auto setGeometry(Geometry geometry) -> void override;
+  auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void;
+  auto setIcon(const image& icon) -> void;
+  auto update() -> void;
+
+  enum class Mode : unsigned { Color, Data, Gradient, Icon };
+
+  auto _onExpose(GdkEventExpose* event) -> void;
+  auto _rasterize() -> void;
+  auto _redraw() -> void;
+  auto _release() -> void;
+
+  GdkPixbuf* surface = nullptr;
+  unsigned surfaceWidth = 0;
+  unsigned surfaceHeight = 0;
+  Mode mode = Mode::Color;
+};
+
+}
diff --git a/hiro/gtk/widget/check-button.cpp b/hiro/gtk/widget/check-button.cpp
new file mode 100644
index 00000000..41c7a51f
--- /dev/null
+++ b/hiro/gtk/widget/check-button.cpp
@@ -0,0 +1,73 @@
+namespace hiro {
+
+static auto CheckButton_toggle(GtkToggleButton* toggleButton, pCheckButton* p) -> void {
+  p->state().checked = gtk_toggle_button_get_active(toggleButton);
+  if(!p->locked()) p->self().doToggle();
+}
+
+auto pCheckButton::construct() -> void {
+  gtkWidget = gtk_toggle_button_new();
+
+  setBordered(state().bordered);
+  setChecked(state().checked);
+  setIcon(state().icon);
+  setOrientation(state().orientation);
+  setText(state().text);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckButton_toggle), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pCheckButton::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pCheckButton::minimumSize() const -> Size {
+  Size size = pFont::size(self().font(true), state().text);
+
+  if(state().orientation == Orientation::Horizontal) {
+    size.setWidth(size.width() + state().icon.width);
+    size.setHeight(max(size.height(), state().icon.height));
+  }
+
+  if(state().orientation == Orientation::Vertical) {
+    size.setWidth(max(size.width(), state().icon.width));
+    size.setHeight(size.height() + state().icon.height);
+  }
+
+  return {size.width() + 24, size.height() + 12};
+}
+
+auto pCheckButton::setBordered(bool bordered) -> void {
+  gtk_button_set_relief(GTK_BUTTON(gtkWidget), bordered ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE);
+}
+
+auto pCheckButton::setChecked(bool checked) -> void {
+  lock();
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), checked);
+  unlock();
+}
+
+auto pCheckButton::setIcon(const image& icon) -> void {
+  if(icon) {
+    GtkImage* gtkImage = CreateImage(icon);
+    gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage);
+  } else {
+    gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr);
+  }
+}
+
+auto pCheckButton::setOrientation(Orientation orientation) -> void {
+  switch(orientation) {
+  case Orientation::Horizontal: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_LEFT); break;
+  case Orientation::Vertical:   gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_TOP);  break;
+  }
+}
+
+auto pCheckButton::setText(const string& text) -> void {
+  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
+  setFont(self().font(true));  //gtk_button_set_label() recreates label, which destroys currently assigned font
+}
+
+}
diff --git a/hiro/gtk/widget/check-button.hpp b/hiro/gtk/widget/check-button.hpp
new file mode 100644
index 00000000..3b31d917
--- /dev/null
+++ b/hiro/gtk/widget/check-button.hpp
@@ -0,0 +1,14 @@
+namespace hiro {
+
+struct pCheckButton : pWidget {
+  Declare(CheckButton, Widget)
+
+  auto minimumSize() const -> Size override;
+  auto setBordered(bool bordered) -> void;
+  auto setChecked(bool checked) -> void;
+  auto setIcon(const image& icon) -> void;
+  auto setOrientation(Orientation orientation) -> void;
+  auto setText(const string& text) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/check-label.cpp b/hiro/gtk/widget/check-label.cpp
new file mode 100644
index 00000000..c9dbd0a7
--- /dev/null
+++ b/hiro/gtk/widget/check-label.cpp
@@ -0,0 +1,39 @@
+namespace hiro {
+
+static auto CheckLabel_toggle(GtkToggleButton* toggleButton, pCheckLabel* p) -> void {
+  p->state().checked = gtk_toggle_button_get_active(toggleButton);
+  if(!p->locked()) p->self().doToggle();
+}
+
+auto pCheckLabel::construct() -> void {
+  gtkWidget = gtk_check_button_new_with_label("");
+
+  setChecked(state().checked);
+  setText(state().text);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckLabel_toggle), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pCheckLabel::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pCheckLabel::minimumSize() const -> Size {
+  Size size = pFont::size(self().font(true), state().text);
+  return {size.width() + 28, size.height() + 4};
+}
+
+auto pCheckLabel::setChecked(bool checked) -> void {
+  lock();
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), checked);
+  unlock();
+}
+
+auto pCheckLabel::setText(const string& text) -> void {
+  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
+  setFont(self().font(true));  //gtk_button_set_label() recreates label, which destroys currently assigned font
+}
+
+}
diff --git a/hiro/gtk/widget/check-label.hpp b/hiro/gtk/widget/check-label.hpp
new file mode 100644
index 00000000..c84195f1
--- /dev/null
+++ b/hiro/gtk/widget/check-label.hpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+struct pCheckLabel : pWidget {
+  Declare(CheckLabel, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setChecked(bool checked) -> void;
+  auto setText(const string& text) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/combo-button-item.cpp b/hiro/gtk/widget/combo-button-item.cpp
new file mode 100644
index 00000000..2f6e79bc
--- /dev/null
+++ b/hiro/gtk/widget/combo-button-item.cpp
@@ -0,0 +1,42 @@
+namespace hiro {
+
+auto pComboButtonItem::construct() -> void {
+}
+
+auto pComboButtonItem::destruct() -> void {
+}
+
+auto pComboButtonItem::setIcon(const image& icon) -> void {
+  if(auto parent = _parent()) {
+    if(icon) {
+      auto copy = icon;
+      auto size = pFont::size(self().font(true), " ").height();
+      copy.scale(size, size);
+      auto pixbuf = CreatePixbuf(copy);
+      gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, pixbuf, -1);
+    } else {
+      gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, nullptr, -1);
+    }
+  }
+}
+
+auto pComboButtonItem::setSelected() -> void {
+  if(auto parent = _parent()) {
+    parent->lock();
+    gtk_combo_box_set_active(parent->gtkComboBox, self().offset());
+    parent->unlock();
+  }
+}
+
+auto pComboButtonItem::setText(const string& text) -> void {
+  if(auto parent = _parent()) {
+    gtk_list_store_set(parent->gtkListStore, &gtkIter, 1, text.data(), -1);
+  }
+}
+
+auto pComboButtonItem::_parent() -> pComboButton* {
+  if(auto parent = self().parentComboButton()) return parent->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/widget/combo-button-item.hpp b/hiro/gtk/widget/combo-button-item.hpp
new file mode 100644
index 00000000..d823b746
--- /dev/null
+++ b/hiro/gtk/widget/combo-button-item.hpp
@@ -0,0 +1,15 @@
+namespace hiro {
+
+struct pComboButtonItem : pObject {
+  Declare(ComboButtonItem, Object)
+
+  auto setIcon(const image& icon) -> void;
+  auto setSelected() -> void;
+  auto setText(const string& text) -> void;
+
+  auto _parent() -> pComboButton*;
+
+  GtkTreeIter gtkIter;
+};
+
+}
diff --git a/hiro/gtk/widget/combo-button.cpp b/hiro/gtk/widget/combo-button.cpp
new file mode 100644
index 00000000..9b2ea147
--- /dev/null
+++ b/hiro/gtk/widget/combo-button.cpp
@@ -0,0 +1,91 @@
+namespace hiro {
+
+static auto ComboButton_change(GtkComboBox* comboBox, pComboButton* p) -> void { p->_updateSelected(); }
+
+auto pComboButton::construct() -> void {
+  gtkListStore = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
+  gtkTreeModel = GTK_TREE_MODEL(gtkListStore);
+  gtkWidget = gtk_combo_box_new_with_model(gtkTreeModel);
+  gtkComboBox = GTK_COMBO_BOX(gtkWidget);
+
+  gtkCellIcon = gtk_cell_renderer_pixbuf_new();
+  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkWidget), gtkCellIcon, false);
+  gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkWidget), gtkCellIcon, "pixbuf", 0, nullptr);
+  gtkCellText = gtk_cell_renderer_text_new();
+  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkWidget), gtkCellText, true);
+  gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkWidget), gtkCellText, "text", 1, nullptr);
+
+  for(auto& item : state().items) {
+    append(item);
+    if(item->selected()) item->setSelected();
+  }
+
+  g_signal_connect(G_OBJECT(gtkWidget), "changed", G_CALLBACK(ComboButton_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pComboButton::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pComboButton::append(sComboButtonItem item) -> void {
+  lock();
+  if(auto delegate = item->self()) {
+    gtk_list_store_append(gtkListStore, &delegate->gtkIter);
+    item->setIcon(item->state.icon);
+    item->setText(item->state.text);
+  }
+  unlock();
+}
+
+auto pComboButton::minimumSize() const -> Size {
+  string font = self().font(true);
+  signed maximumWidth = 0;
+  for(auto& item : state().items) {
+    maximumWidth = max(maximumWidth,
+      (item->state.icon ? 2 + pFont::size(font, " ").height() : 0)
+    + pFont::size(font, item->state.text).width()
+    );
+  }
+  Size size = pFont::size(font, " ");
+  return {maximumWidth + 40, size.height() + 12};
+}
+
+auto pComboButton::remove(sComboButtonItem item) -> void {
+  lock();
+  if(auto delegate = item->self()) {
+    gtk_list_store_remove(gtkListStore, &delegate->gtkIter);
+    //if the currently selected item is removed; GTK+ deselects everything
+    //detect this behavior and select the first item instead of nothing
+    if(gtk_combo_box_get_active(GTK_COMBO_BOX(gtkWidget)) < 0) {
+      if(gtk_tree_model_iter_n_children(gtkTreeModel, nullptr) > 0) {
+        gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), 0);
+        state().selected = 0;
+      }
+    }
+  }
+  unlock();
+}
+
+auto pComboButton::reset() -> void {
+  lock();
+  gtk_list_store_clear(gtkListStore);
+  unlock();
+}
+
+auto pComboButton::setFont(const string& font) -> void {
+  pWidget::setFont(font);
+  auto fontDescription = pFont::create(font);
+  g_object_set(G_OBJECT(gtkCellText), "font-desc", fontDescription, nullptr);
+}
+
+auto pComboButton::_updateSelected() -> void {
+  signed selected = gtk_combo_box_get_active(gtkComboBox);
+  if(selected >= 0) {
+    state().selected = selected;
+    if(!locked()) self().doChange();
+  }
+}
+
+}
diff --git a/hiro/gtk/widget/combo-button.hpp b/hiro/gtk/widget/combo-button.hpp
new file mode 100644
index 00000000..ff66df28
--- /dev/null
+++ b/hiro/gtk/widget/combo-button.hpp
@@ -0,0 +1,21 @@
+namespace hiro {
+
+struct pComboButton : pWidget {
+  Declare(ComboButton, Widget)
+
+  auto append(sComboButtonItem item) -> void;
+  auto minimumSize() const -> Size override;
+  auto remove(sComboButtonItem item) -> void;
+  auto reset() -> void;
+  auto setFont(const string& font) -> void override;
+
+  auto _updateSelected() -> void;
+
+  GtkListStore* gtkListStore = nullptr;
+  GtkTreeModel* gtkTreeModel = nullptr;
+  GtkComboBox* gtkComboBox = nullptr;
+  GtkCellRenderer* gtkCellIcon = nullptr;
+  GtkCellRenderer* gtkCellText = nullptr;
+};
+
+}
diff --git a/phoenix/gtk/widget/console.cpp b/hiro/gtk/widget/console.cpp
similarity index 76%
rename from phoenix/gtk/widget/console.cpp
rename to hiro/gtk/widget/console.cpp
index 722f1387..a72cd629 100644
--- a/phoenix/gtk/widget/console.cpp
+++ b/hiro/gtk/widget/console.cpp
@@ -1,34 +1,60 @@
-namespace phoenix {
+namespace hiro {
 
-static bool Console_keyPress(GtkWidget* widget, GdkEventKey* event, Console* self) {
-  return self->p.keyPress(event->keyval, event->state);
+static auto Console_keyPress(GtkWidget*, GdkEventKey* event, pConsole* p) -> signed {
+  return p->_keyPress(event->keyval, event->state);
 }
 
-void pConsole::print(string text) {
+auto pConsole::construct() -> void {
+  gtkWidget = gtk_scrolled_window_new(0, 0);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
+
+  subWidget = gtk_text_view_new();
+  gtk_widget_show(subWidget);
+  gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), false);
+  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_NONE);
+  gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
+
+  textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
+
+  setBackgroundColor(state().backgroundColor);
+  setForegroundColor(state().foregroundColor);
+
+  g_signal_connect(G_OBJECT(subWidget), "key-press-event", G_CALLBACK(Console_keyPress), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pConsole::destruct() -> void {
+  gtk_widget_destroy(subWidget);
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pConsole::print(const string& text) -> void {
   //insert text before prompt and command
   GtkTextIter iter;
   gtk_text_buffer_get_iter_at_line_offset(textBuffer, &iter, gtk_text_buffer_get_line_count(textBuffer), 0);
   gtk_text_buffer_insert(textBuffer, &iter, text, -1);
-  seekToEnd();
+  _seekToEnd();
 }
 
-void pConsole::reset() {
+auto pConsole::reset() -> void {
   //flush history and redraw prompt
-  gtk_text_buffer_set_text(textBuffer, console.prompt(), -1);
-  seekToEnd();
+  gtk_text_buffer_set_text(textBuffer, state().prompt, -1);
+  _seekToEnd();
 }
 
-void pConsole::setBackgroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, &gdkColor);
+auto pConsole::setBackgroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
 }
 
-void pConsole::setForegroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, &gdkColor);
+auto pConsole::setForegroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
 }
 
-void pConsole::setPrompt(string prompt) {
+auto pConsole::setPrompt(const string& prompt) -> void {
   //erase previous prompt and replace it with new prompt
   GtkTextIter lhs, rhs;
   gtk_text_buffer_get_iter_at_line_offset(textBuffer, &lhs, gtk_text_buffer_get_line_count(textBuffer), 0);
@@ -36,42 +62,15 @@ void pConsole::setPrompt(string prompt) {
   gtk_text_buffer_delete(textBuffer, &lhs, &rhs);
   gtk_text_buffer_get_iter_at_line_offset(textBuffer, &lhs, gtk_text_buffer_get_line_count(textBuffer), 0);
   gtk_text_buffer_insert(textBuffer, &lhs, previousPrompt = prompt, -1);
-  seekToEnd();
+  _seekToEnd();
 }
 
-void pConsole::constructor() {
-  gtkWidget = gtk_scrolled_window_new(0, 0);
-  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
-
-  subWidget = gtk_text_view_new();
-  gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), false);
-  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_NONE);
-  gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
-
-  textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
-
-  g_signal_connect(G_OBJECT(subWidget), "key-press-event", G_CALLBACK(Console_keyPress), (gpointer)&console);
-
-  gtk_widget_show(subWidget);
-}
-
-void pConsole::destructor() {
-  gtk_widget_destroy(subWidget);
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pConsole::orphan() {
-  destructor();
-  constructor();
-}
-
-bool pConsole::keyPress(unsigned scancode, unsigned mask) {
+auto pConsole::_keyPress(unsigned scancode, unsigned mask) -> bool {
   if(mask & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK)) return false;  //allow actions such as Ctrl+C (copy)
 
   GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
   GtkTextIter start, cursor, end;
-  gtk_text_buffer_get_iter_at_line_offset(textBuffer, &start, gtk_text_buffer_get_line_count(textBuffer), console.prompt().size());
+  gtk_text_buffer_get_iter_at_line_offset(textBuffer, &start, gtk_text_buffer_get_line_count(textBuffer), state().prompt.size());
   gtk_text_buffer_get_iter_at_mark(textBuffer, &cursor, mark);
   gtk_text_buffer_get_end_iter(textBuffer, &end);
 
@@ -79,12 +78,12 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
     char* temp = gtk_text_buffer_get_text(textBuffer, &start, &end, true);
     string s = temp;
     g_free(temp);
-    gtk_text_buffer_insert(textBuffer, &end, string{"\n", console.prompt()}, -1);
-    if(console.onActivate) console.onActivate(s);
+    gtk_text_buffer_insert(textBuffer, &end, string{"\n", state().prompt}, -1);
+    self().doActivate(s);
     if(s) history.prepend(s);
     if(history.size() > 128) history.removeLast();
     historyOffset = 0;
-    seekToEnd();
+    _seekToEnd();
     return true;
   }
 
@@ -94,7 +93,7 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
     if(historyOffset < history.size()) {
       gtk_text_buffer_insert(textBuffer, &end, history[historyOffset++], -1);
     }
-    seekToEnd();
+    _seekToEnd();
     return true;
   }
 
@@ -104,7 +103,7 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
     if(historyOffset > 0) {
       gtk_text_buffer_insert(textBuffer, &end, history[--historyOffset], -1);
     }
-    seekToEnd();
+    _seekToEnd();
     return true;
   }
 
@@ -115,7 +114,7 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
       gtk_text_iter_set_offset(&cursor, gtk_text_iter_get_offset(&cursor) - 1);
       gtk_text_buffer_place_cursor(textBuffer, &cursor);
     }
-    seekToMark();
+    _seekToMark();
     return true;
   }
 
@@ -126,19 +125,19 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
       gtk_text_iter_set_offset(&cursor, gtk_text_iter_get_offset(&cursor) + 1);
       gtk_text_buffer_place_cursor(textBuffer, &cursor);
     }
-    seekToMark();
+    _seekToMark();
     return true;
   }
 
   if(scancode == GDK_KEY_Home) {
     gtk_text_buffer_place_cursor(textBuffer, &start);
-    seekToMark();
+    _seekToMark();
     return true;
   }
 
   if(scancode == GDK_KEY_End) {
     gtk_text_buffer_place_cursor(textBuffer, &end);
-    seekToMark();
+    _seekToMark();
     return true;
   }
 
@@ -147,7 +146,7 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
     GtkTextIter lhs = cursor;
     gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&cursor) - 1);
     gtk_text_buffer_delete(textBuffer, &lhs, &cursor);
-    seekToMark();
+    _seekToMark();
     return true;
   }
 
@@ -157,28 +156,28 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
     GtkTextIter rhs = cursor;
     gtk_text_iter_set_offset(&rhs, gtk_text_iter_get_offset(&cursor) + 1);
     gtk_text_buffer_delete(textBuffer, &cursor, &rhs);
-    seekToMark();
+    _seekToMark();
     return true;
   }
 
   if(scancode >= 0x20 && scancode <= 0x7e) {
     if(gtk_text_iter_get_offset(&cursor) < gtk_text_iter_get_offset(&start)) return true;
     gtk_text_buffer_insert(textBuffer, &cursor, string{(char)scancode}, -1);
-    seekToMark();
+    _seekToMark();
     return true;
   }
 
   return false;
 }
 
-void pConsole::seekToEnd() {
+auto pConsole::_seekToEnd() -> void {
   GtkTextIter iter;
   gtk_text_buffer_get_end_iter(textBuffer, &iter);
   gtk_text_buffer_place_cursor(textBuffer, &iter);
-  seekToMark();
+  _seekToMark();
 }
 
-void pConsole::seekToMark() {
+auto pConsole::_seekToMark() -> void {
   GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
   gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark);
 }
diff --git a/hiro/gtk/widget/console.hpp b/hiro/gtk/widget/console.hpp
new file mode 100644
index 00000000..0d32a476
--- /dev/null
+++ b/hiro/gtk/widget/console.hpp
@@ -0,0 +1,23 @@
+namespace hiro {
+
+struct pConsole : pWidget {
+  Declare(Console, Widget)
+
+  auto print(const string& text) -> void;
+  auto reset() -> void;
+  auto setBackgroundColor(Color color) -> void;
+  auto setForegroundColor(Color color) -> void;
+  auto setPrompt(const string& prompt) -> void;
+
+  auto _keyPress(unsigned scancode, unsigned mask) -> bool;
+  auto _seekToEnd() -> void;
+  auto _seekToMark() -> void;
+
+  GtkWidget* subWidget = nullptr;
+  GtkTextBuffer* textBuffer = nullptr;
+  string previousPrompt;
+  lstring history;
+  unsigned historyOffset = 0;
+};
+
+}
diff --git a/hiro/gtk/widget/frame.cpp b/hiro/gtk/widget/frame.cpp
new file mode 100644
index 00000000..30a1b180
--- /dev/null
+++ b/hiro/gtk/widget/frame.cpp
@@ -0,0 +1,66 @@
+namespace hiro {
+
+auto pFrame::construct() -> void {
+  gtkWidget = gtk_frame_new(nullptr);
+  gtkLabel = gtk_label_new("");
+  gtk_frame_set_label_widget(GTK_FRAME(gtkWidget), gtkLabel);
+  gtk_widget_show(gtkLabel);
+
+  setText(state().text);
+
+  pWidget::construct();
+}
+
+auto pFrame::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pFrame::append(shared_pointer<mLayout> layout) -> void {
+  if(auto layout = _layout()) layout->setFont(layout->self().font(true));
+}
+
+auto pFrame::container(mWidget& widget) -> GtkWidget* {
+  return gtk_widget_get_parent(gtkWidget);
+}
+
+auto pFrame::remove(shared_pointer<mLayout> layout) -> void {
+}
+
+auto pFrame::setEnabled(bool enabled) -> void {
+  if(auto layout = _layout()) layout->setEnabled(layout->self().enabled(true));
+  pWidget::setEnabled(enabled);
+}
+
+auto pFrame::setFont(const string& font) -> void {
+  if(auto layout = _layout()) layout->setFont(layout->self().font(true));
+  pFont::setFont(gtkLabel, font);
+}
+
+auto pFrame::setGeometry(Geometry geometry) -> void {
+  pWidget::setGeometry(geometry);
+  if(auto layout = state().layout) {
+    Size size = pFont::size(self().font(true), state().text);
+    if(!state().text) size.setHeight(10);
+    geometry.setX(geometry.x() + 2);
+    geometry.setY(geometry.y() + (size.height() - 1));
+    geometry.setWidth(geometry.width() - 5);
+    geometry.setHeight(geometry.height() - (size.height() + 2));
+    layout->setGeometry(geometry);
+  }
+}
+
+auto pFrame::setText(const string& text) -> void {
+  gtk_label_set_text(GTK_LABEL(gtkLabel), text);
+}
+
+auto pFrame::setVisible(bool visible) -> void {
+  if(auto layout = _layout()) layout->setVisible(layout->self().visible(true));
+  pWidget::setVisible(visible);
+}
+
+auto pFrame::_layout() -> pLayout* {
+  if(auto layout = state().layout) return layout->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/widget/frame.hpp b/hiro/gtk/widget/frame.hpp
new file mode 100644
index 00000000..5d09750e
--- /dev/null
+++ b/hiro/gtk/widget/frame.hpp
@@ -0,0 +1,20 @@
+namespace hiro {
+
+struct pFrame : pWidget {
+  Declare(Frame, Widget)
+
+  auto append(shared_pointer<mLayout> layout) -> void;
+  auto container(mWidget& widget) -> GtkWidget* override;
+  auto remove(shared_pointer<mLayout> layout) -> void;
+  auto setEnabled(bool enabled) -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setGeometry(Geometry geometry) -> void override;
+  auto setText(const string& text) -> void;
+  auto setVisible(bool visible) -> void override;
+
+  auto _layout() -> pLayout*;
+
+  GtkWidget* gtkLabel = nullptr;
+};
+
+}
diff --git a/phoenix/gtk/widget/hex-edit.cpp b/hiro/gtk/widget/hex-edit.cpp
similarity index 57%
rename from phoenix/gtk/widget/hex-edit.cpp
rename to hiro/gtk/widget/hex-edit.cpp
index 08c10a78..03805517 100644
--- a/phoenix/gtk/widget/hex-edit.cpp
+++ b/hiro/gtk/widget/hex-edit.cpp
@@ -1,103 +1,29 @@
-namespace phoenix {
+namespace hiro {
 
-static bool HexEdit_keyPress(GtkWidget* widget, GdkEventKey* event, HexEdit* self) {
-  return self->p.keyPress(event->keyval, event->state);
+static auto HexEdit_keyPress(GtkWidget* widget, GdkEventKey* event, pHexEdit* p) -> signed {
+  return p->keyPress(event->keyval, event->state);
 }
 
-static bool HexEdit_mouseScroll(GtkWidget* widget, GdkEventScroll* event, HexEdit* self) {
-  double position = gtk_range_get_value(GTK_RANGE(self->p.scrollBar));
+static auto HexEdit_mouseScroll(GtkWidget* widget, GdkEventScroll* event, pHexEdit* p) -> signed {
+  double position = gtk_range_get_value(GTK_RANGE(p->scrollBar));
 
   if(event->direction == GDK_SCROLL_UP) {
-    self->p.scroll(position - 1);
+    p->scroll(position - 1);
   }
 
   if(event->direction == GDK_SCROLL_DOWN) {
-    self->p.scroll(position + 1);
+    p->scroll(position + 1);
   }
 
   return true;  //do not propagate event further
 }
 
-static bool HexEdit_scroll(GtkRange* range, GtkScrollType scroll, double value, HexEdit* self) {
-  self->p.scroll((signed)value);
+static auto HexEdit_scroll(GtkRange* range, GtkScrollType scroll, double value, pHexEdit* p) -> signed {
+  p->scroll((signed)value);
   return true;  //do not propagate event further
 }
 
-bool pHexEdit::focused() {
-  return GTK_WIDGET_HAS_FOCUS(subWidget) || GTK_WIDGET_HAS_FOCUS(scrollBar);
-}
-
-void pHexEdit::setBackgroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pHexEdit::setColumns(unsigned columns) {
-  setScroll();
-  update();
-}
-
-void pHexEdit::setForegroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pHexEdit::setLength(unsigned length) {
-  setScroll();
-  update();
-}
-
-void pHexEdit::setOffset(unsigned offset) {
-  setScroll();
-  updateScroll();
-  update();
-}
-
-void pHexEdit::setRows(unsigned rows) {
-  setScroll();
-  update();
-}
-
-void pHexEdit::update() {
-  if(!hexEdit.onRead) {
-    gtk_text_buffer_set_text(textBuffer, "", -1);
-    return;
-  }
-
-  unsigned position = cursorPosition();
-
-  string output;
-  unsigned offset = hexEdit.state.offset;
-  for(unsigned row = 0; row < hexEdit.state.rows; row++) {
-    output.append(hex<8>(offset));
-    output.append("  ");
-
-    string hexdata;
-    string ansidata = " ";
-    for(unsigned column = 0; column < hexEdit.state.columns; column++) {
-      if(offset < hexEdit.state.length) {
-        uint8_t data = hexEdit.onRead(offset++);
-        hexdata.append(hex<2>(data));
-        hexdata.append(" ");
-        ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
-      } else {
-        hexdata.append("   ");
-        ansidata.append(" ");
-      }
-    }
-
-    output.append(hexdata);
-    output.append(ansidata);
-    if(offset >= hexEdit.state.length) break;
-    if(row != hexEdit.state.rows - 1) output.append("\n");
-  }
-
-  gtk_text_buffer_set_text(textBuffer, output, -1);
-  if(position == 0) position = 10;  //start at first position where hex values can be entered
-  setCursorPosition(position);
-}
-
-void pHexEdit::constructor() {
+auto pHexEdit::construct() -> void {
   gtkWidget = gtk_hbox_new(false, 0);
 
   container = gtk_scrolled_window_new(0, 0);
@@ -108,15 +34,11 @@ void pHexEdit::constructor() {
   gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), false);
   gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_NONE);
   gtk_container_add(GTK_CONTAINER(container), subWidget);
-  g_signal_connect(G_OBJECT(subWidget), "key-press-event", G_CALLBACK(HexEdit_keyPress), (gpointer)&hexEdit);
-  g_signal_connect(G_OBJECT(subWidget), "scroll-event", G_CALLBACK(HexEdit_mouseScroll), (gpointer)&hexEdit);
 
   scrollBar = gtk_vscrollbar_new((GtkAdjustment*)nullptr);
   gtk_range_set_range(GTK_RANGE(scrollBar), 0, 255);
   gtk_range_set_increments(GTK_RANGE(scrollBar), 1, 16);
   gtk_widget_set_sensitive(scrollBar, false);
-  g_signal_connect(G_OBJECT(scrollBar), "change-value", G_CALLBACK(HexEdit_scroll), (gpointer)&hexEdit);
-  g_signal_connect(G_OBJECT(scrollBar), "scroll-event", G_CALLBACK(HexEdit_mouseScroll), (gpointer)&hexEdit);
 
   gtk_box_pack_start(GTK_BOX(gtkWidget), container, true, true, 0);
   gtk_box_pack_start(GTK_BOX(gtkWidget), scrollBar, false, false, 1);
@@ -128,38 +50,117 @@ void pHexEdit::constructor() {
   gtk_widget_show(subWidget);
   gtk_widget_show(container);
 
-  setColumns(hexEdit.state.columns);
-  setRows(hexEdit.state.rows);
-  setLength(hexEdit.state.length);
-  setOffset(hexEdit.state.offset);
+  setBackgroundColor(state().backgroundColor);
+  setColumns(state().columns);
+  setForegroundColor(state().foregroundColor);
+  setRows(state().rows);
+  setLength(state().length);
+  setOffset(state().offset);
   update();
+
+  g_signal_connect(G_OBJECT(subWidget), "key-press-event", G_CALLBACK(HexEdit_keyPress), (gpointer)this);
+  g_signal_connect(G_OBJECT(subWidget), "scroll-event", G_CALLBACK(HexEdit_mouseScroll), (gpointer)this);
+
+  g_signal_connect(G_OBJECT(scrollBar), "change-value", G_CALLBACK(HexEdit_scroll), (gpointer)this);
+  g_signal_connect(G_OBJECT(scrollBar), "scroll-event", G_CALLBACK(HexEdit_mouseScroll), (gpointer)this);
+
+  pWidget::construct();
 }
 
-void pHexEdit::destructor() {
+auto pHexEdit::destruct() -> void {
   gtk_widget_destroy(scrollBar);
   gtk_widget_destroy(subWidget);
   gtk_widget_destroy(container);
   gtk_widget_destroy(gtkWidget);
 }
 
-void pHexEdit::orphan() {
-  destructor();
-  constructor();
+auto pHexEdit::focused() const -> bool {
+  return GTK_WIDGET_HAS_FOCUS(subWidget) || GTK_WIDGET_HAS_FOCUS(scrollBar);
 }
 
-unsigned pHexEdit::cursorPosition() {
+auto pHexEdit::setBackgroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pHexEdit::setColumns(unsigned columns) -> void {
+  setScroll();
+  update();
+}
+
+auto pHexEdit::setForegroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pHexEdit::setLength(unsigned length) -> void {
+  setScroll();
+  update();
+}
+
+auto pHexEdit::setOffset(unsigned offset) -> void {
+  setScroll();
+  updateScroll();
+  update();
+}
+
+auto pHexEdit::setRows(unsigned rows) -> void {
+  setScroll();
+  update();
+}
+
+auto pHexEdit::update() -> void {
+  if(!state().onRead) {
+    gtk_text_buffer_set_text(textBuffer, "", -1);
+    return;
+  }
+
+  unsigned position = cursorPosition();
+
+  string output;
+  unsigned offset = state().offset;
+  for(auto row : range(state().rows)) {
+    output.append(hex<8>(offset));
+    output.append("  ");
+
+    string hexdata;
+    string ansidata = " ";
+    for(auto column : range(state().columns)) {
+      if(offset < state().length) {
+        uint8_t data = self().doRead(offset++);
+        hexdata.append(hex<2>(data));
+        hexdata.append(" ");
+        ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
+      } else {
+        hexdata.append("   ");
+        ansidata.append(" ");
+      }
+    }
+
+    output.append(hexdata);
+    output.append(ansidata);
+    if(offset >= state().length) break;
+    if(row != state().rows - 1) output.append("\n");
+  }
+
+  gtk_text_buffer_set_text(textBuffer, output, -1);
+  if(position == 0) position = 10;  //start at first position where hex values can be entered
+  setCursorPosition(position);
+}
+
+auto pHexEdit::cursorPosition() -> unsigned {
   GtkTextIter iter;
   gtk_text_buffer_get_iter_at_mark(textBuffer, &iter, textCursor);
   return gtk_text_iter_get_offset(&iter);
 }
 
-bool pHexEdit::keyPress(unsigned scancode, unsigned mask) {
-  if(!hexEdit.onRead) return false;
+auto pHexEdit::keyPress(unsigned scancode, unsigned mask) -> bool {
+  if(!state().onRead) return false;
 
   if(mask & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK)) return false;  //allow actions such as Ctrl+C (copy)
 
   signed position = cursorPosition();
-  signed lineWidth = 10 + (hexEdit.state.columns * 3) + 1 + hexEdit.state.columns + 1;
+  signed lineWidth = 10 + (state().columns * 3) + 1 + state().columns + 1;
   signed cursorY = position / lineWidth;
   signed cursorX = position % lineWidth;
 
@@ -169,16 +170,16 @@ bool pHexEdit::keyPress(unsigned scancode, unsigned mask) {
   }
 
   if(scancode == GDK_End) {
-    setCursorPosition(cursorY * lineWidth + 10 + (hexEdit.state.columns * 3 - 1));
+    setCursorPosition(cursorY * lineWidth + 10 + (state().columns * 3 - 1));
     return true;
   }
 
   if(scancode == GDK_Up) {
     if(cursorY != 0) return false;
 
-    signed newOffset = hexEdit.state.offset - hexEdit.state.columns;
+    signed newOffset = state().offset - state().columns;
     if(newOffset >= 0) {
-      hexEdit.setOffset(newOffset);
+      self().setOffset(newOffset);
       update();
     }
     return true;
@@ -186,36 +187,36 @@ bool pHexEdit::keyPress(unsigned scancode, unsigned mask) {
 
   if(scancode == GDK_Down) {
     if(cursorY >= rows() - 1) return true;
-    if(cursorY != hexEdit.state.rows - 1) return false;
+    if(cursorY != state().rows - 1) return false;
 
-    signed newOffset = hexEdit.state.offset + hexEdit.state.columns;
-    if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) {
-      hexEdit.setOffset(newOffset);
+    signed newOffset = state().offset + state().columns;
+    if(newOffset + state().columns * state().rows - (state().columns - 1) <= state().length) {
+      self().setOffset(newOffset);
       update();
     }
     return true;
   }
 
   if(scancode == GDK_Page_Up) {
-    signed newOffset = hexEdit.state.offset - hexEdit.state.columns * hexEdit.state.rows;
+    signed newOffset = state().offset - state().columns * state().rows;
     if(newOffset >= 0) {
-      hexEdit.setOffset(newOffset);
+      self().setOffset(newOffset);
     } else {
-      hexEdit.setOffset(0);
+      self().setOffset(0);
     }
     update();
     return true;
   }
 
   if(scancode == GDK_Page_Down) {
-    signed newOffset = hexEdit.state.offset + hexEdit.state.columns * hexEdit.state.rows;
-    for(unsigned n = 0; n < hexEdit.state.rows; n++) {
-      if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) {
-        hexEdit.setOffset(newOffset);
+    signed newOffset = state().offset + state().columns * state().rows;
+    for(auto n : range(state().rows)) {
+      if(newOffset + state().columns * state().rows - (state().columns - 1) <= state().length) {
+        self().setOffset(newOffset);
         update();
         break;
       }
-      newOffset -= hexEdit.state.columns;
+      newOffset -= state().columns;
     }
     return true;
   }
@@ -233,12 +234,12 @@ bool pHexEdit::keyPress(unsigned scancode, unsigned mask) {
       //not on a space
       bool cursorNibble = (cursorX % 3) == 1;  //0 = high, 1 = low
       cursorX /= 3;
-      if(cursorX < hexEdit.state.columns) {
+      if(cursorX < state().columns) {
         //not in ANSI region
-        unsigned offset = hexEdit.state.offset + (cursorY * hexEdit.state.columns + cursorX);
+        unsigned offset = state().offset + (cursorY * state().columns + cursorX);
 
-        if(offset >= hexEdit.state.length) return false;  //do not edit past end of data
-        uint8_t data = hexEdit.onRead(offset);
+        if(offset >= state().length) return false;  //do not edit past end of data
+        uint8_t data = self().doRead(offset);
 
         //write modified value
         if(cursorNibble == 1) {
@@ -246,11 +247,11 @@ bool pHexEdit::keyPress(unsigned scancode, unsigned mask) {
         } else {
           data = (data & 0x0f) | (scancode << 4);
         }
-        if(hexEdit.onWrite) hexEdit.onWrite(offset, data);
+        self().doWrite(offset, data);
 
         //auto-advance cursor to next nibble/byte
         position++;
-        if(cursorNibble && cursorX != hexEdit.state.columns - 1) position++;
+        if(cursorNibble && cursorX != state().columns - 1) position++;
         setCursorPosition(position);
 
         //refresh output to reflect modified data
@@ -263,22 +264,22 @@ bool pHexEdit::keyPress(unsigned scancode, unsigned mask) {
 }
 
 //number of actual rows
-signed pHexEdit::rows() {
-  return (max(1u, hexEdit.state.length) + hexEdit.state.columns - 1) / hexEdit.state.columns;
+auto pHexEdit::rows() -> signed {
+  return (max(1u, state().length) + state().columns - 1) / state().columns;
 }
 
 //number of scrollable row positions
-signed pHexEdit::rowsScrollable() {
-  return max(0u, rows() - hexEdit.state.rows);
+auto pHexEdit::rowsScrollable() -> signed {
+  return max(0u, rows() - state().rows);
 }
 
-void pHexEdit::scroll(signed position) {
+auto pHexEdit::scroll(signed position) -> void {
   if(position > rowsScrollable()) position = rowsScrollable();
   if(position < 0) position = 0;
-  hexEdit.setOffset(position * hexEdit.state.columns);
+  self().setOffset(position * state().columns);
 }
 
-void pHexEdit::setCursorPosition(unsigned position) {
+auto pHexEdit::setCursorPosition(unsigned position) -> void {
   GtkTextIter iter;
   gtk_text_buffer_get_iter_at_mark(textBuffer, &iter, textCursor);
 
@@ -291,7 +292,7 @@ void pHexEdit::setCursorPosition(unsigned position) {
   gtk_text_buffer_place_cursor(textBuffer, &iter);
 }
 
-void pHexEdit::setScroll() {
+auto pHexEdit::setScroll() -> void {
   if(rowsScrollable() > 0) {
     gtk_range_set_range(GTK_RANGE(scrollBar), 0, rowsScrollable());
     gtk_widget_set_sensitive(scrollBar, true);
@@ -300,8 +301,8 @@ void pHexEdit::setScroll() {
   }
 }
 
-void pHexEdit::updateScroll() {
-  unsigned row = hexEdit.state.offset / hexEdit.state.columns;
+auto pHexEdit::updateScroll() -> void {
+  unsigned row = state().offset / state().columns;
   gtk_range_set_value(GTK_RANGE(scrollBar), row);
 }
 
diff --git a/hiro/gtk/widget/hex-edit.hpp b/hiro/gtk/widget/hex-edit.hpp
new file mode 100644
index 00000000..685d7d28
--- /dev/null
+++ b/hiro/gtk/widget/hex-edit.hpp
@@ -0,0 +1,31 @@
+namespace hiro {
+
+struct pHexEdit : pWidget {
+  Declare(HexEdit, Widget)
+
+  auto focused() const -> bool override;
+  auto setBackgroundColor(Color color) -> void;
+  auto setColumns(unsigned columns) -> void;
+  auto setForegroundColor(Color color) -> void;
+  auto setLength(unsigned length) -> void;
+  auto setOffset(unsigned offset) -> void;
+  auto setRows(unsigned rows) -> void;
+  auto update() -> void;
+
+  auto cursorPosition() -> unsigned;
+  auto keyPress(unsigned scancode, unsigned mask) -> bool;
+  auto rows() -> signed;
+  auto rowsScrollable() -> signed;
+  auto scroll(signed position) -> void;
+  auto setCursorPosition(unsigned position) -> void;
+  auto setScroll() -> void;
+  auto updateScroll() -> void;
+
+  GtkWidget* container = nullptr;
+  GtkWidget* subWidget = nullptr;
+  GtkWidget* scrollBar = nullptr;
+  GtkTextBuffer* textBuffer = nullptr;
+  GtkTextMark* textCursor = nullptr;
+};
+
+}
diff --git a/hiro/gtk/widget/horizontal-scroller.cpp b/hiro/gtk/widget/horizontal-scroller.cpp
new file mode 100644
index 00000000..d8dc7ede
--- /dev/null
+++ b/hiro/gtk/widget/horizontal-scroller.cpp
@@ -0,0 +1,41 @@
+namespace hiro {
+
+static auto HorizontalScroller_change(GtkRange* gtkRange, pHorizontalScroller* p) -> void {
+  auto position = (unsigned)gtk_range_get_value(gtkRange);
+  if(p->state().position == position) return;
+  p->state().position = position;
+  if(!p->locked()) p->self().doChange();
+}
+
+auto pHorizontalScroller::construct() -> void {
+  gtkWidget = gtk_hscrollbar_new(0);
+
+  setLength(state().length);
+  setPosition(state().position);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalScroller_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pHorizontalScroller::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pHorizontalScroller::minimumSize() const -> Size {
+  return {0, 20};
+}
+
+auto pHorizontalScroller::setLength(unsigned length) -> void {
+  lock();
+  length += length == 0;
+  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
+  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
+  unlock();
+}
+
+auto pHorizontalScroller::setPosition(unsigned position) -> void {
+  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
+}
+
+}
diff --git a/hiro/gtk/widget/horizontal-scroller.hpp b/hiro/gtk/widget/horizontal-scroller.hpp
new file mode 100644
index 00000000..a522f0bb
--- /dev/null
+++ b/hiro/gtk/widget/horizontal-scroller.hpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+struct pHorizontalScroller : pWidget {
+  Declare(HorizontalScroller, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setLength(unsigned length) -> void;
+  auto setPosition(unsigned position) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/horizontal-slider.cpp b/hiro/gtk/widget/horizontal-slider.cpp
new file mode 100644
index 00000000..dbd2c379
--- /dev/null
+++ b/hiro/gtk/widget/horizontal-slider.cpp
@@ -0,0 +1,40 @@
+namespace hiro {
+
+static auto HorizontalSlider_change(GtkRange* gtkRange, pHorizontalSlider* p) -> void {
+  auto position = (unsigned)gtk_range_get_value(gtkRange);
+  if(p->state().position == position) return;
+  p->state().position = position;
+  if(!p->locked()) p->self().doChange();
+}
+
+auto pHorizontalSlider::construct() -> void {
+  gtkWidget = gtk_hscale_new_with_range(0, 100, 1);
+  gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
+
+  setLength(state().length);
+  setPosition(state().position);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pHorizontalSlider::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pHorizontalSlider::minimumSize() const -> Size {
+  return {0, 20};
+}
+
+auto pHorizontalSlider::setLength(unsigned length) -> void {
+  length += length == 0;
+  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
+  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
+}
+
+auto pHorizontalSlider::setPosition(unsigned position) -> void {
+  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
+}
+
+}
diff --git a/hiro/gtk/widget/horizontal-slider.hpp b/hiro/gtk/widget/horizontal-slider.hpp
new file mode 100644
index 00000000..c24fc004
--- /dev/null
+++ b/hiro/gtk/widget/horizontal-slider.hpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+struct pHorizontalSlider : pWidget {
+  Declare(HorizontalSlider, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setLength(unsigned length) -> void;
+  auto setPosition(unsigned position) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/icon-view-item.cpp b/hiro/gtk/widget/icon-view-item.cpp
new file mode 100644
index 00000000..fa985855
--- /dev/null
+++ b/hiro/gtk/widget/icon-view-item.cpp
@@ -0,0 +1,32 @@
+namespace hiro {
+
+auto pIconViewItem::construct() -> void {
+}
+
+auto pIconViewItem::destruct() -> void {
+}
+
+auto pIconViewItem::setIcon(const image& icon) -> void {
+  if(auto parent = _parent()) {
+    parent->setItemIcon(self().offset(), icon);
+  }
+}
+
+auto pIconViewItem::setSelected(bool selected) -> void {
+  if(auto parent = _parent()) {
+    parent->setItemSelected(self().offset(), selected);
+  }
+}
+
+auto pIconViewItem::setText(const string& text) -> void {
+  if(auto parent = _parent()) {
+    parent->setItemText(self().offset(), text);
+  }
+}
+
+auto pIconViewItem::_parent() -> pIconView* {
+  if(auto parent = self().parentIconView()) return parent->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/widget/icon-view-item.hpp b/hiro/gtk/widget/icon-view-item.hpp
new file mode 100644
index 00000000..4e8fad46
--- /dev/null
+++ b/hiro/gtk/widget/icon-view-item.hpp
@@ -0,0 +1,13 @@
+namespace hiro {
+
+struct pIconViewItem : pObject {
+  Declare(IconViewItem, Object)
+
+  auto setIcon(const image& icon) -> void;
+  auto setSelected(bool selected) -> void;
+  auto setText(const string& text) -> void;
+
+  auto _parent() -> pIconView*;
+};
+
+}
diff --git a/hiro/gtk/widget/icon-view.cpp b/hiro/gtk/widget/icon-view.cpp
new file mode 100644
index 00000000..73f242eb
--- /dev/null
+++ b/hiro/gtk/widget/icon-view.cpp
@@ -0,0 +1,224 @@
+namespace hiro {
+
+static auto IconView_activate(GtkIconView* iconView, GtkTreePath* path, pIconView* p) -> void {
+  if(!p->locked()) p->self().doActivate();
+}
+
+static auto IconView_buttonEvent(GtkTreeView* treeView, GdkEventButton* event, pIconView* p) -> signed {
+  if(event->type == GDK_BUTTON_RELEASE && event->button == 3) {
+    if(!p->locked()) p->self().doContext();
+    return false;
+  }
+
+  return false;
+}
+
+static auto IconView_change(GtkIconView* iconView, pIconView* p) -> void {
+  p->_updateSelected();
+}
+
+auto pIconView::construct() -> void {
+  gtkWidget = gtk_scrolled_window_new(0, 0);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
+
+  store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
+  subWidget = gtk_icon_view_new_with_model(GTK_TREE_MODEL(store));
+  gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(subWidget), 0);
+  gtk_icon_view_set_text_column(GTK_ICON_VIEW(subWidget), 1);
+  gtk_icon_view_set_reorderable(GTK_ICON_VIEW(subWidget), false);
+  gtk_icon_view_set_margin(GTK_ICON_VIEW(subWidget), 0);
+  gtk_icon_view_set_spacing(GTK_ICON_VIEW(subWidget), 0);
+  gtk_icon_view_set_column_spacing(GTK_ICON_VIEW(subWidget), 0);
+  gtk_icon_view_set_row_spacing(GTK_ICON_VIEW(subWidget), 0);
+  gtk_icon_view_set_item_padding(GTK_ICON_VIEW(subWidget), 0);
+  gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
+
+  gtk_widget_show(subWidget);
+
+  setBackgroundColor(state().backgroundColor);
+  setFlow(state().flow);
+  setForegroundColor(state().foregroundColor);
+  setMultiSelect(state().multiSelect);
+  setOrientation(state().orientation);
+  for(auto position : range(self().items())) {
+    auto& item = state().items[position];
+    append(item);
+  }
+  _updateSelected();
+
+  g_signal_connect(G_OBJECT(subWidget), "button-press-event", G_CALLBACK(IconView_buttonEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(subWidget), "button-release-event", G_CALLBACK(IconView_buttonEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(subWidget), "item-activated", G_CALLBACK(IconView_activate), (gpointer)this);
+  g_signal_connect(G_OBJECT(subWidget), "selection-changed", G_CALLBACK(IconView_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pIconView::destruct() -> void {
+  gtk_widget_destroy(subWidget);
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pIconView::append(sIconViewItem item) -> void {
+  GtkTreeIter iter;
+  gtk_list_store_append(store, &iter);
+  setItemIcon(item->offset(), item->state.icon);
+  setItemSelected(item->offset(), item->state.selected);
+  setItemText(item->offset(), item->state.text);
+}
+
+auto pIconView::remove(sIconViewItem item) -> void {
+  lock();
+  GtkTreeIter iter;
+  if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, string{item->offset()})) {
+    gtk_list_store_remove(store, &iter);
+  }
+  _updateSelected();
+  unlock();
+}
+
+auto pIconView::reset() -> void {
+  lock();
+  gtk_list_store_clear(store);
+  _updateSelected();
+  unlock();
+}
+
+auto pIconView::setBackgroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pIconView::setFlow(Orientation flow) -> void {
+  //GTK+ does not support vertical flow ... the closest we can get is a horizontal flow with only one column
+  if(flow == Orientation::Horizontal) {
+    gtk_icon_view_set_columns(GTK_ICON_VIEW(subWidget), -1);
+    gtk_icon_view_set_item_width(GTK_ICON_VIEW(subWidget), -1);
+  } else {
+    gtk_icon_view_set_columns(GTK_ICON_VIEW(subWidget), 1);
+    gtk_icon_view_set_item_width(GTK_ICON_VIEW(subWidget), max(128, pSizable::state().geometry.width() - 64));
+  }
+}
+
+auto pIconView::setForegroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pIconView::setGeometry(Geometry geometry) -> void {
+  pWidget::setGeometry(geometry);
+  if(state().flow == Orientation::Vertical) {
+    gtk_icon_view_set_item_width(GTK_ICON_VIEW(subWidget), max(128, pSizable::state().geometry.width() - 64));
+  }
+}
+
+auto pIconView::setItemIcon(unsigned position, const image& icon) -> void {
+  if(position >= self().items()) return;
+  GtkTreeIter iter;
+  if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, string{position})) {
+    if(icon) {
+      GdkPixbuf* pixbuf = CreatePixbuf(icon);
+      gtk_list_store_set(store, &iter, 0, pixbuf, -1);
+    } else {
+      gtk_list_store_set(store, &iter, 0, nullptr, -1);
+    }
+  }
+}
+
+auto pIconView::setItemSelected(unsigned position, bool selected) -> void {
+  if(position >= self().items()) return;
+  lock();
+  GtkTreePath* path = gtk_tree_path_new_from_string(string{position});
+  if(selected) {
+    gtk_icon_view_select_path(GTK_ICON_VIEW(subWidget), path);
+  } else {
+    gtk_icon_view_unselect_path(GTK_ICON_VIEW(subWidget), path);
+  }
+  gtk_tree_path_free(path);
+  _updateSelected();
+  unlock();
+}
+
+auto pIconView::setItemSelected(const vector<signed>& selections) -> void {
+  lock();
+  setItemSelectedNone();
+  for(auto& position : selections) setItemSelected(position, true);
+  _updateSelected();
+  unlock();
+}
+
+auto pIconView::setItemSelectedAll() -> void {
+  lock();
+  gtk_icon_view_select_all(GTK_ICON_VIEW(subWidget));
+  _updateSelected();
+  unlock();
+}
+
+auto pIconView::setItemSelectedNone() -> void {
+  lock();
+  gtk_icon_view_unselect_all(GTK_ICON_VIEW(subWidget));
+  _updateSelected();
+  unlock();
+}
+
+auto pIconView::setItemText(unsigned position, const string& text) -> void {
+  if(position >= self().items()) return;
+  GtkTreeIter iter;
+  if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, string{position})) {
+    gtk_list_store_set(store, &iter, 1, (const char*)text, -1);
+  }
+}
+
+auto pIconView::setMultiSelect(bool multiSelect) -> void {
+  gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(subWidget),
+    multiSelect ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE
+  );
+}
+
+auto pIconView::setOrientation(Orientation orientation) -> void {
+  gtk_icon_view_set_item_orientation(GTK_ICON_VIEW(subWidget),
+    orientation == Orientation::Horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL
+  );
+}
+
+auto pIconView::_updateSelected() -> void {
+  vector<unsigned> selected;
+
+  GList* list = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(subWidget));
+  GList* p = list;
+
+  while(p) {
+    auto path = (GtkTreePath*)p->data;
+    char* pathString = gtk_tree_path_to_string(path);
+    unsigned position = decimal(pathString);
+    g_free(pathString);
+    selected.append(position);
+    p = p->next;
+  }
+
+  g_list_foreach(list, (GFunc)gtk_tree_path_free, nullptr);
+  g_list_free(list);
+
+  bool identical = selected.size() == currentSelection.size();
+  if(identical) {
+    for(unsigned n = 0; n < selected.size(); n++) {
+      if(selected[n] != currentSelection[n]) {
+        identical = false;
+        break;
+      }
+    }
+  }
+  if(identical) return;
+
+  currentSelection = selected;
+  for(auto& item : state().items) item->state.selected = false;
+  for(auto& position : currentSelection) {
+    if(position >= self().items()) continue;
+    state().items[position]->state.selected = true;
+  }
+
+  if(!locked()) self().doChange();
+}
+
+}
diff --git a/hiro/gtk/widget/icon-view.hpp b/hiro/gtk/widget/icon-view.hpp
new file mode 100644
index 00000000..41447b6b
--- /dev/null
+++ b/hiro/gtk/widget/icon-view.hpp
@@ -0,0 +1,29 @@
+namespace hiro {
+
+struct pIconView : pWidget {
+  Declare(IconView, Widget)
+
+  auto append(sIconViewItem item) -> void;
+  auto remove(sIconViewItem item) -> void;
+  auto reset() -> void;
+  auto setBackgroundColor(Color color) -> void;
+  auto setFlow(Orientation flow) -> void;
+  auto setForegroundColor(Color color) -> void;
+  auto setGeometry(Geometry geometry) -> void;
+  auto setItemIcon(unsigned position, const image& icon) -> void;
+  auto setItemSelected(unsigned position, bool selected) -> void;
+  auto setItemSelected(const vector<signed>& selections) -> void;
+  auto setItemSelectedAll() -> void;
+  auto setItemSelectedNone() -> void;
+  auto setItemText(unsigned position, const string& text) -> void;
+  auto setMultiSelect(bool multiSelect) -> void;
+  auto setOrientation(Orientation orientation) -> void;
+
+  auto _updateSelected() -> void;
+
+  GtkWidget* subWidget = nullptr;
+  GtkListStore* store = nullptr;
+  vector<unsigned> currentSelection;
+};
+
+}
diff --git a/hiro/gtk/widget/label.cpp b/hiro/gtk/widget/label.cpp
new file mode 100644
index 00000000..69766178
--- /dev/null
+++ b/hiro/gtk/widget/label.cpp
@@ -0,0 +1,41 @@
+namespace hiro {
+
+auto pLabel::construct() -> void {
+  gtkWidget = gtk_label_new("");
+
+  _setAlignment();
+  setText(state().text);
+
+  pWidget::construct();
+}
+
+auto pLabel::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pLabel::minimumSize() const -> Size {
+  Size size = pFont::size(self().font(true), state().text);
+  return {size.width(), size.height()};
+}
+
+auto pLabel::setHorizontalAlignment(double alignment) -> void {
+  _setAlignment();
+}
+
+auto pLabel::setText(const string& text) -> void {
+  gtk_label_set_text(GTK_LABEL(gtkWidget), text);
+}
+
+auto pLabel::setVerticalAlignment(double alignment) -> void {
+  _setAlignment();
+}
+
+auto pLabel::_setAlignment() -> void {
+  gtk_misc_set_alignment(GTK_MISC(gtkWidget), state().horizontalAlignment, state().verticalAlignment);
+  auto justify = GTK_JUSTIFY_CENTER;
+  if(state().horizontalAlignment < 0.333) justify = GTK_JUSTIFY_LEFT;
+  if(state().horizontalAlignment > 0.666) justify = GTK_JUSTIFY_RIGHT;
+  gtk_label_set_justify(GTK_LABEL(gtkWidget), justify);
+}
+
+}
diff --git a/hiro/gtk/widget/label.hpp b/hiro/gtk/widget/label.hpp
new file mode 100644
index 00000000..6a79bb14
--- /dev/null
+++ b/hiro/gtk/widget/label.hpp
@@ -0,0 +1,14 @@
+namespace hiro {
+
+struct pLabel : pWidget {
+  Declare(Label, Widget)
+
+  auto minimumSize() const -> Size override;
+  auto setHorizontalAlignment(double alignment) -> void;
+  auto setText(const string& text) -> void;
+  auto setVerticalAlignment(double alignment) -> void;
+
+  auto _setAlignment() -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/line-edit.cpp b/hiro/gtk/widget/line-edit.cpp
new file mode 100644
index 00000000..0fb689b5
--- /dev/null
+++ b/hiro/gtk/widget/line-edit.cpp
@@ -0,0 +1,55 @@
+namespace hiro {
+
+static auto LineEdit_activate(GtkEntry*, pLineEdit* p) -> void {
+  p->self().doActivate();
+}
+
+static auto LineEdit_change(GtkEditable*, pLineEdit* p) -> void {
+  p->state().text = gtk_entry_get_text(GTK_ENTRY(p->gtkWidget));
+  if(!p->locked()) p->self().doChange();
+}
+
+auto pLineEdit::construct() -> void {
+  gtkWidget = gtk_entry_new();
+
+  setBackgroundColor(state().backgroundColor);
+  setEditable(state().editable);
+  setForegroundColor(state().foregroundColor);
+  setText(state().text);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "activate", G_CALLBACK(LineEdit_activate), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "changed", G_CALLBACK(LineEdit_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pLineEdit::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pLineEdit::minimumSize() const -> Size {
+  Size size = pFont::size(self().font(true), state().text);
+  return {size.width() + 10, size.height() + 10};
+}
+
+auto pLineEdit::setBackgroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_base(gtkWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pLineEdit::setEditable(bool editable) -> void {
+  gtk_editable_set_editable(GTK_EDITABLE(gtkWidget), editable);
+}
+
+auto pLineEdit::setForegroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_text(gtkWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pLineEdit::setText(const string& text) -> void {
+  lock();
+  gtk_entry_set_text(GTK_ENTRY(gtkWidget), text);
+  unlock();
+}
+
+}
diff --git a/hiro/gtk/widget/line-edit.hpp b/hiro/gtk/widget/line-edit.hpp
new file mode 100644
index 00000000..243278d6
--- /dev/null
+++ b/hiro/gtk/widget/line-edit.hpp
@@ -0,0 +1,13 @@
+namespace hiro {
+
+struct pLineEdit : pWidget {
+  Declare(LineEdit, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setBackgroundColor(Color color) -> void;
+  auto setEditable(bool editable) -> void;
+  auto setForegroundColor(Color color) -> void;
+  auto setText(const string& text) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/list-view-column.cpp b/hiro/gtk/widget/list-view-column.cpp
new file mode 100644
index 00000000..ffe9ae24
--- /dev/null
+++ b/hiro/gtk/widget/list-view-column.cpp
@@ -0,0 +1,133 @@
+namespace hiro {
+
+auto pListViewColumn::construct() -> void {
+  unsigned offset = self().offset();
+
+  gtkHeader = gtk_hbox_new(false, 0);
+
+  gtkHeaderIcon = gtk_image_new();
+  gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderIcon, false, false, 0);
+
+  gtkHeaderText = gtk_label_new(state().text);
+  gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderText, true, false, 2);
+
+  gtkColumn = gtk_tree_view_column_new();
+  gtk_tree_view_column_set_sizing(gtkColumn, GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_title(gtkColumn, "");
+  gtk_tree_view_column_set_widget(gtkColumn, gtkHeader);
+
+  if(offset == 0) {
+    gtkCellToggle = gtk_cell_renderer_toggle_new();
+    gtk_tree_view_column_pack_start(gtkColumn, gtkCellToggle, false);
+    gtk_tree_view_column_set_attributes(gtkColumn, gtkCellToggle, "active", 0, nullptr);
+  }
+
+  gtkCellIcon = gtk_cell_renderer_pixbuf_new();
+  gtk_tree_view_column_pack_start(gtkColumn, gtkCellIcon, false);
+  gtk_tree_view_column_set_attributes(gtkColumn, gtkCellIcon, "pixbuf", 1 + offset * 2 + 0, nullptr);
+
+  gtkCellText = gtk_cell_renderer_text_new();
+  gtk_tree_view_column_pack_start(gtkColumn, gtkCellText, false);
+  gtk_tree_view_column_set_attributes(gtkColumn, gtkCellText, "text", 1 + offset * 2 + 1, nullptr);
+
+  g_signal_connect(G_OBJECT(gtkColumn), "clicked", G_CALLBACK(ListView_headerActivate), (gpointer)_parent());
+  g_signal_connect(G_OBJECT(gtkCellText), "edited", G_CALLBACK(ListView_edit), (gpointer)_parent());
+  if(gtkCellToggle) g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(ListView_toggle), (gpointer)_parent());
+}
+
+auto pListViewColumn::destruct() -> void {
+}
+
+auto pListViewColumn::setActive() -> void {
+  if(auto parent = _parent()) {
+    gtk_tree_view_set_search_column(parent->gtkTreeView, 1 + self().offset() * 2 + 1);
+  }
+}
+
+auto pListViewColumn::setBackgroundColor(Color color) -> void {
+  if(color) {
+    GdkColor gdkColor = CreateColor(color);
+    if(gtkCellToggle) g_object_set(G_OBJECT(gtkCellToggle), "cell-background-gdk", &gdkColor, nullptr);
+    g_object_set(G_OBJECT(gtkCellIcon), "cell-background-gdk", &gdkColor, nullptr);
+    g_object_set(G_OBJECT(gtkCellText), "cell-background-gdk", &gdkColor, nullptr);
+  } else {
+    if(gtkCellToggle) g_object_set(G_OBJECT(gtkCellToggle), "cell-background-set", FALSE, nullptr);
+    g_object_set(G_OBJECT(gtkCellIcon), "cell-background-set", FALSE, nullptr);
+    g_object_set(G_OBJECT(gtkCellText), "cell-background-set", FALSE, nullptr);
+  }
+}
+
+auto pListViewColumn::setEditable(bool editable) -> void {
+  g_object_set(G_OBJECT(gtkCellText), "editable", editable ? TRUE : FALSE, nullptr);
+}
+
+auto pListViewColumn::setFont(const string& font) -> void {
+  pFont::setFont(gtkHeaderText, font);
+  auto fontDescription = pFont::create(font);
+  g_object_set(G_OBJECT(gtkCellText), "font-desc", fontDescription, nullptr);
+  pango_font_description_free(fontDescription);
+}
+
+auto pListViewColumn::setForegroundColor(Color color) -> void {
+  if(color) {
+    GdkColor gdkColor = CreateColor(color);
+    g_object_set(G_OBJECT(gtkCellText), "foreground-gdk", &gdkColor, nullptr);
+  } else {
+    g_object_set(G_OBJECT(gtkCellText), "foreground-set", FALSE, nullptr);
+  }
+}
+
+auto pListViewColumn::setHorizontalAlignment(double alignment) -> void {
+  _setAlignment();
+}
+
+auto pListViewColumn::setIcon(const image& icon) -> void {
+  if(icon) {
+    gtk_image_set_from_pixbuf(GTK_IMAGE(gtkHeaderIcon), CreatePixbuf(icon));
+  } else {
+    gtk_image_clear(GTK_IMAGE(gtkHeaderIcon));
+  }
+}
+
+auto pListViewColumn::setResizable(bool resizable) -> void {
+  gtk_tree_view_column_set_resizable(gtkColumn, resizable);
+}
+
+auto pListViewColumn::setSortable(bool sortable) -> void {
+  gtk_tree_view_column_set_clickable(gtkColumn, sortable);
+}
+
+auto pListViewColumn::setText(const string& text) -> void {
+  gtk_label_set_text(GTK_LABEL(gtkHeaderText), text);
+}
+
+auto pListViewColumn::setVerticalAlignment(double alignment) -> void {
+  _setAlignment();
+}
+
+auto pListViewColumn::setVisible(bool visible) -> void {
+  gtk_tree_view_column_set_visible(gtkColumn, visible);
+}
+
+auto pListViewColumn::setWidth(signed width) -> void {
+  if(auto parent = _parent()) {
+    parent->resizeColumns();
+  }
+}
+
+auto pListViewColumn::_parent() -> pListView* {
+  if(auto parent = self().parentListView()) return parent->self();
+  return nullptr;
+}
+
+auto pListViewColumn::_setAlignment() -> void {
+  gtk_tree_view_column_set_alignment(gtkColumn, state().horizontalAlignment);
+  gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(gtkCellText), state().horizontalAlignment, state().verticalAlignment);
+  //set multi-line text alignment
+  auto pangoAlignment = PANGO_ALIGN_CENTER;
+  if(state().horizontalAlignment < 0.333) pangoAlignment = PANGO_ALIGN_LEFT;
+  if(state().horizontalAlignment > 0.666) pangoAlignment = PANGO_ALIGN_RIGHT;
+  g_object_set(G_OBJECT(gtkCellText), "alignment", pangoAlignment, nullptr);
+}
+
+}
diff --git a/hiro/gtk/widget/list-view-column.hpp b/hiro/gtk/widget/list-view-column.hpp
new file mode 100644
index 00000000..42394ad1
--- /dev/null
+++ b/hiro/gtk/widget/list-view-column.hpp
@@ -0,0 +1,32 @@
+namespace hiro {
+
+struct pListViewColumn : pObject {
+  Declare(ListViewColumn, Object)
+
+  auto setActive() -> void;
+  auto setBackgroundColor(Color color) -> void;
+  auto setEditable(bool editable) -> void;
+  auto setFont(const string& font) -> void override;
+  auto setForegroundColor(Color color) -> void;
+  auto setHorizontalAlignment(double alignment) -> void;
+  auto setIcon(const image& icon) -> void;
+  auto setResizable(bool resizable) -> void;
+  auto setSortable(bool sortable) -> void;
+  auto setText(const string& text) -> void;
+  auto setVerticalAlignment(double alignment) -> void;
+  auto setVisible(bool visible) -> void override;
+  auto setWidth(signed width) -> void;
+
+  auto _parent() -> pListView*;
+  auto _setAlignment() -> void;
+
+  GtkTreeViewColumn* gtkColumn = nullptr;
+  GtkWidget* gtkHeader = nullptr;
+  GtkWidget* gtkHeaderIcon = nullptr;
+  GtkWidget* gtkHeaderText = nullptr;
+  GtkCellRenderer* gtkCellToggle = nullptr;
+  GtkCellRenderer* gtkCellIcon = nullptr;
+  GtkCellRenderer* gtkCellText = nullptr;
+};
+
+}
diff --git a/hiro/gtk/widget/list-view-item.cpp b/hiro/gtk/widget/list-view-item.cpp
new file mode 100644
index 00000000..3e7617dd
--- /dev/null
+++ b/hiro/gtk/widget/list-view-item.cpp
@@ -0,0 +1,59 @@
+namespace hiro {
+
+auto pListViewItem::construct() -> void {
+}
+
+auto pListViewItem::destruct() -> void {
+}
+
+auto pListViewItem::setChecked(bool checked) -> void {
+  if(auto parent = _parent()) {
+    gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, checked, -1);
+  }
+}
+
+auto pListViewItem::setFocused() -> void {
+  if(auto parent = _parent()) {
+    GtkTreePath* path = gtk_tree_path_new_from_string(string{self().offset()});
+    gtk_tree_view_set_cursor(parent->gtkTreeView, path, nullptr, false);
+    gtk_tree_view_scroll_to_cell(parent->gtkTreeView, path, nullptr, true, 0.5, 0.0);
+    gtk_tree_path_free(path);
+  }
+}
+
+auto pListViewItem::setIcon(unsigned column, const image& icon) -> void {
+  if(auto parent = _parent()) {
+    if(icon) {
+      auto pixbuf = CreatePixbuf(icon, true);
+      gtk_list_store_set(parent->gtkListStore, &gtkIter, 1 + column * 2, pixbuf, -1);
+    } else {
+      gtk_list_store_set(parent->gtkListStore, &gtkIter, 1 + column * 2, nullptr, -1);
+    }
+  }
+}
+
+auto pListViewItem::setSelected(bool selected) -> void {
+  if(auto parent = _parent()) {
+    parent->lock();
+    if(selected) {
+      gtk_tree_selection_select_iter(parent->gtkTreeSelection, &gtkIter);
+    } else {
+      gtk_tree_selection_unselect_iter(parent->gtkTreeSelection, &gtkIter);
+    }
+    parent->_updateSelected();
+    parent->unlock();
+  }
+}
+
+auto pListViewItem::setText(unsigned column, const string& text) -> void {
+  if(auto parent = _parent()) {
+    gtk_list_store_set(parent->gtkListStore, &gtkIter, 1 + column * 2 + 1, text.data(), -1);
+  }
+}
+
+auto pListViewItem::_parent() -> pListView* {
+  if(auto parent = self().parentListView()) return parent->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/widget/list-view-item.hpp b/hiro/gtk/widget/list-view-item.hpp
new file mode 100644
index 00000000..fd584e3b
--- /dev/null
+++ b/hiro/gtk/widget/list-view-item.hpp
@@ -0,0 +1,17 @@
+namespace hiro {
+
+struct pListViewItem : pObject {
+  Declare(ListViewItem, Object)
+
+  auto setChecked(bool checked) -> void;
+  auto setFocused() -> void;
+  auto setIcon(unsigned column, const image& icon) -> void;
+  auto setSelected(bool selected) -> void;
+  auto setText(unsigned column, const string& text) -> void;
+
+  auto _parent() -> pListView*;
+
+  GtkTreeIter gtkIter;
+};
+
+}
diff --git a/hiro/gtk/widget/list-view.cpp b/hiro/gtk/widget/list-view.cpp
new file mode 100644
index 00000000..79b75943
--- /dev/null
+++ b/hiro/gtk/widget/list-view.cpp
@@ -0,0 +1,369 @@
+namespace hiro {
+
+static auto ListView_activate(GtkTreeView*, GtkTreePath*, GtkTreeViewColumn*, pListView* p) -> void { return p->_doActivate(); }
+static auto ListView_buttonEvent(GtkTreeView* treeView, GdkEventButton* event, pListView* p) -> signed { return p->_doEvent(event); }
+static auto ListView_change(GtkTreeSelection*, pListView* p) -> void { return p->_doChange(); }
+static auto ListView_edit(GtkCellRendererText* renderer, const char* path, const char* text, pListView* p) -> void { return p->_doEdit(renderer, path, text); }
+static auto ListView_headerActivate(GtkTreeViewColumn* column, pListView* p) -> void { return p->_doHeaderActivate(column); }
+static auto ListView_mouseMoveEvent(GtkWidget*, GdkEvent*, pListView* p) -> signed { return p->_doMouseMove(); }
+static auto ListView_popup(GtkTreeView*, pListView* p) -> void { return p->_doContext(); }
+static auto ListView_toggle(GtkCellRendererToggle*, const char* path, pListView* p) -> void { return p->_doToggle(path); }
+
+auto pListView::construct() -> void {
+  gtkWidget = gtk_scrolled_window_new(0, 0);
+  gtkScrolledWindow = GTK_SCROLLED_WINDOW(gtkWidget);
+  gtk_scrolled_window_set_policy(gtkScrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type(gtkScrolledWindow, GTK_SHADOW_ETCHED_IN);
+
+  gtkWidgetChild = gtk_tree_view_new();
+  gtkTreeView = GTK_TREE_VIEW(gtkWidgetChild);
+  gtkTreeSelection = gtk_tree_view_get_selection(gtkTreeView);
+  gtk_container_add(GTK_CONTAINER(gtkWidget), gtkWidgetChild);
+  gtk_tree_view_set_rubber_banding(gtkTreeView, true);
+
+  gtk_widget_show(gtkWidgetChild);
+
+  setBackgroundColor(state().backgroundColor);
+  setCheckable(state().checkable);
+  setFont(self().font(true));
+  setForegroundColor(state().foregroundColor);
+  setGridVisible(state().gridVisible);
+  setHeaderVisible(state().headerVisible);
+  setMultiSelect(state().multiSelect);
+
+  g_signal_connect(G_OBJECT(gtkTreeView), "button-press-event", G_CALLBACK(ListView_buttonEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkTreeView), "button-release-event", G_CALLBACK(ListView_buttonEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkTreeView), "motion-notify-event", G_CALLBACK(ListView_mouseMoveEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkTreeView), "popup-menu", G_CALLBACK(ListView_popup), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkTreeView), "row-activated", G_CALLBACK(ListView_activate), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkTreeSelection), "changed", G_CALLBACK(ListView_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pListView::destruct() -> void {
+  gtk_widget_destroy(gtkWidgetChild);
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pListView::append(sListViewColumn column) -> void {
+  gtk_tree_view_append_column(gtkTreeView, column->self()->gtkColumn);
+  gtk_widget_show_all(column->self()->gtkHeader);
+  column->setFont(column->font());
+  setCheckable(state().checkable);
+  _createModel();
+  gtk_tree_view_set_rules_hint(gtkTreeView, self().columns() >= 2);  //two or more columns + checkbutton column
+}
+
+auto pListView::append(sListViewItem item) -> void {
+  gtk_list_store_append(gtkListStore, &item->self()->gtkIter);
+
+  item->setChecked(item->checked());
+  item->setSelected(item->selected());
+  for(auto column : range(self().columns())) {
+    item->setIcon(column, item->state.icon(column, {}));
+    item->setText(column, item->state.text(column, ""));
+  }
+}
+
+auto pListView::focused() -> bool {
+  return GTK_WIDGET_HAS_FOCUS(gtkTreeView);
+}
+
+auto pListView::remove(sListViewColumn column) -> void {
+  if(auto delegate = column->self()) {
+    gtk_tree_view_remove_column(gtkTreeView, delegate->gtkColumn);
+    delegate->gtkColumn = nullptr;
+  }
+  _createModel();
+  gtk_tree_view_set_rules_hint(gtkTreeView, self().columns() >= 2);  //two or more columns + checkbutton column
+}
+
+auto pListView::remove(sListViewItem item) -> void {
+  lock();
+  if(auto delegate = item->self()) {
+    gtk_list_store_remove(gtkListStore, &delegate->gtkIter);
+    _updateSelected();
+  }
+  unlock();
+}
+
+auto pListView::reset() -> void {
+  GList* list = gtk_tree_view_get_columns(gtkTreeView), *p = list;
+  while(p && p->data) {
+    gtk_tree_view_remove_column(gtkTreeView, (GtkTreeViewColumn*)p->data);
+    p = p->next;
+  }
+  g_list_free(list);
+  _createModel();
+  gtk_tree_view_set_rules_hint(gtkTreeView, false);
+}
+
+//column widths:
+//< 0 = expanding (consume all remaining space)
+//  0 = auto (resize to contents
+//> 0 = fixed width
+auto pListView::resizeColumns() -> void {
+  lock();
+
+  //compute the minimum width required for each column based upon the contents of all rows
+  vector<signed> minimumWidths;
+  for(auto column : range(self().columns())) {
+    signed maximumWidth = 1;
+    if(self().headerVisible()) {
+      maximumWidth = max(maximumWidth, 8 //margin
+      + state().columns[column]->state.icon.width
+      + Font::size(state().columns[column]->font(true), state().columns[column]->state.text).width()
+      );
+    }
+    for(auto row : range(self().items())) {
+      maximumWidth = max(maximumWidth, 8  //margin
+      + (row == 0 && state().checkable ? 24 : 0)  //check box
+      + state().items[row]->state.icon(column, {}).width
+      + Font::size(state().columns[column]->font(true), state().items[row]->state.text(column, "")).width()
+      );
+    }
+    if(!state().columns[column]->visible()) maximumWidth = 1;
+    minimumWidths.append(maximumWidth);
+  }
+
+  //subtract the widths of all non-expanding columns from the available widget space
+  signed expansions = 0;  //count the number of expanded columns
+  signed emptyWidth = pSizable::state().geometry.width() - 5;  //margin
+  for(auto column : range(self().columns())) {
+    signed width = state().columns[column]->width();
+    if(!state().columns[column]->visible()) width = 1;
+    if(width < 0) { expansions++; continue; }
+    if(width == 0) width = minimumWidths[column];
+    emptyWidth -= width;
+  }
+
+  //the vertical scroll bar consumes header space when visible; subtract it from available space if needed
+  auto scrollBar = gtk_scrolled_window_get_vscrollbar(gtkScrolledWindow);
+  if(scrollBar && gtk_widget_get_visible(scrollBar)) {
+    emptyWidth -= scrollBar->allocation.width;
+  }
+
+  //divide remaining space among all expanded columns
+  if(expansions && emptyWidth >= expansions) emptyWidth /= expansions;
+  else emptyWidth = 1;
+
+  for(auto column : range(self().columns())) {
+    signed width = state().columns[column]->width();
+    if(!state().columns[column]->visible()) width = 1;
+    if(width < 0) width = emptyWidth;
+    if(width == 0) width = minimumWidths[column];
+    gtk_tree_view_column_set_fixed_width(_column(column)->gtkColumn, width);
+  }
+
+  unlock();
+}
+
+auto pListView::setBackgroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_base(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pListView::setCheckable(bool checkable) -> void {
+  if(auto delegate = _column(0)) {
+    gtk_cell_renderer_set_visible(delegate->gtkCellToggle, checkable);
+  }
+}
+
+auto pListView::setChecked(bool checked) -> void {
+  for(auto& item : state().items) {
+    if(auto delegate = item->self()) delegate->setChecked(checked);
+  }
+}
+
+auto pListView::setFocused() -> void {
+  gtk_widget_grab_focus(gtkWidgetChild);
+}
+
+auto pListView::setFont(const string& font) -> void {
+  for(auto& column : state().columns) {
+    if(auto delegate = column->self()) delegate->setFont(column->font(true));
+  }
+}
+
+auto pListView::setForegroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_text(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pListView::setGridVisible(bool visible) -> void {
+  gtk_tree_view_set_grid_lines(gtkTreeView, visible ? GTK_TREE_VIEW_GRID_LINES_BOTH : GTK_TREE_VIEW_GRID_LINES_NONE);
+}
+
+auto pListView::setHeaderVisible(bool visible) -> void {
+  gtk_tree_view_set_headers_visible(gtkTreeView, visible);
+}
+
+auto pListView::setMultiSelect(bool multiSelect) -> void {
+  gtk_tree_selection_set_mode(gtkTreeSelection, multiSelect ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
+}
+
+auto pListView::setSelected(bool selected) -> void {
+  for(auto& item : state().items) {
+    if(auto delegate = item->self()) delegate->setSelected(selected);
+  }
+}
+
+auto pListView::_column(unsigned column) -> pListViewColumn* {
+  if(auto delegate = self().column(column)) return delegate->self();
+  return nullptr;
+}
+
+auto pListView::_createModel() -> void {
+  gtk_tree_view_set_model(gtkTreeView, nullptr);
+  gtkListStore = nullptr;
+  gtkTreeModel = nullptr;
+
+  vector<GType> types;
+  unsigned position = 0;
+  for(auto column : state().columns) {
+    if(!column->self()->gtkColumn) continue;  //column is being removed
+    if(position++ == 0) types.append(G_TYPE_BOOLEAN);
+    types.append(GDK_TYPE_PIXBUF);
+    types.append(G_TYPE_STRING);
+  }
+  if(!types) return;  //no columns available
+
+  gtkListStore = gtk_list_store_newv(types.size(), types.data());
+  gtkTreeModel = GTK_TREE_MODEL(gtkListStore);
+  gtk_tree_view_set_model(gtkTreeView, gtkTreeModel);
+}
+
+auto pListView::_doActivate() -> void {
+  if(!locked()) self().doActivate();
+}
+
+auto pListView::_doChange() -> void {
+  if(!locked()) _updateSelected();
+}
+
+auto pListView::_doContext() -> void {
+  if(!locked()) self().doContext();
+}
+
+auto pListView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void {
+  for(auto& column : state().columns) {
+    if(auto delegate = column->self()) {
+      if(gtkCellRendererText = GTK_CELL_RENDERER_TEXT(delegate->gtkCellText)) {
+        if(auto item = self().item(decimal(path))) {
+          if(string{text} != item->text(column->offset())) {
+            item->setText(column->offset(), text);
+            if(!locked()) self().doEdit(item, column);
+          }
+          return;
+        }
+      }
+    }
+  }
+}
+
+auto pListView::_doEvent(GdkEventButton* event) -> signed {
+  GtkTreePath* path = nullptr;
+  gtk_tree_view_get_path_at_pos(gtkTreeView, event->x, event->y, &path, nullptr, nullptr, nullptr);
+
+  if(event->type == GDK_BUTTON_PRESS) {
+    //when clicking in empty space below the last list view item; GTK+ does not deselect all items;
+    //below code enables this functionality, to match behavior with all other UI toolkits (and because it's very convenient to have)
+    if(path == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
+      self().setSelected({});
+      self().doChange();
+      return true;
+    }
+  }
+
+  if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
+    //this check prevents the loss of selection on other items if the item under the mouse cursor is currently selected
+    if(path && gtk_tree_selection_path_is_selected(gtkTreeSelection, path)) return true;
+  }
+
+  if(event->type == GDK_BUTTON_RELEASE && event->button == 3) {
+    //handle action during right-click release; as button-press-event is sent prior to selection update
+    //without this, the callback handler would see the previous selection state instead
+    self().doContext();
+    return false;
+  }
+
+  return false;
+}
+
+auto pListView::_doHeaderActivate(GtkTreeViewColumn* gtkTreeViewColumn) -> void {
+  for(auto& column : state().columns) {
+    if(auto delegate = column->self()) {
+      if(gtkTreeViewColumn == delegate->gtkColumn) {
+        if(!locked()) self().doSort(column);
+        return;
+      }
+    }
+  }
+}
+
+//GtkTreeView::cursor-changed and GtkTreeSelection::changed do not send signals for changes during rubber-banding selection
+//so here we capture motion-notify-event, and if the selections have changed, invoke ListView::onChange
+auto pListView::_doMouseMove() -> signed {
+  if(gtk_tree_view_is_rubber_banding_active(gtkTreeView)) {
+    if(!locked()) _updateSelected();
+  }
+  return false;
+}
+
+auto pListView::_doToggle(const char* path) -> void {
+  if(auto item = self().item(decimal(path))) {
+    if(auto delegate = item->self()) {
+      item->state.checked = !item->state.checked;
+      delegate->setChecked(item->state.checked);
+      if(!locked()) self().doToggle(item);
+    }
+  }
+}
+
+//compare currently selected items to previously selected items
+//if different, invoke the onChange callback unless locked, and cache current selection
+//this prevents firing an onChange event when the actual selection has not changed
+//this is particularly important for the motion-notify-event binding
+auto pListView::_updateSelected() -> void {
+  vector<unsigned> selected;
+
+  GList* list = gtk_tree_selection_get_selected_rows(gtkTreeSelection, &gtkTreeModel);
+  GList* p = list;
+
+  while(p) {
+    GtkTreeIter iter;
+    if(gtk_tree_model_get_iter(gtkTreeModel, &iter, (GtkTreePath*)p->data)) {
+      char* pathname = gtk_tree_model_get_string_from_iter(gtkTreeModel, &iter);
+      unsigned selection = decimal(pathname);
+      g_free(pathname);
+      selected.append(selection);
+    }
+    p = p->next;
+  }
+
+  g_list_foreach(list, (GFunc)gtk_tree_path_free, nullptr);
+  g_list_free(list);
+
+  bool identical = selected.size() == currentSelection.size();
+  if(identical) {
+    for(auto n : range(selected)) {
+      if(selected[n] != currentSelection[n]) {
+        identical = false;
+        break;
+      }
+    }
+  }
+  if(identical) return;
+
+  currentSelection = selected;
+  for(auto& item : state().items) item->state.selected = false;
+  for(auto& position : currentSelection) {
+    if(position >= self().items()) continue;
+    self().item(position)->state.selected = true;
+  }
+
+  if(!locked()) self().doChange();
+}
+
+}
diff --git a/hiro/gtk/widget/list-view.hpp b/hiro/gtk/widget/list-view.hpp
new file mode 100644
index 00000000..576cde40
--- /dev/null
+++ b/hiro/gtk/widget/list-view.hpp
@@ -0,0 +1,45 @@
+namespace hiro {
+
+struct pListView : pWidget {
+  Declare(ListView, Widget)
+
+  auto append(sListViewColumn column) -> void;
+  auto append(sListViewItem item) -> void;
+  auto focused() -> bool;
+  auto remove(sListViewColumn column) -> void;
+  auto remove(sListViewItem item) -> void;
+  auto reset() -> void;
+  auto resizeColumns() -> void;
+  auto setBackgroundColor(Color color) -> void;
+  auto setCheckable(bool checkable) -> void;
+  auto setChecked(bool checked) -> void;
+  auto setFocused() -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setForegroundColor(Color color) -> void;
+  auto setGridVisible(bool visible) -> void;
+  auto setHeaderVisible(bool visible) -> void;
+  auto setMultiSelect(bool multiSelect) -> void;
+  auto setSelected(bool selected) -> void;
+
+  auto _column(unsigned column) -> pListViewColumn*;
+  auto _createModel() -> void;
+  auto _doActivate() -> void;
+  auto _doChange() -> void;
+  auto _doContext() -> void;
+  auto _doEdit(GtkCellRendererText* renderer, const char* path, const char* text) -> void;
+  auto _doEvent(GdkEventButton* event) -> signed;
+  auto _doHeaderActivate(GtkTreeViewColumn* column) -> void;
+  auto _doMouseMove() -> signed;
+  auto _doToggle(const char* path) -> void;
+  auto _updateSelected() -> void;
+
+  GtkScrolledWindow* gtkScrolledWindow = nullptr;
+  GtkWidget* gtkWidgetChild = nullptr;
+  GtkTreeView* gtkTreeView = nullptr;
+  GtkTreeSelection* gtkTreeSelection = nullptr;
+  GtkListStore* gtkListStore = nullptr;
+  GtkTreeModel* gtkTreeModel = nullptr;
+  vector<unsigned> currentSelection;
+};
+
+}
diff --git a/hiro/gtk/widget/progress-bar.cpp b/hiro/gtk/widget/progress-bar.cpp
new file mode 100644
index 00000000..8e888d88
--- /dev/null
+++ b/hiro/gtk/widget/progress-bar.cpp
@@ -0,0 +1,24 @@
+namespace hiro {
+
+auto pProgressBar::construct() -> void {
+  gtkWidget = gtk_progress_bar_new();
+
+  setPosition(state().position);
+
+  pWidget::construct();
+}
+
+auto pProgressBar::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pProgressBar::minimumSize() const -> Size {
+  return {0, 25};
+}
+
+auto pProgressBar::setPosition(unsigned position) -> void {
+  position = position <= 100 ? position : 0;
+  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkWidget), (double)position / 100.0);
+}
+
+}
diff --git a/hiro/gtk/widget/progress-bar.hpp b/hiro/gtk/widget/progress-bar.hpp
new file mode 100644
index 00000000..14e677c1
--- /dev/null
+++ b/hiro/gtk/widget/progress-bar.hpp
@@ -0,0 +1,10 @@
+namespace hiro {
+
+struct pProgressBar : pWidget {
+  Declare(ProgressBar, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setPosition(unsigned position) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/radio-button.cpp b/hiro/gtk/widget/radio-button.cpp
new file mode 100644
index 00000000..97b5dd85
--- /dev/null
+++ b/hiro/gtk/widget/radio-button.cpp
@@ -0,0 +1,101 @@
+namespace hiro {
+
+static auto RadioButton_activate(GtkToggleButton*, pRadioButton* p) -> void {
+  if(p->_parent().locked()) return;
+  bool wasChecked = p->state().checked;
+  p->setChecked();
+  if(!wasChecked) p->self().doActivate();
+}
+
+auto pRadioButton::construct() -> void {
+  gtkWidget = gtk_toggle_button_new();
+
+  setGroup(state().group);
+  setBordered(state().bordered);
+  setIcon(state().icon);
+  setOrientation(state().orientation);
+  setText(state().text);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioButton_activate), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pRadioButton::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pRadioButton::minimumSize() const -> Size {
+  Size size = pFont::size(self().font(true), state().text);
+
+  if(state().orientation == Orientation::Horizontal) {
+    size.setWidth(size.width() + state().icon.width);
+    size.setHeight(max(size.height(), state().icon.height));
+  }
+
+  if(state().orientation == Orientation::Vertical) {
+    size.setWidth(max(size.width(), state().icon.width));
+    size.setHeight(size.height() + state().icon.height);
+  }
+
+  return {size.width() + 24, size.height() + 12};
+}
+
+auto pRadioButton::setBordered(bool bordered) -> void {
+  gtk_button_set_relief(GTK_BUTTON(gtkWidget), bordered ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE);
+}
+
+auto pRadioButton::setChecked() -> void {
+  _parent().lock();
+  for(auto& weak : state().group) {
+    if(auto item = weak.acquire()) {
+      if(item->self()) {
+        bool checked = item->self() == this;
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->self()->gtkWidget), item->state.checked = checked);
+      }
+    }
+  }
+  _parent().unlock();
+}
+
+auto pRadioButton::setGroup(const vector<wRadioButton>& group) -> void {
+  _parent().lock();
+  for(auto& weak : state().group) {
+    if(auto item = weak.acquire()) {
+      if(item->self()) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->self()->gtkWidget), item->checked());
+    }
+  }
+  _parent().unlock();
+}
+
+auto pRadioButton::setIcon(const image& icon) -> void {
+  if(icon) {
+    GtkImage* gtkImage = CreateImage(icon);
+    gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage);
+  } else {
+    gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr);
+  }
+}
+
+auto pRadioButton::setOrientation(Orientation orientation) -> void {
+  switch(orientation) {
+  case Orientation::Horizontal: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_LEFT); break;
+  case Orientation::Vertical:   gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_TOP);  break;
+  }
+}
+
+auto pRadioButton::setText(const string& text) -> void {
+  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
+  setFont(self().font(true));  //gtk_button_set_label() recreates label, which destroys currently assigned font
+}
+
+auto pRadioButton::_parent() -> pRadioButton& {
+  if(state().group.size()) {
+    if(auto item = state().group.first().acquire()) {
+      if(item->self()) return *item->self();
+    }
+  }
+  return *this;
+}
+
+}
diff --git a/hiro/gtk/widget/radio-button.hpp b/hiro/gtk/widget/radio-button.hpp
new file mode 100644
index 00000000..f74ac141
--- /dev/null
+++ b/hiro/gtk/widget/radio-button.hpp
@@ -0,0 +1,17 @@
+namespace hiro {
+
+struct pRadioButton : pWidget {
+  Declare(RadioButton, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setBordered(bool bordered) -> void;
+  auto setChecked() -> void;
+  auto setGroup(const vector<wRadioButton>& group) -> void;
+  auto setIcon(const image& icon) -> void;
+  auto setOrientation(Orientation orientation) -> void;
+  auto setText(const string& text) -> void;
+
+  auto _parent() -> pRadioButton&;
+};
+
+}
diff --git a/hiro/gtk/widget/radio-label.cpp b/hiro/gtk/widget/radio-label.cpp
new file mode 100644
index 00000000..b3ec8c61
--- /dev/null
+++ b/hiro/gtk/widget/radio-label.cpp
@@ -0,0 +1,71 @@
+namespace hiro {
+
+static auto RadioLabel_activate(GtkToggleButton*, pRadioLabel* p) -> void {
+  if(p->_parent().locked()) return;
+  bool wasChecked = p->state().checked;
+  p->setChecked();
+  if(!wasChecked) p->self().doActivate();
+}
+
+auto pRadioLabel::construct() -> void {
+  gtkWidget = gtk_radio_button_new_with_label(nullptr, "");
+
+  setGroup(state().group);
+  setText(state().text);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioLabel_activate), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pRadioLabel::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pRadioLabel::minimumSize() const -> Size {
+  Size size = pFont::size(self().font(true), state().text);
+  return {size.width() + 28, size.height() + 4};
+}
+
+auto pRadioLabel::setChecked() -> void {
+  _parent().lock();
+  for(auto& weak : state().group) {
+    if(auto item = weak.acquire()) item->state.checked = false;
+  }
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), state().checked = true);
+  _parent().unlock();
+}
+
+auto pRadioLabel::setGroup(const vector<shared_pointer_weak<mRadioLabel>>& group) -> void {
+  if(&_parent() == this) return;
+  _parent().lock();
+  gtk_radio_button_set_group(
+    GTK_RADIO_BUTTON(gtkWidget),
+    gtk_radio_button_get_group(GTK_RADIO_BUTTON(_parent().gtkWidget))
+  );
+  for(auto& weak : state().group) {
+    if(auto item = weak.acquire()) {
+      if(item->self() && item->checked()) {
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->self()->gtkWidget), true);
+        break;
+      }
+    }
+  }
+  _parent().unlock();
+}
+
+auto pRadioLabel::setText(const string& text) -> void {
+  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
+  setFont(self().font(true));  //gtk_button_set_label() recreates label, which destroys currently assigned font
+}
+
+auto pRadioLabel::_parent() -> pRadioLabel& {
+  if(state().group.size()) {
+    if(auto item = state().group.first().acquire()) {
+      if(item->self()) return *item->self();
+    }
+  }
+  return *this;
+}
+
+}
diff --git a/hiro/gtk/widget/radio-label.hpp b/hiro/gtk/widget/radio-label.hpp
new file mode 100644
index 00000000..758980ba
--- /dev/null
+++ b/hiro/gtk/widget/radio-label.hpp
@@ -0,0 +1,14 @@
+namespace hiro {
+
+struct pRadioLabel : pWidget {
+  Declare(RadioLabel, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setChecked() -> void;
+  auto setGroup(const vector<shared_pointer_weak<mRadioLabel>>& group) -> void;
+  auto setText(const string& text) -> void;
+
+  auto _parent() -> pRadioLabel&;
+};
+
+}
diff --git a/hiro/gtk/widget/source-edit.cpp b/hiro/gtk/widget/source-edit.cpp
new file mode 100644
index 00000000..aacb9264
--- /dev/null
+++ b/hiro/gtk/widget/source-edit.cpp
@@ -0,0 +1,126 @@
+namespace hiro {
+
+static auto SourceEdit_change(GtkTextBuffer*, pSourceEdit* p) -> void {
+  if(!p->locked()) p->self().doChange();
+}
+
+static auto SourceEdit_move(GObject*, GParamSpec*, pSourceEdit* p) -> void {
+  signed position = 0;
+  g_object_get(G_OBJECT(p->gtkSourceBuffer), "cursor-position", &position, nullptr);
+
+  if(p->state().position != position) {
+    p->state().position = position;
+    if(!p->locked()) p->self().doMove();
+  }
+}
+
+auto pSourceEdit::construct() -> void {
+  gtkScrolledWindow = (GtkScrolledWindow*)gtk_scrolled_window_new(0, 0);
+  gtkContainer = GTK_CONTAINER(gtkScrolledWindow);
+  gtkWidget = GTK_WIDGET(gtkScrolledWindow);
+  gtk_scrolled_window_set_policy(gtkScrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type(gtkScrolledWindow, GTK_SHADOW_ETCHED_IN);
+
+  gtkSourceLanguageManager = gtk_source_language_manager_get_default();
+  gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, "cpp");
+
+  gtkSourceStyleSchemeManager = gtk_source_style_scheme_manager_get_default();
+  gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "oblivion");
+
+  gtkSourceBuffer = gtk_source_buffer_new(nullptr);
+  gtkTextBuffer = GTK_TEXT_BUFFER(gtkSourceBuffer);
+  gtk_source_buffer_set_highlight_matching_brackets(gtkSourceBuffer, true);
+  gtk_source_buffer_set_highlight_syntax(gtkSourceBuffer, true);
+//gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
+  gtk_source_buffer_set_style_scheme(gtkSourceBuffer, gtkSourceStyleScheme);
+
+  gtkSourceView = (GtkSourceView*)gtk_source_view_new_with_buffer(gtkSourceBuffer);
+  gtkTextView = GTK_TEXT_VIEW(gtkSourceView);
+  gtkWidgetSourceView = GTK_WIDGET(gtkSourceView);
+  gtk_source_view_set_auto_indent(gtkSourceView, false);
+  gtk_source_view_set_draw_spaces(gtkSourceView, (GtkSourceDrawSpacesFlags)0);
+  gtk_source_view_set_highlight_current_line(gtkSourceView, true);
+  gtk_source_view_set_indent_on_tab(gtkSourceView, false);
+  gtk_source_view_set_indent_width(gtkSourceView, 4);
+  gtk_source_view_set_insert_spaces_instead_of_tabs(gtkSourceView, false);
+  gtk_source_view_set_right_margin_position(gtkSourceView, 80);
+  gtk_source_view_set_show_line_marks(gtkSourceView, false);
+  gtk_source_view_set_show_line_numbers(gtkSourceView, true);
+  gtk_source_view_set_show_right_margin(gtkSourceView, true);
+  gtk_source_view_set_smart_home_end(gtkSourceView, GTK_SOURCE_SMART_HOME_END_DISABLED);
+  gtk_source_view_set_tab_width(gtkSourceView, 4);
+  gtk_container_add(gtkContainer, gtkWidgetSourceView);
+  gtk_widget_show(gtkWidgetSourceView);
+
+  setText(state().text);
+
+  g_signal_connect(G_OBJECT(gtkSourceBuffer), "changed", G_CALLBACK(SourceEdit_change), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkSourceBuffer), "notify::cursor-position", G_CALLBACK(SourceEdit_move), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pSourceEdit::destruct() -> void {
+  state().text = text();
+  gtk_widget_destroy(gtkWidgetSourceView);
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pSourceEdit::setFocused() -> void {
+  gtk_widget_grab_focus(gtkWidgetSourceView);
+}
+
+auto pSourceEdit::setPosition(signed position) -> void {
+  lock();
+  GtkTextIter iter;
+  //note: iterators must be initialized via get_iter() before calling set_offset()
+  gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
+  if(position >= 0) {
+    gtk_text_iter_set_offset(&iter, position);
+  } else {
+    state().position = gtk_text_iter_get_offset(&iter);
+  }
+  gtk_text_buffer_place_cursor(gtkTextBuffer, &iter);
+  auto mark = gtk_text_buffer_get_mark(gtkTextBuffer, "insert");
+  gtk_text_view_scroll_mark_onscreen(gtkTextView, mark);
+  unlock();
+}
+
+auto pSourceEdit::setSelected(Position selected) -> void {
+  lock();
+  GtkTextIter iter;
+  gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
+  signed offset = gtk_text_iter_get_offset(&iter);
+  if(selected.x() < 0 || selected.x() > offset) selected.setX(offset);
+  if(selected.y() < 0 || selected.y() > offset) selected.setY(offset);
+  state().selected = selected;
+  GtkTextIter startIter;
+  gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter);
+  gtk_text_iter_set_offset(&startIter, selected.x());
+  GtkTextIter endIter;
+  gtk_text_buffer_get_end_iter(gtkTextBuffer, &endIter);
+  gtk_text_iter_set_offset(&endIter, selected.y());
+  gtk_text_buffer_select_range(gtkTextBuffer, &startIter, &endIter);
+  unlock();
+}
+
+auto pSourceEdit::setText(const string& text) -> void {
+  lock();
+  gtk_text_buffer_set_text(gtkTextBuffer, text, -1);
+  unlock();
+}
+
+auto pSourceEdit::text() const -> string {
+  GtkTextIter startIter;
+  gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter);
+
+  GtkTextIter endIter;
+  gtk_text_buffer_get_end_iter(gtkTextBuffer, &endIter);
+
+  char* textBuffer = gtk_text_buffer_get_text(gtkTextBuffer, &startIter, &endIter, true);
+  string text = textBuffer;
+  g_free(textBuffer);
+  return text;
+}
+
+}
diff --git a/hiro/gtk/widget/source-edit.hpp b/hiro/gtk/widget/source-edit.hpp
new file mode 100644
index 00000000..7c9c07e5
--- /dev/null
+++ b/hiro/gtk/widget/source-edit.hpp
@@ -0,0 +1,25 @@
+namespace hiro {
+
+struct pSourceEdit : pWidget {
+  Declare(SourceEdit, Widget)
+
+  auto setFocused() -> void override;
+  auto setPosition(signed position) -> void;
+  auto setSelected(Position selected) -> void;
+  auto setText(const string& text) -> void;
+  auto text() const -> string;
+
+  GtkScrolledWindow* gtkScrolledWindow = nullptr;
+  GtkContainer* gtkContainer = nullptr;
+  GtkSourceBuffer* gtkSourceBuffer = nullptr;
+  GtkTextBuffer* gtkTextBuffer = nullptr;
+  GtkSourceLanguageManager* gtkSourceLanguageManager = nullptr;
+  GtkSourceLanguage* gtkSourceLanguage = nullptr;
+  GtkSourceStyleSchemeManager* gtkSourceStyleSchemeManager = nullptr;
+  GtkSourceStyleScheme* gtkSourceStyleScheme = nullptr;
+  GtkSourceView* gtkSourceView = nullptr;
+  GtkTextView* gtkTextView = nullptr;
+  GtkWidget* gtkWidgetSourceView = nullptr;
+};
+
+}
diff --git a/hiro/gtk/widget/tab-frame-item.cpp b/hiro/gtk/widget/tab-frame-item.cpp
new file mode 100644
index 00000000..b202e4bb
--- /dev/null
+++ b/hiro/gtk/widget/tab-frame-item.cpp
@@ -0,0 +1,46 @@
+namespace hiro {
+
+auto pTabFrameItem::construct() -> void {
+  if(auto layout = state().layout) layout->construct();
+}
+
+auto pTabFrameItem::destruct() -> void {
+  if(auto layout = state().layout) layout->destruct();
+}
+
+auto pTabFrameItem::setClosable(bool closable) -> void {
+  if(auto parent = _parent()) {
+    parent->setItemClosable(self().offset(), closable);
+  }
+}
+
+auto pTabFrameItem::setIcon(const image& icon) -> void {
+  if(auto parent = _parent()) {
+    parent->setItemIcon(self().offset(), icon);
+  }
+}
+
+auto pTabFrameItem::setMovable(bool movable) -> void {
+  if(auto parent = _parent()) {
+    parent->setItemMovable(self().offset(), movable);
+  }
+}
+
+auto pTabFrameItem::setSelected() -> void {
+  if(auto parent = _parent()) {
+    parent->setItemSelected(self().offset());
+  }
+}
+
+auto pTabFrameItem::setText(const string& text) -> void {
+  if(auto parent = _parent()) {
+    parent->setItemText(self().offset(), text);
+  }
+}
+
+auto pTabFrameItem::_parent() -> pTabFrame* {
+  if(auto parent = self().parentTabFrame()) return parent->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/widget/tab-frame-item.hpp b/hiro/gtk/widget/tab-frame-item.hpp
new file mode 100644
index 00000000..2f3ddd30
--- /dev/null
+++ b/hiro/gtk/widget/tab-frame-item.hpp
@@ -0,0 +1,15 @@
+namespace hiro {
+
+struct pTabFrameItem : pObject {
+  Declare(TabFrameItem, Object)
+
+  auto setClosable(bool closable) -> void;
+  auto setIcon(const image& icon) -> void;
+  auto setMovable(bool movable) -> void;
+  auto setSelected() -> void;
+  auto setText(const string& text) -> void;
+
+  auto _parent() -> pTabFrame*;
+};
+
+}
diff --git a/hiro/gtk/widget/tab-frame.cpp b/hiro/gtk/widget/tab-frame.cpp
new file mode 100644
index 00000000..0ffa1243
--- /dev/null
+++ b/hiro/gtk/widget/tab-frame.cpp
@@ -0,0 +1,265 @@
+namespace hiro {
+
+static auto TabFrame_change(GtkNotebook* notebook, GtkWidget* page, unsigned position, pTabFrame* p) -> void {
+  p->state().selected = position;
+  p->_synchronizeLayout();
+  if(!p->locked()) p->self().doChange();
+}
+
+static auto TabFrame_close(GtkButton* button, pTabFrame* p) -> void {
+  maybe<unsigned> position;
+  for(auto n : range(p->tabs)) {
+    if(button == (GtkButton*)p->tabs[n].close) {
+      position = n;
+      break;
+    }
+  }
+  if(position) {
+    if(!p->locked()) p->self().doClose(p->self().item(*position));
+  }
+}
+
+static auto TabFrame_move(GtkNotebook* notebook, GtkWidget* page, unsigned moveTo, pTabFrame* p) -> void {
+  p->state().selected = gtk_notebook_get_current_page(notebook);
+  maybe<unsigned> moveFrom;
+  for(auto n : range(p->tabs)) {
+    if(page == p->tabs[n].child) {
+      moveFrom = n;
+      break;
+    }
+  }
+  if(moveFrom) {
+    p->state().items.insert(moveTo, p->state().items.take(*moveFrom));
+    p->tabs.insert(moveTo, p->tabs.take(*moveFrom));
+    if(!p->locked()) p->self().doMove(p->self().item(*moveFrom), p->self().item(moveTo));
+  }
+}
+
+auto pTabFrame::construct() -> void {
+  gtkWidget = gtk_notebook_new();
+  gtk_notebook_set_show_border(GTK_NOTEBOOK(gtkWidget), false);
+  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), GTK_POS_TOP);
+
+  tabs.reset();  //todo: memory leak, need to release each tab
+  for(auto& item : state().items) append(item);
+  setEdge(state().edge);
+  setItemSelected(state().selected);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "page-reordered", G_CALLBACK(TabFrame_move), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "switch-page", G_CALLBACK(TabFrame_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pTabFrame::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pTabFrame::append(sTabFrameItem item) -> void {
+  lock();
+  Tab tab;
+  tab.child = gtk_fixed_new();
+  tab.container = gtk_hbox_new(false, 0);
+  tab.image = gtk_image_new();
+  tab.title = gtk_label_new("");
+  gtk_misc_set_alignment(GTK_MISC(tab.title), 0.0, 0.5);
+  tab.close = gtk_button_new_with_label("\u00d7");  //Unicode multiplication sign (looks better than 'X')
+  gtk_button_set_focus_on_click(GTK_BUTTON(tab.close), false);
+  gtk_button_set_relief(GTK_BUTTON(tab.close), GTK_RELIEF_NONE);
+  pFont::setFont(tab.close, Font::sans(9, "Bold"));
+  auto color = CreateColor({255, 0, 0});
+  gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(tab.close)), GTK_STATE_PRELIGHT, &color);
+  tabs.append(tab);
+
+  gtk_widget_show(tab.child);
+  gtk_widget_show(tab.container);
+  gtk_widget_show(tab.image);
+  gtk_widget_show(tab.title);
+  gtk_widget_show(tab.close);
+  gtk_box_pack_start(GTK_BOX(tab.container), tab.image, false, false, 0);
+  gtk_box_pack_start(GTK_BOX(tab.container), tab.title, true, true, 0);
+  gtk_box_pack_start(GTK_BOX(tab.container), tab.close, false, false, 0);
+
+  g_signal_connect(G_OBJECT(tab.close), "clicked", G_CALLBACK(TabFrame_close), (gpointer)this);
+  gtk_notebook_append_page(GTK_NOTEBOOK(gtkWidget), tab.child, tab.container);
+
+  setFont(self().font(true));
+  setItemMovable(item->offset(), item->movable());
+  _synchronizeTab(tabs.size() - 1);
+  setGeometry(self().geometry());
+  unlock();
+}
+
+auto pTabFrame::container(mWidget& widget) -> GtkWidget* {
+  auto widgetLayout = widget.parentLayout();
+  unsigned position = 0;
+  for(auto& item : state().items) {
+    if(item->state.layout.data() == widgetLayout) return tabs[position].child;
+    position++;
+  }
+  return nullptr;
+}
+
+auto pTabFrame::remove(sTabFrameItem item) -> void {
+  lock();
+  //if we are removing the current tab, we have to select another tab manually
+  if(item->offset() == gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkWidget))) {
+    //the new tab will be the one after this one
+    unsigned displacement = 1;
+    //... unless it's the last tab, in which case it's the one before it
+    if(item->offset() == self().items() - 1) displacement = -1;
+    //... unless there are no tabs left, in which case nothing is selected
+    if(self().items() > 1) {
+      setItemSelected(item->offset() + displacement);
+    }
+  }
+  tabs.remove(item->offset());
+  gtk_notebook_remove_page(GTK_NOTEBOOK(gtkWidget), item->offset());
+  state().selected = gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkWidget));
+  unlock();
+}
+
+auto pTabFrame::setEdge(Edge edge) -> void {
+  GtkPositionType type;
+  switch(edge) { default:
+  case Edge::Top: type = GTK_POS_TOP; break;
+  case Edge::Bottom: type = GTK_POS_BOTTOM; break;
+  case Edge::Left: type = GTK_POS_LEFT; break;
+  case Edge::Right: type = GTK_POS_RIGHT; break;
+  }
+  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), type);
+  setGeometry(self().geometry());
+}
+
+auto pTabFrame::setEnabled(bool enabled) -> void {
+  for(auto& item : state().items) {
+    if(auto layout = item->state.layout) {
+      if(layout->self()) layout->self()->setEnabled(layout->enabled(true));
+    }
+  }
+  pWidget::setEnabled(enabled);
+}
+
+auto pTabFrame::setFont(const string& font) -> void {
+  for(auto n : range(tabs.size())) {
+    pFont::setFont(tabs[n].title, font);
+    if(auto layout = state().items[n]->state.layout) {
+      if(layout->self()) layout->self()->setFont(layout->font(true));
+    }
+  }
+}
+
+auto pTabFrame::setGeometry(Geometry geometry) -> void {
+  pWidget::setGeometry(geometry);
+
+  geometry.setPosition(0, 0);
+  if(state().edge == Edge::Top || state().edge == Edge::Bottom) {
+    geometry.setWidth(geometry.width() - 6);
+    geometry.setHeight(geometry.height() - (15 + _tabHeight()));
+  } else {
+    geometry.setWidth(geometry.width() - (17 + _tabWidth()));
+    geometry.setHeight(geometry.height() - 6);
+  }
+  for(auto& item : state().items) {
+    if(item->state.layout) item->state.layout->setGeometry(geometry);
+  }
+}
+
+auto pTabFrame::setItemClosable(unsigned position, bool closable) -> void {
+  _synchronizeTab(position);
+}
+
+auto pTabFrame::setItemIcon(unsigned position, const image& icon) -> void {
+  _synchronizeTab(position);
+}
+
+auto pTabFrame::setItemLayout(unsigned position, shared_pointer<mLayout> layout) -> void {
+//if(layout->self()) layout->self()->setParent();
+}
+
+auto pTabFrame::setItemMovable(unsigned position, bool movable) -> void {
+  lock();
+  gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(gtkWidget), tabs[position].child, movable);
+  unlock();
+}
+
+auto pTabFrame::setItemSelected(unsigned position) -> void {
+  lock();
+  gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkWidget), position);
+  unlock();
+}
+
+auto pTabFrame::setItemText(unsigned position, const string& text) -> void {
+  _synchronizeTab(position);
+}
+
+auto pTabFrame::setVisible(bool visible) -> void {
+  for(auto& item : state().items) {
+    if(auto layout = item->state.layout) {
+      if(layout->self()) {
+        layout->self()->setVisible(layout->visible(true));
+      }
+    }
+  }
+  pWidget::setVisible(visible);
+}
+
+auto pTabFrame::_synchronizeLayout() -> void {
+  unsigned position = 0;
+  for(auto& item : state().items) {
+    if(auto layout = item->state.layout) {
+      if(layout->self()) {
+        layout->self()->setVisible(layout->visible(true) && position == state().selected);
+      }
+    }
+    position++;
+  }
+}
+
+auto pTabFrame::_synchronizeTab(unsigned position) -> void {
+  auto& item = state().items[position];
+  auto& tab = tabs[position];
+  gtk_widget_set_visible(tab.close, item->closable());
+  if(auto copy = item->state.icon) {
+    unsigned size = pFont::size(self().font(true), " ").height();
+    copy.scale(size, size);
+    auto pixbuf = CreatePixbuf(copy);
+    gtk_image_set_from_pixbuf(GTK_IMAGE(tab.image), pixbuf);
+  } else {
+    gtk_image_clear(GTK_IMAGE(tab.image));
+  }
+  string text = {
+    item->state.icon && item->state.text ? " " : "",
+    item->state.text,
+    item->state.text && item->state.closable ? " " : ""
+  };
+  gtk_label_set_text(GTK_LABEL(tab.title), text);
+}
+
+//compute the height of the tallest tab for child layout geometry calculations
+auto pTabFrame::_tabHeight() -> unsigned {
+  signed height = 1;
+
+  for(auto n : range(self().items())) {
+    height = max(height, tabs[n].image->allocation.height);
+    height = max(height, tabs[n].title->allocation.height);
+    if(!state().items[n]->closable()) continue;
+    height = max(height, tabs[n].close->allocation.height);
+  }
+
+  return height;
+}
+
+auto pTabFrame::_tabWidth() -> unsigned {
+  signed width = 1;
+
+  for(auto n : range(self().items())) {
+    width = max(width, tabs[n].image->allocation.width + tabs[n].title->allocation.width +
+      (state().items[n]->closable() ? tabs[n].close->allocation.width : 0)
+    );
+  }
+
+  return width;
+}
+
+}
diff --git a/hiro/gtk/widget/tab-frame.hpp b/hiro/gtk/widget/tab-frame.hpp
new file mode 100644
index 00000000..4319fc94
--- /dev/null
+++ b/hiro/gtk/widget/tab-frame.hpp
@@ -0,0 +1,39 @@
+namespace hiro {
+
+struct pTabFrame : pWidget {
+  Declare(TabFrame, Widget)
+
+  auto append(sTabFrameItem item) -> void;
+  auto container(mWidget& widget) -> GtkWidget* override;
+  auto remove(sTabFrameItem item) -> void;
+  auto setEdge(Edge edge) -> void;
+  auto setEnabled(bool enabled) -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setGeometry(Geometry geometry) -> void override;
+  auto setItemClosable(unsigned position, bool closable) -> void;
+  auto setItemIcon(unsigned position, const image& icon) -> void;
+  auto setItemLayout(unsigned position, sLayout layout) -> void;
+  auto setItemMovable(unsigned position, bool movable) -> void;
+  auto setItemSelected(unsigned position) -> void;
+  auto setItemText(unsigned position, const string& text) -> void;
+  auto setVisible(bool visible) -> void override;
+
+  auto _append(shared_pointer<mTabFrameItem> item) -> void;
+  auto _remove(shared_pointer<mTabFrameItem> item) -> void;
+  auto _synchronizeLayout() -> void;
+  auto _synchronizeTab(unsigned position) -> void;
+  auto _tabHeight() -> unsigned;
+  auto _tabWidth() -> unsigned;
+
+  struct Tab {
+    GtkWidget* child = nullptr;
+    GtkWidget* container = nullptr;
+    GtkWidget* layout = nullptr;
+    GtkWidget* image = nullptr;
+    GtkWidget* title = nullptr;
+    GtkWidget* close = nullptr;
+  };
+  vector<Tab> tabs;
+};
+
+}
diff --git a/hiro/gtk/widget/text-edit.cpp b/hiro/gtk/widget/text-edit.cpp
new file mode 100644
index 00000000..758b782b
--- /dev/null
+++ b/hiro/gtk/widget/text-edit.cpp
@@ -0,0 +1,100 @@
+namespace hiro {
+
+static auto TextEdit_change(GtkTextBuffer* textBuffer, pTextEdit* p) -> void {
+  if(!p->locked()) p->self().doChange();
+}
+
+static auto TextEdit_move(GObject* object, GParamSpec* spec, pTextEdit* p) -> void {
+  int position = 0;
+  g_object_get(p->textBuffer, "cursor-position", &position, nullptr);
+
+  if(p->state().cursorPosition != position) {
+    p->state().cursorPosition = position;
+    if(!p->locked()) p->self().doMove();
+  }
+}
+
+auto pTextEdit::construct() -> void {
+  gtkWidget = gtk_scrolled_window_new(0, 0);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
+
+  subWidget = gtk_text_view_new();
+  gtk_widget_show(subWidget);
+  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_WORD_CHAR);
+  gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
+
+  textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
+
+  setBackgroundColor(state().backgroundColor);
+  setEditable(state().editable);
+  setForegroundColor(state().foregroundColor);
+  setText(state().text);
+  setWordWrap(state().wordWrap);
+
+  g_signal_connect(G_OBJECT(textBuffer), "changed", G_CALLBACK(TextEdit_change), (gpointer)this);
+  g_signal_connect(G_OBJECT(textBuffer), "notify::cursor-position", G_CALLBACK(TextEdit_move), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pTextEdit::destruct() -> void {
+  state().text = text();
+  gtk_widget_destroy(subWidget);
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pTextEdit::focused() const -> bool {
+  return GTK_WIDGET_HAS_FOCUS(subWidget);
+}
+
+auto pTextEdit::setBackgroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pTextEdit::setCursorPosition(unsigned position) -> void {
+  lock();
+  GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
+  GtkTextIter iter;
+  gtk_text_buffer_get_end_iter(textBuffer, &iter);
+  gtk_text_iter_set_offset(&iter, min(position, gtk_text_iter_get_offset(&iter)));
+  gtk_text_buffer_place_cursor(textBuffer, &iter);
+  gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark);
+  unlock();
+}
+
+auto pTextEdit::setEditable(bool editable) -> void {
+  gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), editable);
+}
+
+auto pTextEdit::setForegroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pTextEdit::setText(const string& text) -> void {
+  lock();
+  textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
+  gtk_text_buffer_set_text(textBuffer, text, -1);
+  unlock();
+}
+
+auto pTextEdit::setWordWrap(bool wordWrap) -> void {
+  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget),
+    wordWrap ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS,
+    GTK_POLICY_ALWAYS);
+}
+
+auto pTextEdit::text() const -> string {
+  GtkTextIter start, end;
+  gtk_text_buffer_get_start_iter(textBuffer, &start);
+  gtk_text_buffer_get_end_iter(textBuffer, &end);
+  char* temp = gtk_text_buffer_get_text(textBuffer, &start, &end, true);
+  string text = temp;
+  g_free(temp);
+  return text;
+}
+
+}
diff --git a/hiro/gtk/widget/text-edit.hpp b/hiro/gtk/widget/text-edit.hpp
new file mode 100644
index 00000000..5d04e346
--- /dev/null
+++ b/hiro/gtk/widget/text-edit.hpp
@@ -0,0 +1,19 @@
+namespace hiro {
+
+struct pTextEdit : pWidget {
+  Declare(TextEdit, Widget)
+
+  auto focused() const -> bool override;
+  auto setBackgroundColor(Color color) -> void;
+  auto setCursorPosition(unsigned position) -> void;
+  auto setEditable(bool editable) -> void;
+  auto setForegroundColor(Color color) -> void;
+  auto setText(const string& text) -> void;
+  auto setWordWrap(bool wordWrap) -> void;
+  auto text() const -> string;
+
+  GtkWidget* subWidget = nullptr;
+  GtkTextBuffer* textBuffer = nullptr;
+};
+
+}
diff --git a/hiro/gtk/widget/tree-view-item.cpp b/hiro/gtk/widget/tree-view-item.cpp
new file mode 100644
index 00000000..3648e9db
--- /dev/null
+++ b/hiro/gtk/widget/tree-view-item.cpp
@@ -0,0 +1,82 @@
+namespace hiro {
+
+auto pTreeViewItem::construct() -> void {
+}
+
+auto pTreeViewItem::destruct() -> void {
+}
+
+//
+
+auto pTreeViewItem::append(sTreeViewItem item) -> void {
+  if(auto parentWidget = _parentWidget()) {
+    gtk_tree_store_append(parentWidget->gtkTreeStore, &item->self()->gtkIter, &gtkIter);
+    item->setChecked(item->checked());
+    item->setIcon(item->icon());
+    item->setText(item->text());
+  }
+}
+
+auto pTreeViewItem::remove(sTreeViewItem item) -> void {
+  if(auto parentWidget = _parentWidget()) {
+    gtk_tree_store_remove(parentWidget->gtkTreeStore, &item->self()->gtkIter);
+  }
+}
+
+auto pTreeViewItem::setChecked(bool checked) -> void {
+  if(auto parentWidget = _parentWidget()) {
+    gtk_tree_store_set(parentWidget->gtkTreeStore, &gtkIter, 0, checked, -1);
+  }
+}
+
+auto pTreeViewItem::setFocused() -> void {
+  if(auto parentWidget = _parentWidget()) {
+    GtkTreePath* path = gtk_tree_path_new_from_string(self().path().transform("/", ":"));
+    gtk_tree_view_set_cursor(parentWidget->gtkTreeView, path, nullptr, false);
+    gtk_tree_view_scroll_to_cell(parentWidget->gtkTreeView, path, nullptr, true, 0.5, 0.0);
+    gtk_tree_path_free(path);
+  }
+}
+
+auto pTreeViewItem::setIcon(const image& icon) -> void {
+  if(auto parentWidget = _parentWidget()) {
+    if(icon) {
+      auto pixbuf = CreatePixbuf(icon, true);
+      gtk_tree_store_set(parentWidget->gtkTreeStore, &gtkIter, 1, pixbuf, -1);
+    } else {
+      gtk_tree_store_set(parentWidget->gtkTreeStore, &gtkIter, 1, nullptr, -1);
+    }
+  }
+}
+
+auto pTreeViewItem::setSelected() -> void {
+  if(auto parentWidget = _parentWidget()) {
+    parentWidget->lock();
+    //in order to select an item, it must first be visible
+    auto gtkPath = gtk_tree_model_get_path(parentWidget->gtkTreeModel, &gtkIter);
+    gtk_tree_view_expand_to_path(parentWidget->gtkTreeView, gtkPath);
+    gtk_tree_path_free(gtkPath);
+    gtk_tree_selection_select_iter(parentWidget->gtkTreeSelection, &gtkIter);
+    parentWidget->unlock();
+  }
+}
+
+auto pTreeViewItem::setText(const string& text) -> void {
+  if(auto parentWidget = _parentWidget()) {
+    gtk_tree_store_set(parentWidget->gtkTreeStore, &gtkIter, 2, (const char*)text, -1);
+  }
+}
+
+//
+
+auto pTreeViewItem::_parentItem() -> pTreeViewItem* {
+  if(auto parentItem = self().parentTreeViewItem()) return parentItem->self();
+  return nullptr;
+}
+
+auto pTreeViewItem::_parentWidget() -> pTreeView* {
+  if(auto parentWidget = self().parentTreeView(true)) return parentWidget->self();
+  return nullptr;
+}
+
+}
diff --git a/hiro/gtk/widget/tree-view-item.hpp b/hiro/gtk/widget/tree-view-item.hpp
new file mode 100644
index 00000000..224d1197
--- /dev/null
+++ b/hiro/gtk/widget/tree-view-item.hpp
@@ -0,0 +1,20 @@
+namespace hiro {
+
+struct pTreeViewItem : pObject {
+  Declare(TreeViewItem, Object)
+
+  auto append(sTreeViewItem item) -> void;
+  auto remove(sTreeViewItem item) -> void;
+  auto setChecked(bool checked) -> void;
+  auto setFocused() -> void;
+  auto setIcon(const image& icon) -> void;
+  auto setSelected() -> void;
+  auto setText(const string& text) -> void;
+
+  auto _parentItem() -> pTreeViewItem*;
+  auto _parentWidget() -> pTreeView*;
+
+  GtkTreeIter gtkIter;
+};
+
+}
diff --git a/hiro/gtk/widget/tree-view.cpp b/hiro/gtk/widget/tree-view.cpp
new file mode 100644
index 00000000..fc895a40
--- /dev/null
+++ b/hiro/gtk/widget/tree-view.cpp
@@ -0,0 +1,162 @@
+namespace hiro {
+
+static auto TreeView_activate(GtkTreeView*, GtkTreePath* gtkPath, GtkTreeViewColumn*, pTreeView* p) -> void { p->_activatePath(gtkPath); }
+static auto TreeView_buttonEvent(GtkTreeView*, GdkEventButton* gdkEvent, pTreeView* p) -> signed { return p->_buttonEvent(gdkEvent); }
+static auto TreeView_change(GtkTreeSelection*, pTreeView* p) -> void { p->_updateSelected(); }
+static auto TreeView_context(GtkTreeView*, pTreeView* p) -> void { p->self().doContext(); }
+static auto TreeView_toggle(GtkCellRendererToggle*, char* path, pTreeView* p) -> void { p->_togglePath(path); }
+
+auto pTreeView::construct() -> void {
+  gtkWidget = gtk_scrolled_window_new(0, 0);
+  gtkScrolledWindow = GTK_SCROLLED_WINDOW(gtkWidget);
+  gtk_scrolled_window_set_policy(gtkScrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type(gtkScrolledWindow, GTK_SHADOW_ETCHED_IN);
+
+  gtkTreeStore = gtk_tree_store_new(3, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING);
+  gtkTreeModel = GTK_TREE_MODEL(gtkTreeStore);
+
+  gtkWidgetChild = gtk_tree_view_new_with_model(gtkTreeModel);
+  gtkTreeView = GTK_TREE_VIEW(gtkWidgetChild);
+  gtkTreeSelection = gtk_tree_view_get_selection(gtkTreeView);
+  gtk_tree_view_set_headers_visible(gtkTreeView, false);
+  gtk_container_add(GTK_CONTAINER(gtkWidget), gtkWidgetChild);
+  gtk_widget_show(gtkWidgetChild);
+
+  gtkTreeViewColumn = gtk_tree_view_column_new();
+
+  gtkCellToggle = gtk_cell_renderer_toggle_new();
+  gtk_tree_view_column_pack_start(gtkTreeViewColumn, gtkCellToggle, false);
+  gtk_tree_view_column_set_attributes(gtkTreeViewColumn, gtkCellToggle, "active", 0, nullptr);
+  gtk_cell_renderer_set_visible(gtkCellToggle, state().checkable);
+
+  gtkCellPixbuf = gtk_cell_renderer_pixbuf_new();
+  gtk_tree_view_column_pack_start(gtkTreeViewColumn, gtkCellPixbuf, false);
+  gtk_tree_view_column_set_attributes(gtkTreeViewColumn, gtkCellPixbuf, "pixbuf", 1, nullptr);
+
+  gtkCellText = gtk_cell_renderer_text_new();
+  gtk_tree_view_column_pack_start(gtkTreeViewColumn, gtkCellText, true);
+  gtk_tree_view_column_set_attributes(gtkTreeViewColumn, gtkCellText, "text", 2, nullptr);
+
+  gtk_tree_view_append_column(gtkTreeView, gtkTreeViewColumn);
+  gtk_tree_view_set_search_column(gtkTreeView, 2);
+
+  setBackgroundColor(state().backgroundColor);
+  setForegroundColor(state().foregroundColor);
+
+  g_signal_connect(G_OBJECT(gtkWidgetChild), "button-press-event", G_CALLBACK(TreeView_buttonEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidgetChild), "button-release-event", G_CALLBACK(TreeView_buttonEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidgetChild), "popup-menu", G_CALLBACK(TreeView_context), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidgetChild), "row-activated", G_CALLBACK(TreeView_activate), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkTreeSelection), "changed", G_CALLBACK(TreeView_change), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(TreeView_toggle), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pTreeView::destruct() -> void {
+  gtk_widget_destroy(gtkWidgetChild);
+  gtk_widget_destroy(gtkWidget);
+}
+
+//
+
+auto pTreeView::append(sTreeViewItem item) -> void {
+  gtk_tree_store_append(gtkTreeStore, &item->self()->gtkIter, nullptr);
+  item->setChecked(item->checked());
+  item->setIcon(item->icon());
+  item->setText(item->text());
+}
+
+auto pTreeView::collapse() -> void {
+  gtk_tree_view_collapse_all(gtkTreeView);
+}
+
+auto pTreeView::expand() -> void {
+  gtk_tree_view_expand_all(gtkTreeView);
+}
+
+auto pTreeView::remove(sTreeViewItem item) -> void {
+  gtk_tree_store_remove(gtkTreeStore, &item->self()->gtkIter);
+}
+
+auto pTreeView::reset() -> void {
+  gtk_tree_view_set_model(gtkTreeView, nullptr);
+  gtkTreeStore = gtk_tree_store_new(3, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING);
+  gtkTreeModel = GTK_TREE_MODEL(gtkTreeStore);
+  gtk_tree_view_set_model(gtkTreeView, gtkTreeModel);
+}
+
+auto pTreeView::setBackgroundColor(Color color) -> void {
+  auto gdkColor = CreateColor(color);
+  gtk_widget_modify_base(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pTreeView::setCheckable(bool checkable) -> void {
+  gtk_cell_renderer_set_visible(gtkCellToggle, checkable);
+}
+
+auto pTreeView::setForegroundColor(Color color) -> void {
+  auto gdkColor = CreateColor(color);
+  gtk_widget_modify_text(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+//
+
+auto pTreeView::_activatePath(GtkTreePath* gtkPath) -> void {
+  char* path = gtk_tree_path_to_string(gtkPath);
+  if(auto item = self().item(string{path}.transform(":", "/"))) {
+    if(!locked()) self().doActivate();
+  }
+  g_free(path);
+}
+
+auto pTreeView::_buttonEvent(GdkEventButton* gdkEvent) -> signed {
+  GtkTreePath* gtkPath = nullptr;
+  gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, &gtkPath, nullptr, nullptr, nullptr);
+
+  if(gdkEvent->type == GDK_BUTTON_PRESS) {
+    //detect when the empty space of the GtkTreeView is clicked; and clear the selection
+    if(gtkPath == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
+      gtk_tree_selection_unselect_all(gtkTreeSelection);
+      state().selectedPath.reset();
+      self().doChange();
+      return true;
+    }
+  }
+
+  if(gdkEvent->type == GDK_BUTTON_RELEASE && gdkEvent->button == 3) {
+    //handle right-click context menu
+    //have to detect on button release instead of press; as GTK+ does not update new selection prior to press event
+    self().doContext();
+    return false;
+  }
+
+  return false;
+}
+
+auto pTreeView::_togglePath(string path) -> void {
+  if(auto item = self().item(path.transform(":", "/"))) {
+    bool checked = !item->checked();
+    gtk_tree_store_set(gtkTreeStore, &item->self()->gtkIter, 0, checked, -1);
+    item->state.checked = checked;
+    if(!locked()) self().doToggle(item);
+  }
+}
+
+auto pTreeView::_updateSelected() -> void {
+  GtkTreeIter iter;
+  if(gtk_tree_selection_get_selected(gtkTreeSelection, &gtkTreeModel, &iter)) {
+    char* gtkPath = gtk_tree_model_get_string_from_iter(gtkTreeModel, &iter);
+    string path = string{gtkPath}.transform(":", "/");
+    g_free(gtkPath);
+    if(state().selectedPath != path) {
+      state().selectedPath = path;
+      if(!locked()) self().doChange();
+    }
+  } else if(state().selectedPath) {
+    state().selectedPath.reset();
+    if(!locked()) self().doChange();
+  }
+}
+
+}
diff --git a/hiro/gtk/widget/tree-view.hpp b/hiro/gtk/widget/tree-view.hpp
new file mode 100644
index 00000000..e3abe189
--- /dev/null
+++ b/hiro/gtk/widget/tree-view.hpp
@@ -0,0 +1,32 @@
+namespace hiro {
+
+struct pTreeView : pWidget {
+  Declare(TreeView, Widget)
+
+  auto append(sTreeViewItem item) -> void;
+  auto collapse() -> void;
+  auto expand() -> void;
+  auto remove(sTreeViewItem item) -> void;
+  auto reset() -> void;
+  auto setBackgroundColor(Color color) -> void;
+  auto setCheckable(bool checkable) -> void;
+  auto setForegroundColor(Color color) -> void;
+
+  auto _activatePath(GtkTreePath* gtkPath) -> void;
+  auto _buttonEvent(GdkEventButton* gdkEvent) -> signed;
+  auto _togglePath(string path) -> void;
+  auto _updateSelected() -> void;
+
+  GtkScrolledWindow* gtkScrolledWindow = nullptr;
+  GtkWidget* gtkWidgetChild = nullptr;
+  GtkTreeStore* gtkTreeStore = nullptr;
+  GtkTreeModel* gtkTreeModel = nullptr;
+  GtkTreeSelection* gtkTreeSelection = nullptr;
+  GtkTreeView* gtkTreeView = nullptr;
+  GtkTreeViewColumn* gtkTreeViewColumn = nullptr;
+  GtkCellRenderer* gtkCellToggle = nullptr;
+  GtkCellRenderer* gtkCellPixbuf = nullptr;
+  GtkCellRenderer* gtkCellText = nullptr;
+};
+
+}
diff --git a/hiro/gtk/widget/vertical-scroller.cpp b/hiro/gtk/widget/vertical-scroller.cpp
new file mode 100644
index 00000000..0bd8ef86
--- /dev/null
+++ b/hiro/gtk/widget/vertical-scroller.cpp
@@ -0,0 +1,41 @@
+namespace hiro {
+
+static auto VerticalScroller_change(GtkRange* gtkRange, pVerticalScroller* p) -> void {
+  auto position = (unsigned)gtk_range_get_value(gtkRange);
+  if(p->state().position == position) return;
+  p->state().position = position;
+  if(!p->locked()) p->self().doChange();
+}
+
+auto pVerticalScroller::construct() -> void {
+  gtkWidget = gtk_vscrollbar_new(0);
+
+  setLength(state().length);
+  setPosition(state().position);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalScroller_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pVerticalScroller::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pVerticalScroller::minimumSize() const -> Size {
+  return {20, 0};
+}
+
+auto pVerticalScroller::setLength(unsigned length) -> void {
+  lock();
+  length += length == 0;
+  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
+  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
+  unlock();
+}
+
+auto pVerticalScroller::setPosition(unsigned position) -> void {
+  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
+}
+
+}
diff --git a/hiro/gtk/widget/vertical-scroller.hpp b/hiro/gtk/widget/vertical-scroller.hpp
new file mode 100644
index 00000000..e4333c90
--- /dev/null
+++ b/hiro/gtk/widget/vertical-scroller.hpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+struct pVerticalScroller : pWidget {
+  Declare(VerticalScroller, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setLength(unsigned length) -> void;
+  auto setPosition(unsigned position) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/vertical-slider.cpp b/hiro/gtk/widget/vertical-slider.cpp
new file mode 100644
index 00000000..25fed744
--- /dev/null
+++ b/hiro/gtk/widget/vertical-slider.cpp
@@ -0,0 +1,40 @@
+namespace hiro {
+
+static auto VerticalSlider_change(GtkRange* gtkRange, pVerticalSlider* p) -> void {
+  auto position = (unsigned)gtk_range_get_value(gtkRange);
+  if(p->state().position == position) return;
+  p->state().position = position;
+  if(!p->locked()) p->self().doChange();
+}
+
+auto pVerticalSlider::construct() -> void {
+  gtkWidget = gtk_vscale_new_with_range(0, 100, 1);
+  gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
+
+  setLength(state().length);
+  setPosition(state().position);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pVerticalSlider::destruct() -> void {
+  gtk_widget_destroy(gtkWidget);
+}
+
+auto pVerticalSlider::minimumSize() const -> Size {
+  return {20, 0};
+}
+
+auto pVerticalSlider::setLength(unsigned length) -> void {
+  length += length == 0;
+  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
+  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
+}
+
+auto pVerticalSlider::setPosition(unsigned position) -> void {
+  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
+}
+
+}
diff --git a/hiro/gtk/widget/vertical-slider.hpp b/hiro/gtk/widget/vertical-slider.hpp
new file mode 100644
index 00000000..58995857
--- /dev/null
+++ b/hiro/gtk/widget/vertical-slider.hpp
@@ -0,0 +1,11 @@
+namespace hiro {
+
+struct pVerticalSlider : pWidget {
+  Declare(VerticalSlider, Widget)
+
+  auto minimumSize() const -> Size;
+  auto setLength(unsigned length) -> void;
+  auto setPosition(unsigned position) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/viewport.cpp b/hiro/gtk/widget/viewport.cpp
new file mode 100644
index 00000000..f6a27c94
--- /dev/null
+++ b/hiro/gtk/widget/viewport.cpp
@@ -0,0 +1,78 @@
+namespace hiro {
+
+static auto Viewport_dropEvent(GtkWidget* widget, GdkDragContext* context, signed x, signed y,
+GtkSelectionData* data, unsigned type, unsigned timestamp, pViewport* p) -> void {
+  if(!p->state().droppable) return;
+  lstring paths = DropPaths(data);
+  if(paths.empty()) return;
+  p->self().doDrop(paths);
+}
+
+static auto Viewport_mouseLeave(GtkWidget* widget, GdkEventButton* event, pViewport* p) -> signed {
+  p->self().doMouseLeave();
+  return true;
+}
+
+static auto Viewport_mouseMove(GtkWidget* widget, GdkEventButton* event, pViewport* p) -> signed {
+  p->self().doMouseMove({(signed)event->x, (signed)event->y});
+  return true;
+}
+
+static auto Viewport_mousePress(GtkWidget* widget, GdkEventButton* event, pViewport* p) -> signed {
+  switch(event->button) {
+  case 1: p->self().doMousePress(Mouse::Button::Left); break;
+  case 2: p->self().doMousePress(Mouse::Button::Middle); break;
+  case 3: p->self().doMousePress(Mouse::Button::Right); break;
+  }
+  return true;
+}
+
+static auto Viewport_mouseRelease(GtkWidget* widget, GdkEventButton* event, pViewport* p) -> signed {
+  switch(event->button) {
+  case 1: p->self().doMouseRelease(Mouse::Button::Left); break;
+  case 2: p->self().doMouseRelease(Mouse::Button::Middle); break;
+  case 3: p->self().doMouseRelease(Mouse::Button::Right); break;
+  }
+  return true;
+}
+
+auto pViewport::construct() -> void {
+  gtkWidget = gtk_drawing_area_new();
+  gtk_widget_add_events(gtkWidget,
+    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
+
+  GdkColor color;
+  color.pixel = 0;
+  color.red = 0;
+  color.green = 0;
+  color.blue = 0;
+  gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, &color);
+
+  setDroppable(state().droppable);
+
+  g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Viewport_mousePress), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Viewport_dropEvent), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this);
+  g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Viewport_mouseMove), (gpointer)this);
+
+  pWidget::construct();
+}
+
+auto pViewport::destruct() -> void {
+  if(gtkWidget) gtk_widget_destroy(gtkWidget), gtkWidget = nullptr;
+  gtkParent = nullptr;
+}
+
+auto pViewport::handle() const -> uintptr_t {
+  return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget));
+}
+
+auto pViewport::setDroppable(bool droppable) -> void {
+  if(droppable) {
+    gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
+    gtk_drag_dest_add_uri_targets(gtkWidget);
+  }
+}
+
+}
diff --git a/hiro/gtk/widget/viewport.hpp b/hiro/gtk/widget/viewport.hpp
new file mode 100644
index 00000000..137d0b01
--- /dev/null
+++ b/hiro/gtk/widget/viewport.hpp
@@ -0,0 +1,10 @@
+namespace hiro {
+
+struct pViewport : pWidget {
+  Declare(Viewport, Widget)
+
+  auto handle() const -> uintptr_t;
+  auto setDroppable(bool droppable) -> void;
+};
+
+}
diff --git a/hiro/gtk/widget/widget.cpp b/hiro/gtk/widget/widget.cpp
new file mode 100644
index 00000000..6c03f128
--- /dev/null
+++ b/hiro/gtk/widget/widget.cpp
@@ -0,0 +1,51 @@
+namespace hiro {
+
+auto pWidget::construct() -> void {
+  if(!gtkWidget) return;
+  if(auto window = self().parentWindow(true)) {
+    if(window->self()) window->self()->_append(self());
+    setFont(self().font(true));
+    setVisible(self().visible(true));
+  }
+}
+
+auto pWidget::destruct() -> void {
+}
+
+auto pWidget::container(mWidget& widget) -> GtkWidget* {
+  return nullptr;
+}
+
+auto pWidget::focused() const -> bool {
+  if(!gtkWidget) return false;
+  return GTK_WIDGET_HAS_FOCUS(gtkWidget);
+}
+
+auto pWidget::setEnabled(bool enabled) -> void {
+  if(!gtkWidget) return;
+  gtk_widget_set_sensitive(gtkWidget, enabled);
+}
+
+auto pWidget::setFocused() -> void {
+  if(!gtkWidget) return;
+  gtk_widget_grab_focus(gtkWidget);
+}
+
+auto pWidget::setFont(const string& font) -> void {
+  if(!gtkWidget) return;
+  return pFont::setFont(gtkWidget, font);
+}
+
+auto pWidget::setGeometry(Geometry geometry) -> void {
+  if(!gtkWidget) return;
+  if(gtkParent) gtk_fixed_move(GTK_FIXED(gtkParent), gtkWidget, geometry.x(), geometry.y());
+  gtk_widget_set_size_request(gtkWidget, max(1, geometry.width()), max(1, geometry.height()));
+  self().doSize();
+}
+
+auto pWidget::setVisible(bool visible) -> void {
+  if(!gtkWidget) return;
+  gtk_widget_set_visible(gtkWidget, visible);
+}
+
+}
diff --git a/hiro/gtk/widget/widget.hpp b/hiro/gtk/widget/widget.hpp
new file mode 100644
index 00000000..244361eb
--- /dev/null
+++ b/hiro/gtk/widget/widget.hpp
@@ -0,0 +1,18 @@
+namespace hiro {
+
+struct pWidget : pSizable {
+  Declare(Widget, Sizable)
+
+  virtual auto container(mWidget& widget) -> GtkWidget*;
+  virtual auto focused() const -> bool override;
+  auto setEnabled(bool enabled) -> void override;
+  virtual auto setFocused() -> void override;
+  auto setFont(const string& font) -> void override;
+  auto setGeometry(Geometry geometry) -> void override;
+  auto setVisible(bool visible) -> void override;
+
+  GtkWidget* gtkWidget = nullptr;
+  GtkWidget* gtkParent = nullptr;
+};
+
+}
diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp
new file mode 100644
index 00000000..11de8a97
--- /dev/null
+++ b/hiro/gtk/window.cpp
@@ -0,0 +1,400 @@
+namespace hiro {
+
+static auto Window_close(GtkWidget* widget, GdkEvent* event, pWindow* p) -> signed {
+  if(p->state().onClose) {
+    p->self().doClose();
+  } else {
+    p->self().setVisible(false);
+  }
+  if(p->state().modal && !p->pObject::state().visible) p->self().setModal(false);
+  return true;
+}
+
+static auto Window_expose(GtkWidget* widget, GdkEvent* event, pWindow* p) -> signed {
+  if(auto color = p->state().backgroundColor) {
+    cairo_t* context = gdk_cairo_create(widget->window);
+
+    double red   = (double)color.red()   / 255.0;
+    double green = (double)color.green() / 255.0;
+    double blue  = (double)color.blue()  / 255.0;
+    double alpha = (double)color.alpha() / 255.0;
+
+    if(gdk_screen_is_composited(gdk_screen_get_default())
+    && gdk_screen_get_rgba_colormap(gdk_screen_get_default())
+    ) {
+      cairo_set_source_rgba(context, red, green, blue, alpha);
+    } else {
+      cairo_set_source_rgb(context, red, green, blue);
+    }
+
+    cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
+    cairo_paint(context);
+    cairo_destroy(context);
+  }
+  return false;
+}
+
+static auto Window_configure(GtkWidget* widget, GdkEvent* event, pWindow* p) -> signed {
+  if(!gtk_widget_get_realized(p->widget)) return false;
+  if(!p->pObject::state().visible) return false;
+  GdkWindow* gdkWindow = gtk_widget_get_window(widget);
+
+  GdkRectangle border, client;
+  gdk_window_get_frame_extents(gdkWindow, &border);
+  gdk_window_get_geometry(gdkWindow, nullptr, nullptr, &client.width, &client.height, nullptr);
+  gdk_window_get_origin(gdkWindow, &client.x, &client.y);
+
+  if(!p->state().fullScreen) {
+    //update geometry settings
+    settings->geometry.frameX = client.x - border.x;
+    settings->geometry.frameY = client.y - border.y;
+    settings->geometry.frameWidth = border.width - client.width;
+    settings->geometry.frameHeight = border.height - client.height;
+    settings->save();
+  }
+
+  Geometry geometry = {
+    client.x,
+    client.y + p->_menuHeight(),
+    client.width,
+    client.height - p->_menuHeight() - p->_statusHeight()
+  };
+
+  //move
+  if(geometry.x() != p->state().geometry.x() || geometry.y() != p->state().geometry.y()) {
+    if(!p->state().fullScreen) {
+      p->state().geometry.setX(geometry.x());
+      p->state().geometry.setY(geometry.y());
+    }
+    if(!p->locked()) p->self().doMove();
+  }
+
+  //size
+  if(geometry.width() != p->state().geometry.width() || geometry.height() != p->state().geometry.height()) {
+    p->onSizePending = true;
+  }
+
+  return false;
+}
+
+static auto Window_drop(GtkWidget* widget, GdkDragContext* context, signed x, signed y,
+GtkSelectionData* data, unsigned type, unsigned timestamp, pWindow* p) -> void {
+  if(!p->state().droppable) return;
+  lstring paths = DropPaths(data);
+  if(paths.empty()) return;
+  p->self().doDrop(paths);
+}
+
+static auto Window_keyPress(GtkWidget* widget, GdkEventKey* event, pWindow* p) -> signed {
+  if(auto key = pKeyboard::_translate(event->keyval)) {
+    p->self().doKeyPress(key);
+  }
+  return false;
+}
+
+static auto Window_keyRelease(GtkWidget* widget, GdkEventKey* event, pWindow* p) -> signed {
+  if(auto key = pKeyboard::_translate(event->keyval)) {
+    p->self().doKeyRelease(key);
+  }
+  return false;
+}
+
+static auto Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, pWindow* p) -> void {
+  //size-allocate sent from gtk_fixed_move(); detect if layout unchanged and return
+  if(allocation->width == p->lastAllocation.width
+  && allocation->height == p->lastAllocation.height) return;
+
+  p->state().geometry.setWidth(allocation->width);
+  p->state().geometry.setHeight(allocation->height);
+
+  if(auto& layout = p->state().layout) {
+    layout->setGeometry(p->self().geometry().setPosition(0, 0));
+  }
+
+  if(!p->locked() && p->onSizePending) {
+    p->onSizePending = false;
+    p->self().doSize();
+  }
+
+  p->lastAllocation = *allocation;
+}
+
+static auto Window_sizeRequest(GtkWidget* widget, GtkRequisition* requisition, pWindow* p) -> void {
+  requisition->width  = p->state().geometry.width();
+  requisition->height = p->state().geometry.height();
+}
+
+auto pWindow::construct() -> void {
+  lastAllocation.width  = 0;
+  lastAllocation.height = 0;
+  onSizePending = false;
+
+  widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_resizable(GTK_WINDOW(widget), true);
+
+  //if program was given a name, try and set the window taskbar icon from one of the pixmaps folders
+  if(!Application::state.name);
+  else if(_setIcon({userpath(), ".local/share/icons/"}));
+  else if(_setIcon("/usr/local/share/pixmaps/"));
+  else if(_setIcon("/usr/share/pixmaps/"));
+
+  GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
+  if(!colormap) colormap = gdk_screen_get_rgb_colormap(gdk_screen_get_default());
+  if(colormap) gtk_widget_set_colormap(widget, colormap);
+
+  gtk_widget_set_app_paintable(widget, true);
+  gtk_widget_add_events(widget, GDK_CONFIGURE);
+
+  menuContainer = gtk_vbox_new(false, 0);
+  gtk_container_add(GTK_CONTAINER(widget), menuContainer);
+  gtk_widget_show(menuContainer);
+
+  gtkMenu = gtk_menu_bar_new();
+  gtk_box_pack_start(GTK_BOX(menuContainer), gtkMenu, false, false, 0);
+
+  formContainer = gtk_fixed_new();
+  gtk_box_pack_start(GTK_BOX(menuContainer), formContainer, true, true, 0);
+  gtk_widget_show(formContainer);
+
+  statusContainer = gtk_event_box_new();
+  gtkStatus = gtk_statusbar_new();
+  gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(gtkStatus), true);
+  gtk_container_add(GTK_CONTAINER(statusContainer), gtkStatus);
+  gtk_box_pack_start(GTK_BOX(menuContainer), statusContainer, false, false, 0);
+  gtk_widget_show(statusContainer);
+
+  setBackgroundColor(state().backgroundColor);
+  setDroppable(state().droppable);
+  setGeometry(state().geometry);
+  setResizable(state().resizable);
+  setTitle(state().title);
+
+  g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)this);
+  g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)this);
+  g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)this);
+  g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_drop), (gpointer)this);
+  g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPress), (gpointer)this);
+  g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyRelease), (gpointer)this);
+  g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)this);
+  g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)this);
+}
+
+auto pWindow::destruct() -> void {
+}
+
+auto pWindow::append(shared_pointer<mMenuBar> menuBar) -> void {
+}
+
+auto pWindow::append(shared_pointer<mStatusBar> statusBar) -> void {
+  _setStatusEnabled(statusBar->enabled(true));
+  _setStatusFont(statusBar->font(true));
+  _setStatusText(statusBar->text());
+  _setStatusVisible(statusBar->visible(true));
+}
+
+auto pWindow::focused() const -> bool {
+  return gtk_window_is_active(GTK_WINDOW(widget));
+}
+
+auto pWindow::frameMargin() const -> Geometry {
+  if(state().fullScreen) return {
+    0, _menuHeight(),
+    0, _menuHeight() + _statusHeight()
+  };
+
+  return {
+    settings->geometry.frameX,
+    settings->geometry.frameY + _menuHeight(),
+    settings->geometry.frameWidth,
+    settings->geometry.frameHeight + _menuHeight() + _statusHeight()
+  };
+}
+
+auto pWindow::remove(shared_pointer<mMenuBar> menuBar) -> void {
+  _setMenuVisible(false);
+}
+
+auto pWindow::remove(shared_pointer<mStatusBar> statusBar) -> void {
+  _setStatusVisible(false);
+}
+
+auto pWindow::setBackgroundColor(Color color) -> void {
+  GdkColor gdkColor = CreateColor(color);
+  gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
+}
+
+auto pWindow::setDroppable(bool droppable) -> void {
+  gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
+  if(droppable) gtk_drag_dest_add_uri_targets(widget);
+}
+
+auto pWindow::setEnabled(bool enabled) -> void {
+  if(auto& menuBar = state().menuBar) {
+    if(menuBar->self()) menuBar->self()->setEnabled(menuBar->enabled(true));
+  }
+
+  if(auto& statusBar = state().statusBar) {
+    if(statusBar->self()) statusBar->self()->setEnabled(statusBar->enabled(true));
+  }
+
+  if(auto& layout = state().layout) {
+    if(layout->self()) layout->self()->setEnabled(layout->enabled(true));
+  }
+}
+
+auto pWindow::setFocused() -> void {
+  gtk_window_present(GTK_WINDOW(widget));
+}
+
+auto pWindow::setFullScreen(bool fullScreen) -> void {
+  if(fullScreen == false) {
+    gtk_window_unfullscreen(GTK_WINDOW(widget));
+  } else {
+    gtk_window_fullscreen(GTK_WINDOW(widget));
+  }
+}
+
+auto pWindow::setGeometry(Geometry geometry) -> void {
+  Geometry margin = frameMargin();
+  gtk_window_move(GTK_WINDOW(widget), geometry.x() - margin.x(), geometry.y() - margin.y());
+
+  GdkGeometry geom;
+  geom.min_width  = state().resizable ? 1 : state().geometry.width();
+  geom.min_height = state().resizable ? 1 : state().geometry.height();
+  gtk_window_set_geometry_hints(GTK_WINDOW(widget), GTK_WIDGET(widget), &geom, GDK_HINT_MIN_SIZE);
+
+  gtk_widget_set_size_request(formContainer, geometry.width(), geometry.height());
+  gtk_window_resize(GTK_WINDOW(widget), geometry.width(), geometry.height() + _menuHeight() + _statusHeight());
+}
+
+auto pWindow::setModal(bool modal) -> void {
+  if(modal) {
+    gtk_window_set_modal(GTK_WINDOW(widget), true);
+    while(state().modal) {
+      Application::processEvents();
+      if(Application::state.onMain) {
+        Application::doMain();
+      } else {
+        usleep(20 * 1000);
+      }
+    }
+    gtk_window_set_modal(GTK_WINDOW(widget), false);
+  }
+}
+
+auto pWindow::setResizable(bool resizable) -> void {
+  gtk_window_set_resizable(GTK_WINDOW(widget), resizable);
+  gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(gtkStatus), resizable);
+}
+
+auto pWindow::setTitle(const string& title) -> void {
+  gtk_window_set_title(GTK_WINDOW(widget), title ? title : " ");
+}
+
+auto pWindow::setVisible(bool visible) -> void {
+  gtk_widget_set_visible(widget, visible);
+
+  if(auto& menuBar = state().menuBar) {
+    if(menuBar->self()) menuBar->self()->setVisible(menuBar->visible(true));
+  }
+
+  if(auto& statusBar = state().statusBar) {
+    if(statusBar->self()) statusBar->self()->setVisible(statusBar->visible(true));
+  }
+
+  if(visible) {
+    if(gtk_widget_get_visible(gtkMenu)) {
+      GtkAllocation allocation;
+      gtk_widget_get_allocation(gtkMenu, &allocation);
+      settings->geometry.menuHeight = allocation.height;
+    }
+
+    if(gtk_widget_get_visible(gtkStatus)) {
+      GtkAllocation allocation;
+      gtk_widget_get_allocation(gtkStatus, &allocation);
+      settings->geometry.statusHeight = allocation.height;
+    }
+  }
+
+  if(auto& layout = state().layout) {
+    if(layout->self()) layout->self()->setVisible(layout->visible(true));
+    Application::processEvents();  //todo: this should not be necessary
+    layout->setGeometry(self().geometry().setPosition(0, 0));
+  }
+}
+
+auto pWindow::_append(mWidget& widget) -> void {
+  if(!widget.self()) return;
+  if(auto parent = widget.parentWidget(true)) {
+    if(parent->self()) widget.self()->gtkParent = parent->self()->container(widget);
+  } else {
+    widget.self()->gtkParent = formContainer;
+  }
+  gtk_fixed_put(GTK_FIXED(widget.self()->gtkParent), widget.self()->gtkWidget, 0, 0);
+}
+
+auto pWindow::_append(mMenu& menu) -> void {
+  if(menu.self()) gtk_menu_shell_append(GTK_MENU_SHELL(gtkMenu), menu.self()->widget);
+}
+
+auto pWindow::_menuHeight() const -> signed {
+  return gtk_widget_get_visible(gtkMenu) ? settings->geometry.menuHeight : 0;
+}
+
+auto pWindow::_setIcon(const string& pathname) -> bool {
+  string filename;
+
+  filename = {pathname, Application::state.name, ".svg"};
+  if(file::exists(filename)) {
+    gtk_window_set_icon_from_file(GTK_WINDOW(widget), filename, nullptr);
+    return true;
+  }
+
+  filename = {pathname, Application::state.name, ".png"};
+  if(file::exists(filename)) {
+    //maximum image size GTK+ supports is 256x256; scale image down if necessary to prevent error
+    nall::image icon(filename);
+    icon.scale(min(256u, icon.width), min(256u, icon.height), true);
+    GdkPixbuf* pixbuf = CreatePixbuf(icon);
+    gtk_window_set_icon(GTK_WINDOW(widget), pixbuf);
+    g_object_unref(G_OBJECT(pixbuf));
+    return true;
+  }
+
+  return false;
+}
+
+auto pWindow::_setMenuEnabled(bool enabled) -> void {
+  gtk_widget_set_sensitive(gtkMenu, enabled);
+}
+
+auto pWindow::_setMenuFont(const string& font) -> void {
+  pFont::setFont(gtkMenu, font);
+}
+
+auto pWindow::_setMenuVisible(bool visible) -> void {
+  gtk_widget_set_visible(gtkMenu, visible);
+}
+
+auto pWindow::_setStatusEnabled(bool enabled) -> void {
+  gtk_widget_set_sensitive(gtkStatus, enabled);
+}
+
+auto pWindow::_setStatusFont(const string& font) -> void {
+  pFont::setFont(gtkStatus, font);
+}
+
+auto pWindow::_setStatusText(const string& text) -> void {
+  gtk_statusbar_pop(GTK_STATUSBAR(gtkStatus), 1);
+  gtk_statusbar_push(GTK_STATUSBAR(gtkStatus), 1, text);
+}
+
+auto pWindow::_setStatusVisible(bool visible) -> void {
+  gtk_widget_set_visible(gtkStatus, visible);
+}
+
+auto pWindow::_statusHeight() const -> signed {
+  return gtk_widget_get_visible(gtkStatus) ? settings->geometry.statusHeight : 0;
+}
+
+}
diff --git a/hiro/gtk/window.hpp b/hiro/gtk/window.hpp
new file mode 100644
index 00000000..7f88ba8f
--- /dev/null
+++ b/hiro/gtk/window.hpp
@@ -0,0 +1,46 @@
+namespace hiro {
+
+struct pWindow : pObject {
+  Declare(Window, Object)
+
+  GtkWidget* widget = nullptr;
+  GtkWidget* menuContainer = nullptr;
+  GtkWidget* formContainer = nullptr;
+  GtkWidget* statusContainer = nullptr;
+  GtkWidget* gtkMenu = nullptr;
+  GtkWidget* gtkStatus = nullptr;
+  GtkAllocation lastAllocation = {0};
+  bool onSizePending = false;
+
+  auto append(shared_pointer<mMenuBar> menuBar) -> void;
+  auto append(shared_pointer<mStatusBar> statusBar) -> void;
+  auto focused() const -> bool override;
+  auto frameMargin() const -> Geometry;
+  auto remove(shared_pointer<mMenuBar> menuBar) -> void;
+  auto remove(shared_pointer<mStatusBar> statusBar) -> void;
+  auto setBackgroundColor(Color color) -> void;
+  auto setDroppable(bool droppable) -> void;
+  auto setEnabled(bool enabled) -> void override;
+  auto setFocused() -> void override;
+  auto setFullScreen(bool fullScreen) -> void;
+  auto setGeometry(Geometry geometry) -> void;
+  auto setModal(bool modal) -> void;
+  auto setResizable(bool resizable) -> void;
+  auto setTitle(const string& title) -> void;
+  auto setVisible(bool visible) -> void override;
+
+  auto _append(mWidget& widget) -> void;
+  auto _append(mMenu& menu) -> void;
+  auto _menuHeight() const -> signed;
+  auto _setIcon(const string& basename) -> bool;
+  auto _setMenuEnabled(bool enabled) -> void;
+  auto _setMenuFont(const string& font) -> void;
+  auto _setMenuVisible(bool visible) -> void;
+  auto _setStatusEnabled(bool enabled) -> void;
+  auto _setStatusFont(const string& font) -> void;
+  auto _setStatusText(const string& text) -> void;
+  auto _setStatusVisible(bool visible) -> void;
+  auto _statusHeight() const -> signed;
+};
+
+}
diff --git a/hiro/hiro.cpp b/hiro/hiro.cpp
new file mode 100644
index 00000000..ab89294f
--- /dev/null
+++ b/hiro/hiro.cpp
@@ -0,0 +1,7 @@
+#ifndef HIRO_CPP
+#define HIRO_CPP
+
+#include "core/core.cpp"
+#include "extension/extension.cpp"
+
+#endif
diff --git a/hiro/hiro.hpp b/hiro/hiro.hpp
new file mode 100644
index 00000000..8adb9a69
--- /dev/null
+++ b/hiro/hiro.hpp
@@ -0,0 +1,16 @@
+/* hiro
+ * author: byuu
+ * license: ISC
+ *
+ * hiro is a cross-platform GUI toolkit
+ * it provides a consistent, minimal API wrapper to: Windows, Cocoa, GTK+ and Qt
+ * it also provides a reference wrapper for terminal applications with optional UIs
+ */
+
+#ifndef HIRO_HPP
+#define HIRO_HPP
+
+#include "core/core.hpp"
+#include "extension/extension.hpp"
+
+#endif
diff --git a/phoenix/qt/action/action.cpp b/hiro/qt/action/action.cpp
similarity index 100%
rename from phoenix/qt/action/action.cpp
rename to hiro/qt/action/action.cpp
diff --git a/phoenix/qt/action/check-item.cpp b/hiro/qt/action/check-item.cpp
similarity index 100%
rename from phoenix/qt/action/check-item.cpp
rename to hiro/qt/action/check-item.cpp
diff --git a/phoenix/qt/action/item.cpp b/hiro/qt/action/item.cpp
similarity index 100%
rename from phoenix/qt/action/item.cpp
rename to hiro/qt/action/item.cpp
diff --git a/phoenix/qt/action/menu.cpp b/hiro/qt/action/menu.cpp
similarity index 96%
rename from phoenix/qt/action/menu.cpp
rename to hiro/qt/action/menu.cpp
index efe5188a..0b258fa3 100644
--- a/phoenix/qt/action/menu.cpp
+++ b/hiro/qt/action/menu.cpp
@@ -18,7 +18,7 @@ void pMenu::remove(Action& action) {
   if(dynamic_cast<Menu*>(&action)) {
     //QMenu::removeMenu() does not exist
     qtMenu->clear();
-    for(auto &action : menu.state.action) append(action);
+    for(auto& action : menu.state.action) append(action);
   } else if(dynamic_cast<Separator*>(&action)) {
     qtMenu->removeAction(((Separator&)action).p.qtAction);
   } else if(dynamic_cast<Item*>(&action)) {
diff --git a/phoenix/qt/action/radio-item.cpp b/hiro/qt/action/radio-item.cpp
similarity index 89%
rename from phoenix/qt/action/radio-item.cpp
rename to hiro/qt/action/radio-item.cpp
index e7c9974b..0dc16084 100644
--- a/phoenix/qt/action/radio-item.cpp
+++ b/hiro/qt/action/radio-item.cpp
@@ -1,13 +1,13 @@
 namespace phoenix {
 
 void pRadioItem::setChecked() {
-  locked = true;
+  lock();
   for(auto& item : radioItem.state.group) {
     bool checkState = item.p.qtAction == qtAction;
     item.state.checked = checkState;
     item.p.qtAction->setChecked(checkState);
   }
-  locked = false;
+  unlock();
 }
 
 void pRadioItem::setGroup(const group<RadioItem>& group) {
@@ -35,7 +35,7 @@ void pRadioItem::destructor() {
 void pRadioItem::onActivate() {
   if(!radioItem.state.checked) {
     setChecked();
-    if(!locked && radioItem.onActivate) radioItem.onActivate();
+    if(!locked() && radioItem.onActivate) radioItem.onActivate();
   }
 }
 
diff --git a/phoenix/qt/action/separator.cpp b/hiro/qt/action/separator.cpp
similarity index 100%
rename from phoenix/qt/action/separator.cpp
rename to hiro/qt/action/separator.cpp
diff --git a/phoenix/qt/application.cpp b/hiro/qt/application.cpp
similarity index 100%
rename from phoenix/qt/application.cpp
rename to hiro/qt/application.cpp
diff --git a/phoenix/qt/browser-window.cpp b/hiro/qt/browser-window.cpp
similarity index 100%
rename from phoenix/qt/browser-window.cpp
rename to hiro/qt/browser-window.cpp
diff --git a/phoenix/qt/desktop.cpp b/hiro/qt/desktop.cpp
similarity index 100%
rename from phoenix/qt/desktop.cpp
rename to hiro/qt/desktop.cpp
diff --git a/phoenix/qt/font.cpp b/hiro/qt/font.cpp
similarity index 93%
rename from phoenix/qt/font.cpp
rename to hiro/qt/font.cpp
index 3c6fe572..58542f67 100644
--- a/phoenix/qt/font.cpp
+++ b/hiro/qt/font.cpp
@@ -23,9 +23,7 @@ Size pFont::size(string font, string text) {
 }
 
 QFont pFont::create(string description) {
-  lstring part;
-  part.split<2>(",", description);
-  for(auto& item : part) item.trim(" ");
+  lstring part = description.split<2>(",").strip();
 
   string family = "Sans";
   unsigned size = 8u;
diff --git a/phoenix/qt/header.hpp b/hiro/qt/header.hpp
similarity index 100%
rename from phoenix/qt/header.hpp
rename to hiro/qt/header.hpp
diff --git a/phoenix/gtk/keyboard.cpp b/hiro/qt/keyboard.cpp
similarity index 100%
rename from phoenix/gtk/keyboard.cpp
rename to hiro/qt/keyboard.cpp
diff --git a/phoenix/qt/message-window.cpp b/hiro/qt/message-window.cpp
similarity index 100%
rename from phoenix/qt/message-window.cpp
rename to hiro/qt/message-window.cpp
diff --git a/phoenix/qt/monitor.cpp b/hiro/qt/monitor.cpp
similarity index 100%
rename from phoenix/qt/monitor.cpp
rename to hiro/qt/monitor.cpp
diff --git a/phoenix/qt/mouse.cpp b/hiro/qt/mouse.cpp
similarity index 100%
rename from phoenix/qt/mouse.cpp
rename to hiro/qt/mouse.cpp
diff --git a/phoenix/qt/platform.cpp b/hiro/qt/platform.cpp
similarity index 95%
rename from phoenix/qt/platform.cpp
rename to hiro/qt/platform.cpp
index 4547e362..849bd5b4 100644
--- a/phoenix/qt/platform.cpp
+++ b/hiro/qt/platform.cpp
@@ -13,6 +13,7 @@
 #include "font.cpp"
 #include "timer.cpp"
 #include "window.cpp"
+#include "popup-menu.cpp"
 
 #include "action/action.cpp"
 #include "action/menu.cpp"
@@ -32,6 +33,7 @@
 #include "widget/hex-edit.cpp"
 #include "widget/horizontal-scroller.cpp"
 #include "widget/horizontal-slider.cpp"
+#include "widget/icon-view.cpp"
 #include "widget/label.cpp"
 #include "widget/line-edit.cpp"
 #include "widget/list-view.cpp"
diff --git a/phoenix/qt/platform.moc b/hiro/qt/platform.moc
similarity index 90%
rename from phoenix/qt/platform.moc
rename to hiro/qt/platform.moc
index ccf22d32..7ff83e9b 100644
--- a/phoenix/qt/platform.moc
+++ b/hiro/qt/platform.moc
@@ -1,8 +1,7 @@
 /****************************************************************************
 ** Meta object code from reading C++ file 'platform.moc.hpp'
 **
-** Created: Fri Feb 14 13:37:55 2014
-**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.5)
 **
 ** WARNING! All changes made in this file will be lost!
 *****************************************************************************/
@@ -10,7 +9,7 @@
 #if !defined(Q_MOC_OUTPUT_REVISION)
 #error "The header file 'platform.moc.hpp' doesn't include <QObject>."
 #elif Q_MOC_OUTPUT_REVISION != 63
-#error "This file was generated using the moc from 4.8.2. It"
+#error "This file was generated using the moc from 4.8.5. It"
 #error "cannot be used with the include files from this version of Qt."
 #error "(The moc has changed too much.)"
 #endif
@@ -30,13 +29,13 @@ static const uint qt_meta_data_phoenix__pTimer[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      17,   16,   16,   16, 0x0a,
+      16,   29,   29,   29, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pTimer[] = {
-    "phoenix::pTimer\0\0onActivate()\0"
+    "phoenix::pTimer\0onActivate()\0\0"
 };
 
 void phoenix::pTimer::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -169,13 +168,13 @@ static const uint qt_meta_data_phoenix__pItem[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      16,   15,   15,   15, 0x0a,
+      15,   28,   28,   28, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pItem[] = {
-    "phoenix::pItem\0\0onActivate()\0"
+    "phoenix::pItem\0onActivate()\0\0"
 };
 
 void phoenix::pItem::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -245,13 +244,13 @@ static const uint qt_meta_data_phoenix__pCheckItem[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      21,   20,   20,   20, 0x0a,
+      20,   31,   31,   31, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pCheckItem[] = {
-    "phoenix::pCheckItem\0\0onToggle()\0"
+    "phoenix::pCheckItem\0onToggle()\0\0"
 };
 
 void phoenix::pCheckItem::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -321,13 +320,13 @@ static const uint qt_meta_data_phoenix__pRadioItem[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      21,   20,   20,   20, 0x0a,
+      20,   33,   33,   33, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pRadioItem[] = {
-    "phoenix::pRadioItem\0\0onActivate()\0"
+    "phoenix::pRadioItem\0onActivate()\0\0"
 };
 
 void phoenix::pRadioItem::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -397,13 +396,13 @@ static const uint qt_meta_data_phoenix__pButton[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      18,   17,   17,   17, 0x0a,
+      17,   30,   30,   30, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pButton[] = {
-    "phoenix::pButton\0\0onActivate()\0"
+    "phoenix::pButton\0onActivate()\0\0"
 };
 
 void phoenix::pButton::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -536,14 +535,14 @@ static const uint qt_meta_data_phoenix__pCheckButton[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      31,   23,   22,   22, 0x0a,
+      22,   37,   45,   45, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pCheckButton[] = {
-    "phoenix::pCheckButton\0\0checked\0"
-    "onToggle(bool)\0"
+    "phoenix::pCheckButton\0onToggle(bool)\0"
+    "checked\0\0"
 };
 
 void phoenix::pCheckButton::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -612,13 +611,13 @@ static const uint qt_meta_data_phoenix__pCheckLabel[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      22,   21,   21,   21, 0x0a,
+      21,   32,   32,   32, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pCheckLabel[] = {
-    "phoenix::pCheckLabel\0\0onToggle()\0"
+    "phoenix::pCheckLabel\0onToggle()\0\0"
 };
 
 void phoenix::pCheckLabel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -688,13 +687,13 @@ static const uint qt_meta_data_phoenix__pComboButton[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      23,   22,   22,   22, 0x0a,
+      22,   33,   33,   33, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pComboButton[] = {
-    "phoenix::pComboButton\0\0onChange()\0"
+    "phoenix::pComboButton\0onChange()\0\0"
 };
 
 void phoenix::pComboButton::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -890,13 +889,13 @@ static const uint qt_meta_data_phoenix__pHexEdit[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      19,   18,   18,   18, 0x0a,
+      18,   29,   29,   29, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pHexEdit[] = {
-    "phoenix::pHexEdit\0\0onScroll()\0"
+    "phoenix::pHexEdit\0onScroll()\0\0"
 };
 
 void phoenix::pHexEdit::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -966,13 +965,14 @@ static const uint qt_meta_data_phoenix__pHorizontalScroller[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      30,   29,   29,   29, 0x0a,
+      29,   40,   40,   40, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pHorizontalScroller[] = {
-    "phoenix::pHorizontalScroller\0\0onChange()\0"
+    "phoenix::pHorizontalScroller\0onChange()\0"
+    "\0"
 };
 
 void phoenix::pHorizontalScroller::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1042,13 +1042,14 @@ static const uint qt_meta_data_phoenix__pHorizontalSlider[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      28,   27,   27,   27, 0x0a,
+      27,   38,   38,   38, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pHorizontalSlider[] = {
-    "phoenix::pHorizontalSlider\0\0onChange()\0"
+    "phoenix::pHorizontalSlider\0onChange()\0"
+    "\0"
 };
 
 void phoenix::pHorizontalSlider::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1104,6 +1105,87 @@ int phoenix::pHorizontalSlider::qt_metacall(QMetaObject::Call _c, int _id, void
     }
     return _id;
 }
+static const uint qt_meta_data_phoenix__pIconView[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       3,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       0,       // signalCount
+
+ // slots: signature, parameters, type, tag, flags
+      19,   32,   32,   32, 0x0a,
+      33,   32,   32,   32, 0x0a,
+      44,   32,   32,   32, 0x0a,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_phoenix__pIconView[] = {
+    "phoenix::pIconView\0onActivate()\0\0"
+    "onChange()\0onContext()\0"
+};
+
+void phoenix::pIconView::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        pIconView *_t = static_cast<pIconView *>(_o);
+        switch (_id) {
+        case 0: _t->onActivate(); break;
+        case 1: _t->onChange(); break;
+        case 2: _t->onContext(); break;
+        default: ;
+        }
+    }
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData phoenix::pIconView::staticMetaObjectExtraData = {
+    0,  qt_static_metacall 
+};
+
+const QMetaObject phoenix::pIconView::staticMetaObject = {
+    { &QObject::staticMetaObject, qt_meta_stringdata_phoenix__pIconView,
+      qt_meta_data_phoenix__pIconView, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &phoenix::pIconView::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *phoenix::pIconView::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *phoenix::pIconView::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_phoenix__pIconView))
+        return static_cast<void*>(const_cast< pIconView*>(this));
+    if (!strcmp(_clname, "pWidget"))
+        return static_cast< pWidget*>(const_cast< pIconView*>(this));
+    return QObject::qt_metacast(_clname);
+}
+
+int phoenix::pIconView::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QObject::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 3)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 3;
+    }
+    return _id;
+}
 static const uint qt_meta_data_phoenix__pLineEdit[] = {
 
  // content:
@@ -1118,14 +1200,14 @@ static const uint qt_meta_data_phoenix__pLineEdit[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      20,   19,   19,   19, 0x0a,
-      33,   19,   19,   19, 0x0a,
+      19,   32,   32,   32, 0x0a,
+      33,   32,   32,   32, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pLineEdit[] = {
-    "phoenix::pLineEdit\0\0onActivate()\0"
+    "phoenix::pLineEdit\0onActivate()\0\0"
     "onChange()\0"
 };
 
@@ -1189,7 +1271,7 @@ static const uint qt_meta_data_phoenix__pListView[] = {
        6,       // revision
        0,       // classname
        0,    0, // classinfo
-       3,   14, // methods
+       6,   14, // methods
        0,    0, // properties
        0,    0, // enums/sets
        0,    0, // constructors
@@ -1197,17 +1279,22 @@ static const uint qt_meta_data_phoenix__pListView[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      20,   19,   19,   19, 0x0a,
-      38,   33,   19,   19, 0x0a,
-      65,   33,   19,   19, 0x0a,
+      19,   32,   32,   32, 0x0a,
+      33,   32,   32,   32, 0x0a,
+      44,   32,   32,   32, 0x0a,
+      56,   68,   32,   32, 0x0a,
+      75,  102,   32,   32, 0x0a,
+     107,  141,  161,   32, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pListView[] = {
-    "phoenix::pListView\0\0onActivate()\0item\0"
-    "onChange(QTreeWidgetItem*)\0"
-    "onToggle(QTreeWidgetItem*)\0"
+    "phoenix::pListView\0onActivate()\0\0"
+    "onChange()\0onContext()\0onSort(int)\0"
+    "column\0onToggle(QTreeWidgetItem*)\0"
+    "item\0calculateAlignment(double,double)\0"
+    "horizontal,vertical\0int\0"
 };
 
 void phoenix::pListView::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1217,8 +1304,12 @@ void phoenix::pListView::qt_static_metacall(QObject *_o, QMetaObject::Call _c, i
         pListView *_t = static_cast<pListView *>(_o);
         switch (_id) {
         case 0: _t->onActivate(); break;
-        case 1: _t->onChange((*reinterpret_cast< QTreeWidgetItem*(*)>(_a[1]))); break;
-        case 2: _t->onToggle((*reinterpret_cast< QTreeWidgetItem*(*)>(_a[1]))); break;
+        case 1: _t->onChange(); break;
+        case 2: _t->onContext(); break;
+        case 3: _t->onSort((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 4: _t->onToggle((*reinterpret_cast< QTreeWidgetItem*(*)>(_a[1]))); break;
+        case 5: { int _r = _t->calculateAlignment((*reinterpret_cast< double(*)>(_a[1])),(*reinterpret_cast< double(*)>(_a[2])));
+            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; }  break;
         default: ;
         }
     }
@@ -1258,9 +1349,9 @@ int phoenix::pListView::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
     if (_id < 0)
         return _id;
     if (_c == QMetaObject::InvokeMetaMethod) {
-        if (_id < 3)
+        if (_id < 6)
             qt_static_metacall(this, _c, _id, _a);
-        _id -= 3;
+        _id -= 6;
     }
     return _id;
 }
@@ -1278,13 +1369,13 @@ static const uint qt_meta_data_phoenix__pRadioLabel[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      22,   21,   21,   21, 0x0a,
+      21,   34,   34,   34, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pRadioLabel[] = {
-    "phoenix::pRadioLabel\0\0onActivate()\0"
+    "phoenix::pRadioLabel\0onActivate()\0\0"
 };
 
 void phoenix::pRadioLabel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1354,13 +1445,13 @@ static const uint qt_meta_data_phoenix__pRadioButton[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      23,   22,   22,   22, 0x0a,
+      22,   35,   35,   35, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pRadioButton[] = {
-    "phoenix::pRadioButton\0\0onActivate()\0"
+    "phoenix::pRadioButton\0onActivate()\0\0"
 };
 
 void phoenix::pRadioButton::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1430,14 +1521,14 @@ static const uint qt_meta_data_phoenix__pTabFrame[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      30,   20,   19,   19, 0x0a,
+      19,   33,   43,   43, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pTabFrame[] = {
-    "phoenix::pTabFrame\0\0selection\0"
-    "onChange(int)\0"
+    "phoenix::pTabFrame\0onChange(int)\0"
+    "selection\0\0"
 };
 
 void phoenix::pTabFrame::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1506,13 +1597,13 @@ static const uint qt_meta_data_phoenix__pTextEdit[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      20,   19,   19,   19, 0x0a,
+      19,   30,   30,   30, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pTextEdit[] = {
-    "phoenix::pTextEdit\0\0onChange()\0"
+    "phoenix::pTextEdit\0onChange()\0\0"
 };
 
 void phoenix::pTextEdit::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1582,13 +1673,14 @@ static const uint qt_meta_data_phoenix__pVerticalScroller[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      28,   27,   27,   27, 0x0a,
+      27,   38,   38,   38, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pVerticalScroller[] = {
-    "phoenix::pVerticalScroller\0\0onChange()\0"
+    "phoenix::pVerticalScroller\0onChange()\0"
+    "\0"
 };
 
 void phoenix::pVerticalScroller::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -1658,13 +1750,14 @@ static const uint qt_meta_data_phoenix__pVerticalSlider[] = {
        0,       // signalCount
 
  // slots: signature, parameters, type, tag, flags
-      26,   25,   25,   25, 0x0a,
+      25,   36,   36,   36, 0x0a,
 
        0        // eod
 };
 
 static const char qt_meta_stringdata_phoenix__pVerticalSlider[] = {
-    "phoenix::pVerticalSlider\0\0onChange()\0"
+    "phoenix::pVerticalSlider\0onChange()\0"
+    "\0"
 };
 
 void phoenix::pVerticalSlider::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
diff --git a/phoenix/qt/platform.moc.hpp b/hiro/qt/platform.moc.hpp
similarity index 83%
rename from phoenix/qt/platform.moc.hpp
rename to hiro/qt/platform.moc.hpp
index 7a8a5677..ac1b96b7 100644
--- a/phoenix/qt/platform.moc.hpp
+++ b/hiro/qt/platform.moc.hpp
@@ -84,9 +84,13 @@ struct pMessageWindow {
 
 struct pObject {
   Object& object;
-  bool locked;
+  signed locks = 0;
 
-  pObject(Object& object) : object(object), locked(false) {}
+  bool locked() const { return locks != 0; }
+  void lock() { locks++; }
+  void unlock() { locks--; }
+
+  pObject(Object& object) : object(object) {}
   virtual ~pObject() {}
   void constructor() {}
   void destructor() {}
@@ -138,7 +142,6 @@ public:
   void append(Widget& widget);
   Geometry frameMargin();
   bool focused();
-  Geometry geometry();
   void remove(Layout& layout);
   void remove(Menu& menu);
   void remove(Widget& widget);
@@ -164,6 +167,19 @@ public:
   void updateFrameGeometry();
 };
 
+struct pPopupMenu : public pObject {
+  PopupMenu& popupMenu;
+  QMenu* qtMenu;
+
+  void append(Action& action);
+  void remove(Action& action);
+  void setVisible();
+
+  pPopupMenu(PopupMenu& popupMenu) : pObject(popupMenu), popupMenu(popupMenu) {}
+  void constructor();
+  void destructor();
+};
+
 struct pAction : public pObject {
   Action& action;
 
@@ -304,6 +320,7 @@ public:
   QToolButton* qtButton;
 
   Size minimumSize();
+  void setBordered(bool bordered);
   void setImage(const image& image, Orientation orientation);
   void setText(string text);
 
@@ -400,12 +417,11 @@ public:
   ComboButton& comboButton;
   QComboBox* qtComboButton;
 
-  void append(string text);
+  void append();
   Size minimumSize();
   void remove(unsigned selection);
   void reset();
-  unsigned selection();
-  void setSelection(unsigned selection);
+  void setSelected(unsigned selection);
   void setText(unsigned selection, string text);
 
   pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {}
@@ -543,6 +559,42 @@ public slots:
   void onChange();
 };
 
+struct pIconView : public QObject, public pWidget {
+  Q_OBJECT
+
+public:
+  IconView& iconView;
+  struct QtListWidget : public QListWidget {
+    void resizeEvent(QResizeEvent*);
+  };
+  QtListWidget* qtIconView;
+
+  void append();
+  void remove(unsigned selection);
+  void reset();
+  void setBackgroundColor(Color color);
+  void setFlow(Orientation flow);
+  void setForegroundColor(Color color);
+  void setImage(unsigned selection, const image& image);
+  void setOrientation(Orientation orientation);
+  void setSelected(unsigned selection, bool selected);
+  void setSelected(const vector<unsigned>& selections);
+  void setSelectedAll();
+  void setSelectedNone();
+  void setSingleSelection(bool singleSelection);
+  void setText(unsigned selection, const string& text);
+
+  pIconView(IconView& iconView) : pWidget(iconView), iconView(iconView) {}
+  void constructor();
+  void destructor();
+  void orphan();
+
+public slots:
+  void onActivate();
+  void onChange();
+  void onContext();
+};
+
 struct pLabel : public pWidget {
   Label& label;
   QLabel* qtLabel;
@@ -585,22 +637,53 @@ struct pListView : public QObject, public pWidget {
 
 public:
   ListView& listView;
-  QTreeWidget* qtListView;
+  struct QtTreeWidget : public QTreeWidget {
+    pListView& self;
+    void mousePressEvent(QMouseEvent*);
+    QtTreeWidget(pListView& self);
+  };
+  struct QtTreeWidgetDelegate : public QStyledItemDelegate {
+    pListView& self;
+    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
+    QtTreeWidgetDelegate(pListView& self);
+  };
+  QtTreeWidget* qtListView;
+  QtTreeWidgetDelegate* qtListViewDelegate;
 
-  void append(const lstring& text);
-  void autoSizeColumns();
-  void remove(unsigned selection);
+  void appendColumn();
+  void appendItem();
+  void removeColumn(unsigned position);
+  void removeItem(unsigned position);
   void reset();
+  void resizeColumns();
+  void setActiveColumn(unsigned column);
   void setBackgroundColor(Color color);
   void setCheckable(bool checkable);
-  void setChecked(unsigned selection, bool checked);
+  void setChecked(unsigned position, bool checked);
+  void setChecked(const vector<unsigned>& selections);
+  void setCheckedAll();
+  void setCheckedNone();
+  void setColumnBackgroundColor(unsigned position, maybe<Color> color);
+  void setColumnEditable(unsigned position, bool editable);
+  void setColumnFont(unsigned position, maybe<string> font);
+  void setColumnForegroundColor(unsigned position, maybe<Color> color);
+  void setColumnHorizontalAlignment(unsigned position, double alignment);
+  void setColumnResizable(unsigned position, bool resizable);
+  void setColumnSortable(unsigned position, bool sortable);
+  void setColumnText(unsigned position, const string& text);
+  void setColumnVerticalAlignment(unsigned position, double alignment);
+  void setColumnVisible(unsigned position, bool visible);
+  void setColumnWidth(unsigned position, signed width);
   void setForegroundColor(Color color);
-  void setHeaderText(const lstring& text);
+  void setGridVisible(bool visible);
   void setHeaderVisible(bool visible);
-  void setImage(unsigned selection, unsigned position, const image& image);
-  void setSelected(bool selected);
-  void setSelection(unsigned selection);
-  void setText(unsigned selection, unsigned position, string text);
+  void setImage(unsigned row, unsigned column, const image& image);
+  void setSelected(unsigned position, bool selected);
+  void setSelected(const vector<unsigned>& selections);
+  void setSelectedAll();
+  void setSelectedNone();
+  void setSingleSelection(bool singleSelection);
+  void setText(unsigned row, unsigned column, string text);
 
   pListView(ListView& listView) : pWidget(listView), listView(listView) {}
   void constructor();
@@ -609,8 +692,11 @@ public:
 
 public slots:
   void onActivate();
-  void onChange(QTreeWidgetItem* item);
+  void onChange();
+  void onContext();
+  void onSort(int column);
   void onToggle(QTreeWidgetItem* item);
+  int calculateAlignment(double horizontal, double vertical);
 };
 
 struct pProgressBar : public pWidget {
@@ -679,14 +765,14 @@ public:
   TabFrame& tabFrame;
   QTabWidget* qtTabFrame;
 
-  void append(string text, const image& image);
+  void append();
   QWidget* container(Widget& widget);
   Position displacement();
   void remove(unsigned selection);
   void setEnabled(bool enabled);
   void setGeometry(Geometry geometry);
   void setImage(unsigned selection, const image& image);
-  void setSelection(unsigned selection);
+  void setSelected(unsigned selection);
   void setText(unsigned selection, string text);
   void setVisible(bool visible);
 
diff --git a/hiro/qt/popup-menu.cpp b/hiro/qt/popup-menu.cpp
new file mode 100644
index 00000000..789765c6
--- /dev/null
+++ b/hiro/qt/popup-menu.cpp
@@ -0,0 +1,46 @@
+namespace phoenix {
+
+void pPopupMenu::append(Action& action) {
+  if(dynamic_cast<Menu*>(&action)) {
+    qtMenu->addMenu(((Menu&)action).p.qtMenu);
+  } else if(dynamic_cast<Separator*>(&action)) {
+    qtMenu->addAction(((Separator&)action).p.qtAction);
+  } else if(dynamic_cast<Item*>(&action)) {
+    qtMenu->addAction(((Item&)action).p.qtAction);
+  } else if(dynamic_cast<CheckItem*>(&action)) {
+    qtMenu->addAction(((CheckItem&)action).p.qtAction);
+  } else if(dynamic_cast<RadioItem*>(&action)) {
+    qtMenu->addAction(((RadioItem&)action).p.qtAction);
+  }
+}
+
+void pPopupMenu::remove(Action& action) {
+  if(dynamic_cast<Menu*>(&action)) {
+    //QMenu::removeMenu() does not exist
+    qtMenu->clear();
+    for(auto& action : popupMenu.state.action) append(action);
+  } else if(dynamic_cast<Separator*>(&action)) {
+    qtMenu->removeAction(((Separator&)action).p.qtAction);
+  } else if(dynamic_cast<Item*>(&action)) {
+    qtMenu->removeAction(((Item&)action).p.qtAction);
+  } else if(dynamic_cast<CheckItem*>(&action)) {
+    qtMenu->removeAction(((CheckItem&)action).p.qtAction);
+  } else if(dynamic_cast<RadioItem*>(&action)) {
+    qtMenu->removeAction(((RadioItem&)action).p.qtAction);
+  }
+}
+
+void pPopupMenu::setVisible() {
+  qtMenu->popup(QCursor::pos());
+}
+
+void pPopupMenu::constructor() {
+  qtMenu = new QMenu;
+}
+
+void pPopupMenu::destructor() {
+  delete qtMenu;
+  qtMenu = nullptr;
+}
+
+}
diff --git a/phoenix/qt/settings.cpp b/hiro/qt/settings.cpp
similarity index 100%
rename from phoenix/qt/settings.cpp
rename to hiro/qt/settings.cpp
diff --git a/phoenix/qt/timer.cpp b/hiro/qt/timer.cpp
similarity index 100%
rename from phoenix/qt/timer.cpp
rename to hiro/qt/timer.cpp
diff --git a/phoenix/qt/utility.cpp b/hiro/qt/utility.cpp
similarity index 99%
rename from phoenix/qt/utility.cpp
rename to hiro/qt/utility.cpp
index 5e4f34c8..4a175807 100644
--- a/phoenix/qt/utility.cpp
+++ b/hiro/qt/utility.cpp
@@ -1,6 +1,7 @@
 namespace phoenix {
 
 static QIcon CreateIcon(const nall::image& image, bool scale = false) {
+  if(image.empty()) return QIcon();
   nall::image qtBuffer = image;
   qtBuffer.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
   if(scale) qtBuffer.scale(16, 16, Interpolation::Linear);
diff --git a/phoenix/qt/widget/button.cpp b/hiro/qt/widget/button.cpp
similarity index 84%
rename from phoenix/qt/widget/button.cpp
rename to hiro/qt/widget/button.cpp
index 32ad8463..083e5272 100644
--- a/phoenix/qt/widget/button.cpp
+++ b/hiro/qt/widget/button.cpp
@@ -13,7 +13,11 @@ Size pButton::minimumSize() {
     size.height += button.state.image.height;
   }
 
-  return {size.width + 20, size.height + 12};
+  return {size.width + (button.state.text ? 20 : 12), size.height + 12};
+}
+
+void pButton::setBordered(bool bordered) {
+  qtButton->setAutoRaise(bordered == false);
 }
 
 void pButton::setImage(const image& image, Orientation orientation) {
@@ -36,6 +40,8 @@ void pButton::constructor() {
   connect(qtButton, SIGNAL(released()), SLOT(onActivate()));
 
   pWidget::synchronizeState();
+  setBordered(button.state.bordered);
+  setImage(button.state.image, button.state.orientation);
   setText(button.state.text);
 }
 
diff --git a/phoenix/qt/widget/canvas.cpp b/hiro/qt/widget/canvas.cpp
similarity index 100%
rename from phoenix/qt/widget/canvas.cpp
rename to hiro/qt/widget/canvas.cpp
diff --git a/phoenix/qt/widget/check-button.cpp b/hiro/qt/widget/check-button.cpp
similarity index 94%
rename from phoenix/qt/widget/check-button.cpp
rename to hiro/qt/widget/check-button.cpp
index 921dc15d..6248ab51 100644
--- a/phoenix/qt/widget/check-button.cpp
+++ b/hiro/qt/widget/check-button.cpp
@@ -17,9 +17,9 @@ Size pCheckButton::minimumSize() {
 }
 
 void pCheckButton::setChecked(bool checked) {
-  locked = true;
+  lock();
   qtCheckButton->setChecked(checked);
-  locked = false;
+  unlock();
 }
 
 void pCheckButton::setImage(const image& image, Orientation orientation) {
@@ -57,7 +57,7 @@ void pCheckButton::orphan() {
 
 void pCheckButton::onToggle(bool checked) {
   checkButton.state.checked = checked;
-  if(!locked && checkButton.onToggle) checkButton.onToggle();
+  if(!locked() && checkButton.onToggle) checkButton.onToggle();
 }
 
 }
diff --git a/phoenix/qt/widget/check-label.cpp b/hiro/qt/widget/check-label.cpp
similarity index 90%
rename from phoenix/qt/widget/check-label.cpp
rename to hiro/qt/widget/check-label.cpp
index 4e88e5b6..b3246fa4 100644
--- a/phoenix/qt/widget/check-label.cpp
+++ b/hiro/qt/widget/check-label.cpp
@@ -6,9 +6,9 @@ Size pCheckLabel::minimumSize() {
 }
 
 void pCheckLabel::setChecked(bool checked) {
-  locked = true;
+  lock();
   qtCheckLabel->setChecked(checked);
-  locked = false;
+  unlock();
 }
 
 void pCheckLabel::setText(string text) {
@@ -36,7 +36,7 @@ void pCheckLabel::orphan() {
 
 void pCheckLabel::onToggle() {
   checkLabel.state.checked = qtCheckLabel->isChecked();
-  if(!locked && checkLabel.onToggle) checkLabel.onToggle();
+  if(!locked() && checkLabel.onToggle) checkLabel.onToggle();
 }
 
 }
diff --git a/phoenix/qt/widget/combo-button.cpp b/hiro/qt/widget/combo-button.cpp
similarity index 67%
rename from phoenix/qt/widget/combo-button.cpp
rename to hiro/qt/widget/combo-button.cpp
index 5b25bc79..4e7250c7 100644
--- a/phoenix/qt/widget/combo-button.cpp
+++ b/hiro/qt/widget/combo-button.cpp
@@ -1,9 +1,9 @@
 namespace phoenix {
 
-void pComboButton::append(string text) {
-  locked = true;
-  qtComboButton->addItem(QString::fromUtf8(text));
-  locked = false;
+void pComboButton::append() {
+  lock();
+  qtComboButton->addItem("");
+  unlock();
 }
 
 Size pComboButton::minimumSize() {
@@ -14,23 +14,22 @@ Size pComboButton::minimumSize() {
 }
 
 void pComboButton::remove(unsigned selection) {
-  locked = true;
+  lock();
   qtComboButton->removeItem(selection);
-  locked = false;
-
-  if(selection == comboButton.state.selection) comboButton.setSelection(0);
+  if(selection == comboButton.state.selection) comboButton[0].setSelected();
+  unlock();
 }
 
 void pComboButton::reset() {
-  locked = true;
+  lock();
   while(qtComboButton->count()) qtComboButton->removeItem(0);
-  locked = false;
+  unlock();
 }
 
-void pComboButton::setSelection(unsigned selection) {
-  locked = true;
+void pComboButton::setSelected(unsigned selection) {
+  lock();
   qtComboButton->setCurrentIndex(selection);
-  locked = false;
+  unlock();
 }
 
 void pComboButton::setText(unsigned selection, string text) {
@@ -43,10 +42,11 @@ void pComboButton::constructor() {
 
   pWidget::synchronizeState();
   unsigned selection = comboButton.state.selection;
-  locked = true;
-  for(auto& text : comboButton.state.text) append(text);
-  locked = false;
-  comboButton.setSelection(selection);
+  for(unsigned n = 0; n < comboButton.count(); n++) {
+    append();
+    setText(n, comboButton.state.text[n]);
+  }
+  comboButton[selection].setSelected();
 }
 
 void pComboButton::destructor() {
@@ -61,7 +61,7 @@ void pComboButton::orphan() {
 
 void pComboButton::onChange() {
   comboButton.state.selection = qtComboButton->currentIndex();
-  if(!locked && comboButton.onChange) comboButton.onChange();
+  if(!locked() && comboButton.onChange) comboButton.onChange();
 }
 
 }
diff --git a/phoenix/qt/widget/console.cpp b/hiro/qt/widget/console.cpp
similarity index 67%
rename from phoenix/qt/widget/console.cpp
rename to hiro/qt/widget/console.cpp
index 22a3d813..5173a126 100644
--- a/phoenix/qt/widget/console.cpp
+++ b/hiro/qt/widget/console.cpp
@@ -7,9 +7,16 @@ void pConsole::reset() {
 }
 
 void pConsole::setBackgroundColor(Color color) {
+  QPalette palette = qtConsole->palette();
+  palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
+  qtConsole->setPalette(palette);
+  qtConsole->setAutoFillBackground(true);
 }
 
 void pConsole::setForegroundColor(Color color) {
+  QPalette palette = qtConsole->palette();
+  palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
+  qtConsole->setPalette(palette);
 }
 
 void pConsole::setPrompt(string prompt) {
diff --git a/phoenix/qt/widget/frame.cpp b/hiro/qt/widget/frame.cpp
similarity index 100%
rename from phoenix/qt/widget/frame.cpp
rename to hiro/qt/widget/frame.cpp
diff --git a/phoenix/qt/widget/hex-edit.cpp b/hiro/qt/widget/hex-edit.cpp
similarity index 95%
rename from phoenix/qt/widget/hex-edit.cpp
rename to hiro/qt/widget/hex-edit.cpp
index f0883e36..4a465886 100644
--- a/phoenix/qt/widget/hex-edit.cpp
+++ b/hiro/qt/widget/hex-edit.cpp
@@ -1,6 +1,10 @@
 namespace phoenix {
 
 void pHexEdit::setBackgroundColor(Color color) {
+  QPalette palette = qtHexEdit->palette();
+  palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
+  qtHexEdit->setPalette(palette);
+  qtHexEdit->setAutoFillBackground(true);
 }
 
 void pHexEdit::setColumns(unsigned columns) {
@@ -8,6 +12,9 @@ void pHexEdit::setColumns(unsigned columns) {
 }
 
 void pHexEdit::setForegroundColor(Color color) {
+  QPalette palette = qtHexEdit->palette();
+  palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
+  qtHexEdit->setPalette(palette);
 }
 
 void pHexEdit::setLength(unsigned length) {
@@ -18,9 +25,9 @@ void pHexEdit::setLength(unsigned length) {
 }
 
 void pHexEdit::setOffset(unsigned offset) {
-  locked = true;
+  lock();
   qtScroll->setSliderPosition(hexEdit.state.offset / hexEdit.state.columns);
-  locked = false;
+  unlock();
   update();
 }
 
@@ -254,7 +261,7 @@ void pHexEdit::scrollTo(signed position) {
 }
 
 void pHexEdit::onScroll() {
-  if(locked) return;
+  if(locked()) return;
   unsigned offset = qtScroll->sliderPosition();
   hexEdit.state.offset = offset * hexEdit.state.columns;
   update();
diff --git a/phoenix/qt/widget/horizontal-scroller.cpp b/hiro/qt/widget/horizontal-scroller.cpp
similarity index 100%
rename from phoenix/qt/widget/horizontal-scroller.cpp
rename to hiro/qt/widget/horizontal-scroller.cpp
diff --git a/phoenix/qt/widget/horizontal-slider.cpp b/hiro/qt/widget/horizontal-slider.cpp
similarity index 100%
rename from phoenix/qt/widget/horizontal-slider.cpp
rename to hiro/qt/widget/horizontal-slider.cpp
diff --git a/hiro/qt/widget/icon-view.cpp b/hiro/qt/widget/icon-view.cpp
new file mode 100644
index 00000000..d829cd0d
--- /dev/null
+++ b/hiro/qt/widget/icon-view.cpp
@@ -0,0 +1,148 @@
+namespace phoenix {
+
+void pIconView::append() {
+  lock();
+  auto item = new QListWidgetItem(qtIconView);
+  unlock();
+}
+
+void pIconView::remove(unsigned selection) {
+  lock();
+  if(auto item = qtIconView->item(selection)) {
+    delete item;
+  }
+  unlock();
+}
+
+void pIconView::reset() {
+  lock();
+  qtIconView->clear();
+  unlock();
+}
+
+void pIconView::setBackgroundColor(Color color) {
+  QPalette palette = qtIconView->palette();
+  palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
+  qtIconView->setPalette(palette);
+  qtIconView->setAutoFillBackground(true);
+}
+
+void pIconView::setFlow(Orientation flow) {
+  qtIconView->setFlow(flow == Orientation::Horizontal ? QListView::LeftToRight : QListView::TopToBottom);
+  qtIconView->resize(qtIconView->size());  //adjust visibility of scroll bars
+}
+
+void pIconView::setForegroundColor(Color color) {
+  QPalette palette = qtIconView->palette();
+  palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
+  qtIconView->setPalette(palette);
+}
+
+void pIconView::setImage(unsigned selection, const image& image) {
+  if(auto item = qtIconView->item(selection)) {
+    item->setIcon(CreateIcon(image));
+  }
+}
+
+void pIconView::setOrientation(Orientation orientation) {
+  qtIconView->setViewMode(orientation == Orientation::Horizontal ? QListView::ListMode : QListView::IconMode);
+  qtIconView->setWrapping(true);
+}
+
+void pIconView::setSelected(unsigned selection, bool selected) {
+  lock();
+  if(auto item = qtIconView->item(selection)) {
+    item->setSelected(selected);
+  }
+  unlock();
+}
+
+void pIconView::setSelected(const vector<unsigned>& selections) {
+  lock();
+  qtIconView->clearSelection();
+  for(auto& selection : selections) {
+    if(auto item = qtIconView->item(selection)) {
+      item->setSelected(true);
+    }
+  }
+  unlock();
+}
+
+void pIconView::setSelectedAll() {
+  lock();
+  qtIconView->selectAll();
+  unlock();
+}
+
+void pIconView::setSelectedNone() {
+  lock();
+  qtIconView->clearSelection();
+  unlock();
+}
+
+void pIconView::setSingleSelection(bool singleSelection) {
+  qtIconView->setSelectionMode(singleSelection ? QAbstractItemView::SingleSelection : QAbstractItemView::ExtendedSelection);
+}
+
+void pIconView::setText(unsigned selection, const string& text) {
+  if(auto item = qtIconView->item(selection)) {
+    item->setText(QString::fromUtf8(text));
+  }
+}
+
+void pIconView::constructor() {
+  qtWidget = qtIconView = new QtListWidget;
+  qtIconView->setContextMenuPolicy(Qt::CustomContextMenu);
+  qtIconView->setMovement(QListView::Static);
+  qtIconView->setResizeMode(QListView::Adjust);
+  qtIconView->setSelectionRectVisible(true);
+  qtIconView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+  qtIconView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+
+  connect(qtIconView, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(onActivate()));
+  connect(qtIconView, SIGNAL(itemSelectionChanged()), SLOT(onChange()));
+  connect(qtIconView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(onContext()));
+
+  setFlow(iconView.state.flow);
+  setOrientation(iconView.state.orientation);
+  setSingleSelection(iconView.state.singleSelection);
+}
+
+void pIconView::destructor() {
+  delete qtIconView;
+  qtWidget = qtIconView = nullptr;
+}
+
+void pIconView::orphan() {
+  destructor();
+  constructor();
+}
+
+void pIconView::onActivate() {
+  if(!locked() && iconView.onActivate) iconView.onActivate();
+}
+
+void pIconView::onChange() {
+  for(auto& selected : iconView.state.selected) selected = false;
+  for(unsigned n = 0; n < qtIconView->count(); n++) {
+    if(auto item = qtIconView->item(n)) {
+      if(item->isSelected()) iconView.state.selected[n] = true;
+    }
+  }
+  if(!locked() && iconView.onChange) iconView.onChange();
+}
+
+void pIconView::onContext() {
+  if(!locked() && iconView.onContext) iconView.onContext();
+}
+
+void pIconView::QtListWidget::resizeEvent(QResizeEvent* event) {
+  //Qt::ScrollBarAsNeeded results in the scroll bar area being reserved from the icon viewport even when scroll bar is hidden
+  //this creates the appearance of an invisible gap that wastes precious screen space
+  //below code simulates a Qt::ScrollBarAsNeeded which uses the extra space when the scroll bar is hidden
+  setHorizontalScrollBarPolicy(horizontalScrollBar()->maximum() > horizontalScrollBar()->minimum() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
+  setVerticalScrollBarPolicy(verticalScrollBar()->maximum() > verticalScrollBar()->minimum() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
+  return QListWidget::resizeEvent(event);
+}
+
+}
diff --git a/phoenix/qt/widget/label.cpp b/hiro/qt/widget/label.cpp
similarity index 100%
rename from phoenix/qt/widget/label.cpp
rename to hiro/qt/widget/label.cpp
diff --git a/phoenix/qt/widget/line-edit.cpp b/hiro/qt/widget/line-edit.cpp
similarity index 76%
rename from phoenix/qt/widget/line-edit.cpp
rename to hiro/qt/widget/line-edit.cpp
index 3142c8ad..ab537608 100644
--- a/phoenix/qt/widget/line-edit.cpp
+++ b/hiro/qt/widget/line-edit.cpp
@@ -6,6 +6,10 @@ Size pLineEdit::minimumSize() {
 }
 
 void pLineEdit::setBackgroundColor(Color color) {
+  QPalette palette = qtLineEdit->palette();
+  palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
+  qtLineEdit->setPalette(palette);
+  qtLineEdit->setAutoFillBackground(true);
 }
 
 void pLineEdit::setEditable(bool editable) {
@@ -13,6 +17,9 @@ void pLineEdit::setEditable(bool editable) {
 }
 
 void pLineEdit::setForegroundColor(Color color) {
+  QPalette palette = qtLineEdit->palette();
+  palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
+  qtLineEdit->setPalette(palette);
 }
 
 void pLineEdit::setText(string text) {
diff --git a/hiro/qt/widget/list-view.cpp b/hiro/qt/widget/list-view.cpp
new file mode 100644
index 00000000..113b6e0b
--- /dev/null
+++ b/hiro/qt/widget/list-view.cpp
@@ -0,0 +1,395 @@
+namespace phoenix {
+
+void pListView::appendColumn() {
+  orphan();
+}
+
+void pListView::appendItem() {
+  lock();
+  auto item = new QTreeWidgetItem(qtListView);
+  for(unsigned column = 0; column < listView.columns(); column++) {
+    auto& state = listView.state.columns[column];
+    if(state.backgroundColor) {
+      item->setBackground(column, QColor(
+        state.backgroundColor->red, state.backgroundColor->green, state.backgroundColor->blue
+      ));
+    }
+    if(state.font) {
+      item->setFont(column, pFont::create(*state.font));
+    }
+    if(state.foregroundColor) {
+      item->setForeground(column, QColor(
+        state.foregroundColor->red, state.foregroundColor->green, state.foregroundColor->blue
+      ));
+    }
+    item->setTextAlignment(column, calculateAlignment(
+      state.horizontalAlignment, state.verticalAlignment
+    ));
+  }
+  if(listView.state.checkable) item->setCheckState(0, Qt::Unchecked);
+  unlock();
+}
+
+void pListView::removeColumn(unsigned position) {
+  orphan();
+}
+
+void pListView::removeItem(unsigned position) {
+  lock();
+  if(auto item = qtListView->topLevelItem(position)) {
+    delete item;
+  }
+  unlock();
+}
+
+void pListView::reset() {
+  lock();
+  qtListView->clear();
+  unlock();
+}
+
+void pListView::resizeColumns() {
+}
+
+//todo: this doesn't work ...
+void pListView::setActiveColumn(unsigned column) {
+  if(column >= listView.columns()) return;
+  qtListView->header()->setSortIndicator(column, Qt::DescendingOrder);
+}
+
+void pListView::setBackgroundColor(Color color) {
+  QPalette palette = qtListView->palette();
+  palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
+  palette.setColor(QPalette::AlternateBase, QColor(max(0, (signed)color.red - 17), max(0, (signed)color.green - 17), max(0, (signed)color.blue - 17)));
+  qtListView->setPalette(palette);
+  qtListView->setAutoFillBackground(true);
+}
+
+void pListView::setCheckable(bool checkable) {
+  lock();
+  if(checkable) {
+    for(unsigned n = 0; n < qtListView->topLevelItemCount(); n++) {
+      if(auto item = qtListView->topLevelItem(n)) {
+        item->setCheckState(0, Qt::Unchecked);
+      }
+    }
+  }
+  unlock();
+}
+
+void pListView::setChecked(unsigned position, bool checked) {
+  lock();
+  if(auto item = qtListView->topLevelItem(position)) {
+    item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked);
+  }
+  unlock();
+}
+
+void pListView::setChecked(const vector<unsigned>& selections) {
+  lock();
+  setCheckedNone();
+  for(auto& position : selections) setChecked(position, true);
+  unlock();
+}
+
+void pListView::setCheckedAll() {
+  lock();
+  for(unsigned n = 0; n < qtListView->topLevelItemCount(); n++) {
+    if(auto item = qtListView->topLevelItem(n)) {
+      item->setCheckState(0, Qt::Checked);
+    }
+  }
+  unlock();
+}
+
+void pListView::setCheckedNone() {
+  lock();
+  for(unsigned n = 0; n < qtListView->topLevelItemCount(); n++) {
+    if(auto item = qtListView->topLevelItem(n)) {
+      item->setCheckState(0, Qt::Unchecked);
+    }
+  }
+  unlock();
+}
+
+void pListView::setColumnBackgroundColor(unsigned column, maybe<Color> color) {
+  for(unsigned row = 0; row < listView.items(); row++) {
+    if(auto item = qtListView->topLevelItem(row)) {
+      item->setBackground(column, color ? QColor(color->red, color->green, color->blue) : QBrush());
+    }
+  }
+}
+
+void pListView::setColumnEditable(unsigned column, bool editable) {
+}
+
+void pListView::setColumnFont(unsigned column, maybe<string> font) {
+  auto qtFont = pFont::create(font ? *font : widget.state.font);
+  for(unsigned row = 0; row < listView.items(); row++) {
+    if(auto item = qtListView->topLevelItem(row)) {
+      item->setFont(column, qtFont);
+    }
+  }
+}
+
+void pListView::setColumnForegroundColor(unsigned column, maybe<Color> color) {
+  for(unsigned row = 0; row < listView.items(); row++) {
+    if(auto item = qtListView->topLevelItem(row)) {
+      item->setForeground(column, color ? QColor(color->red, color->green, color->blue) : QBrush());
+    }
+  }
+}
+
+void pListView::setColumnHorizontalAlignment(unsigned column, double alignment) {
+  qtListView->headerItem()->setTextAlignment(column, calculateAlignment(alignment, 0.5));
+  for(unsigned row = 0; row < listView.items(); row++) {
+    if(auto item = qtListView->topLevelItem(row)) {
+      auto& state = listView.state.columns[column];
+      item->setTextAlignment(column, calculateAlignment(state.horizontalAlignment, state.verticalAlignment));
+    }
+  }
+}
+
+void pListView::setColumnResizable(unsigned column, bool resizable) {
+  qtListView->header()->setResizeMode(column, resizable ? QHeaderView::Interactive : QHeaderView::Fixed);
+}
+
+void pListView::setColumnSortable(unsigned column, bool sortable) {
+  bool clickable = false;
+  for(auto& column : listView.state.columns) clickable |= column.sortable;
+  qtListView->header()->setClickable(clickable);
+}
+
+void pListView::setColumnText(unsigned column, const string& text) {
+  qtListView->headerItem()->setText(column, QString::fromUtf8(text));
+}
+
+void pListView::setColumnVerticalAlignment(unsigned column, double alignment) {
+  for(unsigned row = 0; row < listView.items(); row++) {
+    if(auto item = qtListView->topLevelItem(row)) {
+      auto& state = listView.state.columns[column];
+      item->setTextAlignment(column, calculateAlignment(state.horizontalAlignment, state.verticalAlignment));
+    }
+  }
+}
+
+void pListView::setColumnVisible(unsigned column, bool visible) {
+  if(column >= listView.columns()) return;
+  qtListView->setColumnHidden(column, !visible);
+}
+
+void pListView::setColumnWidth(unsigned column, signed width) {
+  if(column >= listView.columns()) return;
+  resizeColumns();
+}
+
+void pListView::setForegroundColor(Color color) {
+  QPalette palette = qtListView->palette();
+  palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
+  qtListView->setPalette(palette);
+}
+
+void pListView::setGridVisible(bool visible) {
+  qtListView->repaint();
+}
+
+void pListView::setHeaderVisible(bool visible) {
+  qtListView->setHeaderHidden(!visible);
+  resizeColumns();
+}
+
+void pListView::setImage(unsigned row, unsigned column, const nall::image& image) {
+  if(auto item = qtListView->topLevelItem(row)) {
+    item->setIcon(column, CreateIcon(image));
+  }
+}
+
+void pListView::setSelected(unsigned position, bool selected) {
+  lock();
+  if(auto item = qtListView->topLevelItem(position)) {
+    item->setSelected(selected);
+  }
+  unlock();
+}
+
+void pListView::setSelected(const vector<unsigned>& selections) {
+  lock();
+  setSelectedNone();
+  if(selections.size()) {
+    if(auto item = qtListView->topLevelItem(selections[0])) {
+      qtListView->setCurrentItem(item);
+    }
+    for(auto& position : selections) setSelected(position, true);
+  }
+  unlock();
+}
+
+void pListView::setSelectedAll() {
+  lock();
+  qtListView->selectAll();
+  unlock();
+}
+
+void pListView::setSelectedNone() {
+  lock();
+  qtListView->clearSelection();
+  unlock();
+}
+
+void pListView::setSingleSelection(bool singleSelection) {
+  lock();
+  qtListView->setSelectionMode(singleSelection ? QAbstractItemView::SingleSelection : QAbstractItemView::ExtendedSelection);
+  unlock();
+}
+
+void pListView::setText(unsigned row, unsigned column, string text) {
+  lock();
+  if(auto item = qtListView->topLevelItem(row)) {
+    item->setText(column, QString::fromUtf8(text));
+  }
+  unlock();
+}
+
+void pListView::constructor() {
+  qtWidget = qtListView = new QtTreeWidget(*this);
+  qtListView->setAllColumnsShowFocus(true);
+  qtListView->setAlternatingRowColors(listView.columns() >= 2);
+  qtListView->setColumnCount(max(1u, listView.columns()));
+  qtListView->setContextMenuPolicy(Qt::CustomContextMenu);
+  qtListView->setRootIsDecorated(false);
+  qtListView->header()->setMovable(false);
+
+  qtListViewDelegate = new QtTreeWidgetDelegate(*this);
+  qtListView->setItemDelegate(qtListViewDelegate);
+
+  connect(qtListView, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate()));
+  connect(qtListView, SIGNAL(itemSelectionChanged()), SLOT(onChange()));
+  connect(qtListView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(onContext()));
+  connect(qtListView->header(), SIGNAL(sectionClicked(int)), SLOT(onSort(int)));
+  connect(qtListView, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(onToggle(QTreeWidgetItem*)));
+
+  pWidget::synchronizeState();
+  for(unsigned column = 0; column < listView.columns(); column++) {
+    auto& state = listView.state.columns[column];
+    setColumnBackgroundColor(column, state.backgroundColor);
+    setColumnEditable(column, state.editable);
+    setColumnFont(column, state.font);
+    setColumnForegroundColor(column, state.foregroundColor);
+  //setColumnHorizontalAlignment(column, state.horizontalAlignment);
+    setColumnResizable(column, state.resizable);
+    setColumnSortable(column, state.sortable);
+    setColumnText(column, state.text);
+  //setColumnVerticalAlignment(column, state.verticalAlignment);
+    setColumnVisible(column, state.visible);
+  //setColumnWidth(column, state.width);
+    qtListView->headerItem()->setTextAlignment(column, calculateAlignment(state.horizontalAlignment, 0.5));
+  }
+  setActiveColumn(listView.state.activeColumn);
+  setCheckable(listView.state.checkable);
+//setGridVisible(listView.state.gridVisible);
+  setHeaderVisible(listView.state.headerVisible);
+  setSingleSelection(listView.state.singleSelection);
+  for(unsigned row = 0; row < listView.items(); row++) {
+    appendItem();
+    setSelected(row, listView.state.items[row].selected);
+    if(listView.state.checkable) {
+      setChecked(row, listView.state.items[row].checked);
+    }
+    for(unsigned column = 0; column < listView.columns(); column++) {
+      setImage(row, column, listView.state.items[row].image(column, {}));
+      setText(row, column, listView.state.items[row].text(column, ""));
+    }
+  }
+  resizeColumns();
+}
+
+void pListView::destructor() {
+  delete qtListViewDelegate;
+  delete qtListView;
+  qtWidget = qtListView = nullptr;
+  qtListViewDelegate = nullptr;
+}
+
+void pListView::orphan() {
+  destructor();
+  constructor();
+}
+
+void pListView::onActivate() {
+  if(!locked() && listView.onActivate) listView.onActivate();
+}
+
+void pListView::onChange() {
+  for(auto& item : listView.state.items) item.selected = false;
+  for(unsigned position = 0; position < qtListView->topLevelItemCount(); position++) {
+    if(auto item = qtListView->topLevelItem(position)) {
+      if(item->isSelected()) listView.state.items[position].selected = true;
+    }
+  }
+  if(!locked() && listView.onChange) listView.onChange();
+}
+
+void pListView::onContext() {
+  if(!locked() && listView.onContext) listView.onContext();
+}
+
+void pListView::onSort(int column) {
+  if(column >= listView.columns()) return;
+  if(listView.state.columns[column].sortable) {
+    if(!locked() && listView.onSort) listView.onSort(column);
+  }
+}
+
+void pListView::onToggle(QTreeWidgetItem* item) {
+  maybe<unsigned> row;
+  for(unsigned position = 0; position < qtListView->topLevelItemCount(); position++) {
+    if(auto topLevelItem = qtListView->topLevelItem(position)) {
+      if(topLevelItem == item) {
+        row = position;
+        break;
+      }
+    }
+  }
+  if(row) {
+    listView.state.items[*row].checked = (item->checkState(0) == Qt::Checked);
+    if(!locked() && listView.onToggle) listView.onToggle(*row);
+  }
+}
+
+int pListView::calculateAlignment(double horizontal, double vertical) {
+  int alignment = 0;
+  if(horizontal < 0.333) alignment |= Qt::AlignLeft;
+  else if(horizontal > 0.666) alignment |= Qt::AlignRight;
+  else alignment |= Qt::AlignCenter;
+  if(vertical < 0.333) alignment |= Qt::AlignTop;
+  else if(vertical > 0.666) alignment |= Qt::AlignBottom;
+  else alignment |= Qt::AlignVCenter;
+  return alignment;
+}
+
+void pListView::QtTreeWidget::mousePressEvent(QMouseEvent* event) {
+  QTreeWidget::mousePressEvent(event);
+
+  if(event->button() == Qt::RightButton) {
+    self.onContext();
+  }
+}
+
+pListView::QtTreeWidget::QtTreeWidget(pListView& self) : self(self) {
+}
+
+void pListView::QtTreeWidgetDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
+  QStyledItemDelegate::paint(painter, option, index);
+  if(self.listView.state.gridVisible) {
+    QPen pen;
+    pen.setColor(QColor(192, 192, 192));
+    pen.setWidth(1);
+    painter->setPen(pen);
+    painter->drawRect(option.rect);
+  }
+}
+
+pListView::QtTreeWidgetDelegate::QtTreeWidgetDelegate(pListView& self) : QStyledItemDelegate(self.qtListView), self(self) {
+}
+
+}
diff --git a/phoenix/qt/widget/progress-bar.cpp b/hiro/qt/widget/progress-bar.cpp
similarity index 100%
rename from phoenix/qt/widget/progress-bar.cpp
rename to hiro/qt/widget/progress-bar.cpp
diff --git a/phoenix/qt/widget/radio-button.cpp b/hiro/qt/widget/radio-button.cpp
similarity index 94%
rename from phoenix/qt/widget/radio-button.cpp
rename to hiro/qt/widget/radio-button.cpp
index b3554b85..3ba491ab 100644
--- a/phoenix/qt/widget/radio-button.cpp
+++ b/hiro/qt/widget/radio-button.cpp
@@ -17,20 +17,20 @@ Size pRadioButton::minimumSize() {
 }
 
 void pRadioButton::setChecked() {
-  parent().locked = true;
+  parent().lock();
   for(auto& item : radioButton.state.group) {
     bool checked = &item.p == this;
     item.p.qtRadioButton->setChecked(item.state.checked = checked);
   }
-  parent().locked = false;
+  parent().unlock();
 }
 
 void pRadioButton::setGroup(const group<RadioButton>& group) {
-  parent().locked = true;
+  parent().lock();
   for(auto& item : radioButton.state.group) {
     item.p.qtRadioButton->setChecked(item.state.checked);
   }
-  parent().locked = false;
+  parent().unlock();
 }
 
 void pRadioButton::setImage(const image& image, Orientation orientation) {
@@ -74,7 +74,7 @@ void pRadioButton::orphan() {
 }
 
 void pRadioButton::onActivate() {
-  if(parent().locked) return;
+  if(parent().locked()) return;
   bool wasChecked = radioButton.state.checked;
   setChecked();
   if(!wasChecked) {
diff --git a/phoenix/qt/widget/radio-label.cpp b/hiro/qt/widget/radio-label.cpp
similarity index 91%
rename from phoenix/qt/widget/radio-label.cpp
rename to hiro/qt/widget/radio-label.cpp
index 927591a4..cdbf013f 100644
--- a/phoenix/qt/widget/radio-label.cpp
+++ b/hiro/qt/widget/radio-label.cpp
@@ -6,20 +6,20 @@ Size pRadioLabel::minimumSize() {
 }
 
 void pRadioLabel::setChecked() {
-  parent().locked = true;
+  parent().lock();
   for(auto& item : radioLabel.state.group) {
     bool checked = &item == &radioLabel;
     item.p.qtRadioLabel->setChecked(item.state.checked = checked);
   }
-  parent().locked = false;
+  parent().unlock();
 }
 
 void pRadioLabel::setGroup(const group<RadioLabel>& group) {
-  parent().locked = true;
+  parent().lock();
   for(auto& item : radioLabel.state.group) {
     item.p.qtRadioLabel->setChecked(item.state.checked);
   }
-  parent().locked = false;
+  parent().unlock();
 }
 
 void pRadioLabel::setText(string text) {
@@ -52,7 +52,7 @@ void pRadioLabel::orphan() {
 }
 
 void pRadioLabel::onActivate() {
-  if(parent().locked) return;
+  if(parent().locked()) return;
   bool wasChecked = radioLabel.state.checked;
   setChecked();
   if(!wasChecked) {
diff --git a/phoenix/qt/widget/tab-frame.cpp b/hiro/qt/widget/tab-frame.cpp
similarity index 83%
rename from phoenix/qt/widget/tab-frame.cpp
rename to hiro/qt/widget/tab-frame.cpp
index 6fb9396d..e2b393f0 100644
--- a/phoenix/qt/widget/tab-frame.cpp
+++ b/hiro/qt/widget/tab-frame.cpp
@@ -1,9 +1,7 @@
 namespace phoenix {
 
-void pTabFrame::append(string text, const image& image) {
-  unsigned selection = tabFrame.state.text.size() - 1;
-  qtTabFrame->addTab(new QWidget, QString::fromUtf8(text));
-  if(!image.empty()) setImage(selection, image);
+void pTabFrame::append() {
+  qtTabFrame->addTab(new QWidget, "");
 }
 
 QWidget* pTabFrame::container(Widget& widget) {
@@ -45,11 +43,11 @@ void pTabFrame::setImage(unsigned selection, const image& image) {
   qtTabFrame->setTabIcon(selection, CreateIcon(image));
 }
 
-void pTabFrame::setSelection(unsigned selection) {
-  locked = true;
+void pTabFrame::setSelected(unsigned selection) {
+  lock();
   qtTabFrame->setCurrentIndex(selection);
   synchronizeLayout();
-  locked = false;
+  unlock();
 }
 
 void pTabFrame::setText(unsigned selection, string text) {
@@ -67,7 +65,7 @@ void pTabFrame::constructor() {
   qtWidget = qtTabFrame = new QTabWidget;
   connect(qtTabFrame, SIGNAL(currentChanged(int)), SLOT(onChange(int)));
 
-  setSelection(tabFrame.state.selection);
+  setSelected(tabFrame.state.selection);
 }
 
 void pTabFrame::destructor() {
@@ -91,7 +89,7 @@ void pTabFrame::synchronizeLayout() {
 void pTabFrame::onChange(int selection) {
   tabFrame.state.selection = selection;
   synchronizeLayout();
-  if(!locked && tabFrame.onChange) tabFrame.onChange();
+  if(!locked() && tabFrame.onChange) tabFrame.onChange();
 }
 
 }
diff --git a/phoenix/qt/widget/text-edit.cpp b/hiro/qt/widget/text-edit.cpp
similarity index 81%
rename from phoenix/qt/widget/text-edit.cpp
rename to hiro/qt/widget/text-edit.cpp
index ec705731..8097e2cf 100644
--- a/phoenix/qt/widget/text-edit.cpp
+++ b/hiro/qt/widget/text-edit.cpp
@@ -1,6 +1,10 @@
 namespace phoenix {
 
 void pTextEdit::setBackgroundColor(Color color) {
+  QPalette palette = qtTextEdit->palette();
+  palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
+  qtTextEdit->setPalette(palette);
+  qtTextEdit->setAutoFillBackground(true);
 }
 
 void pTextEdit::setCursorPosition(unsigned position) {
@@ -15,6 +19,9 @@ void pTextEdit::setEditable(bool editable) {
 }
 
 void pTextEdit::setForegroundColor(Color color) {
+  QPalette palette = qtTextEdit->palette();
+  palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
+  qtTextEdit->setPalette(palette);
 }
 
 void pTextEdit::setText(string text) {
diff --git a/phoenix/qt/widget/vertical-scroller.cpp b/hiro/qt/widget/vertical-scroller.cpp
similarity index 100%
rename from phoenix/qt/widget/vertical-scroller.cpp
rename to hiro/qt/widget/vertical-scroller.cpp
diff --git a/phoenix/qt/widget/vertical-slider.cpp b/hiro/qt/widget/vertical-slider.cpp
similarity index 100%
rename from phoenix/qt/widget/vertical-slider.cpp
rename to hiro/qt/widget/vertical-slider.cpp
diff --git a/phoenix/qt/widget/viewport.cpp b/hiro/qt/widget/viewport.cpp
similarity index 100%
rename from phoenix/qt/widget/viewport.cpp
rename to hiro/qt/widget/viewport.cpp
diff --git a/phoenix/qt/widget/widget.cpp b/hiro/qt/widget/widget.cpp
similarity index 100%
rename from phoenix/qt/widget/widget.cpp
rename to hiro/qt/widget/widget.cpp
diff --git a/phoenix/qt/window.cpp b/hiro/qt/window.cpp
similarity index 89%
rename from phoenix/qt/window.cpp
rename to hiro/qt/window.cpp
index 8753f2ef..6c6ae89c 100644
--- a/phoenix/qt/window.cpp
+++ b/hiro/qt/window.cpp
@@ -41,16 +41,6 @@ bool pWindow::focused() {
   return qtWindow->isActiveWindow() && !qtWindow->isMinimized();
 }
 
-Geometry pWindow::geometry() {
-  if(window.state.fullScreen) {
-    unsigned menuHeight = window.state.menuVisible ? qtMenu->height() : 0;
-    unsigned statusHeight = window.state.statusVisible ? qtStatus->height() : 0;
-    QRect geometry = qtWindow->geometry();  //frameGeometry() includes frame even though it's not visible in fullscreen mode
-    return {geometry.x(), geometry.y() + menuHeight, geometry.width(), geometry.height() - menuHeight - statusHeight};
-  }
-  return window.state.geometry;
-}
-
 void pWindow::remove(Layout& layout) {
 }
 
@@ -97,10 +87,9 @@ void pWindow::setFullScreen(bool fullScreen) {
 }
 
 void pWindow::setGeometry(Geometry geometry) {
-  locked = true;
+  lock();
   Application::processEvents();
   QApplication::syncX();
-  Geometry margin = frameMargin();
 
   setResizable(window.state.resizable);
   qtWindow->move(geometry.x - frameMargin().x, geometry.y - frameMargin().y);
@@ -116,7 +105,7 @@ void pWindow::setGeometry(Geometry geometry) {
     geometry.x = geometry.y = 0;
     layout.setGeometry(geometry);
   }
-  locked = false;
+  unlock();
 }
 
 void pWindow::setMenuFont(string font) {
@@ -137,7 +126,11 @@ void pWindow::setModal(bool modal) {
     setVisible(true);
     while(window.state.modal) {
       Application::processEvents();
-      usleep(20 * 1000);
+      if(Application::main) {
+        Application::main();
+      } else {
+        usleep(20 * 1000);
+      }
     }
     qtWindow->setWindowModality(Qt::NonModal);
   }
@@ -172,13 +165,13 @@ void pWindow::setTitle(string text) {
 }
 
 void pWindow::setVisible(bool visible) {
-  locked = true;
+  lock();
   qtWindow->setVisible(visible);
   if(visible) {
     updateFrameGeometry();
     setGeometry(window.state.geometry);
   }
-  locked = false;
+  unlock();
 }
 
 void pWindow::setWidgetFont(string font) {
@@ -194,6 +187,8 @@ void pWindow::constructor() {
       qtWindow->setWindowIcon(QIcon(string{"/usr/share/pixmaps/", applicationState.name, ".png"}));
     } else if(file::exists({"/usr/local/share/pixmaps/", applicationState.name, ".png"})) {
       qtWindow->setWindowIcon(QIcon(string{"/usr/local/share/pixmaps/", applicationState.name, ".png"}));
+    } else if(file::exists({userpath(), ".local/share/icons/", applicationState.name, ".png"})) {
+      qtWindow->setWindowIcon(QIcon(string{userpath(), ".local/share/icons/", applicationState.name, ".png"}));
     }
   }
 
@@ -263,14 +258,12 @@ void pWindow::QtWindow::closeEvent(QCloseEvent* event) {
 }
 
 void pWindow::QtWindow::moveEvent(QMoveEvent* event) {
-  if(self.locked == false && self.window.state.fullScreen == false && self.qtWindow->isVisible() == true) {
+  if(!self.locked() && self.window.state.fullScreen == false && self.qtWindow->isVisible() == true) {
     self.window.state.geometry.x += event->pos().x() - event->oldPos().x();
     self.window.state.geometry.y += event->pos().y() - event->oldPos().y();
   }
 
-  if(self.locked == false) {
-    if(self.window.onMove) self.window.onMove();
-  }
+  if(!self.locked() && self.window.onMove) self.window.onMove();
 }
 
 void pWindow::QtWindow::dragEnterEvent(QDragEnterEvent* event) {
@@ -296,20 +289,18 @@ void pWindow::QtWindow::keyReleaseEvent(QKeyEvent* event) {
 }
 
 void pWindow::QtWindow::resizeEvent(QResizeEvent*) {
-  if(self.locked == false && self.window.state.fullScreen == false && self.qtWindow->isVisible() == true) {
+  if(!self.locked() && self.window.state.fullScreen == false && self.qtWindow->isVisible() == true) {
     self.window.state.geometry.width = self.qtContainer->geometry().width();
     self.window.state.geometry.height = self.qtContainer->geometry().height();
   }
 
   for(auto& layout : self.window.state.layout) {
-    Geometry geometry = self.geometry();
+    Geometry geometry = self.window.state.geometry;
     geometry.x = geometry.y = 0;
     layout.setGeometry(geometry);
   }
 
-  if(self.locked == false) {
-    if(self.window.onSize) self.window.onSize();
-  }
+  if(!self.locked() && self.window.onSize) self.window.onSize();
 }
 
 QSize pWindow::QtWindow::sizeHint() const {
diff --git a/phoenix/reference/action/action.cpp b/hiro/reference/action/action.cpp
similarity index 100%
rename from phoenix/reference/action/action.cpp
rename to hiro/reference/action/action.cpp
diff --git a/phoenix/reference/action/action.hpp b/hiro/reference/action/action.hpp
similarity index 100%
rename from phoenix/reference/action/action.hpp
rename to hiro/reference/action/action.hpp
diff --git a/phoenix/reference/action/check-item.cpp b/hiro/reference/action/check-item.cpp
similarity index 100%
rename from phoenix/reference/action/check-item.cpp
rename to hiro/reference/action/check-item.cpp
diff --git a/phoenix/reference/action/check-item.hpp b/hiro/reference/action/check-item.hpp
similarity index 100%
rename from phoenix/reference/action/check-item.hpp
rename to hiro/reference/action/check-item.hpp
diff --git a/phoenix/reference/action/item.cpp b/hiro/reference/action/item.cpp
similarity index 100%
rename from phoenix/reference/action/item.cpp
rename to hiro/reference/action/item.cpp
diff --git a/phoenix/reference/action/item.hpp b/hiro/reference/action/item.hpp
similarity index 100%
rename from phoenix/reference/action/item.hpp
rename to hiro/reference/action/item.hpp
diff --git a/phoenix/reference/action/menu.cpp b/hiro/reference/action/menu.cpp
similarity index 100%
rename from phoenix/reference/action/menu.cpp
rename to hiro/reference/action/menu.cpp
diff --git a/phoenix/reference/action/menu.hpp b/hiro/reference/action/menu.hpp
similarity index 100%
rename from phoenix/reference/action/menu.hpp
rename to hiro/reference/action/menu.hpp
diff --git a/phoenix/reference/action/radio-item.cpp b/hiro/reference/action/radio-item.cpp
similarity index 100%
rename from phoenix/reference/action/radio-item.cpp
rename to hiro/reference/action/radio-item.cpp
diff --git a/phoenix/reference/action/radio-item.hpp b/hiro/reference/action/radio-item.hpp
similarity index 100%
rename from phoenix/reference/action/radio-item.hpp
rename to hiro/reference/action/radio-item.hpp
diff --git a/phoenix/reference/action/separator.cpp b/hiro/reference/action/separator.cpp
similarity index 100%
rename from phoenix/reference/action/separator.cpp
rename to hiro/reference/action/separator.cpp
diff --git a/phoenix/reference/action/separator.hpp b/hiro/reference/action/separator.hpp
similarity index 100%
rename from phoenix/reference/action/separator.hpp
rename to hiro/reference/action/separator.hpp
diff --git a/phoenix/reference/application.cpp b/hiro/reference/application.cpp
similarity index 100%
rename from phoenix/reference/application.cpp
rename to hiro/reference/application.cpp
diff --git a/phoenix/reference/application.hpp b/hiro/reference/application.hpp
similarity index 100%
rename from phoenix/reference/application.hpp
rename to hiro/reference/application.hpp
diff --git a/phoenix/reference/browser-window.cpp b/hiro/reference/browser-window.cpp
similarity index 100%
rename from phoenix/reference/browser-window.cpp
rename to hiro/reference/browser-window.cpp
diff --git a/phoenix/reference/browser-window.hpp b/hiro/reference/browser-window.hpp
similarity index 100%
rename from phoenix/reference/browser-window.hpp
rename to hiro/reference/browser-window.hpp
diff --git a/phoenix/reference/desktop.cpp b/hiro/reference/desktop.cpp
similarity index 100%
rename from phoenix/reference/desktop.cpp
rename to hiro/reference/desktop.cpp
diff --git a/phoenix/reference/desktop.hpp b/hiro/reference/desktop.hpp
similarity index 100%
rename from phoenix/reference/desktop.hpp
rename to hiro/reference/desktop.hpp
diff --git a/phoenix/reference/font.cpp b/hiro/reference/font.cpp
similarity index 100%
rename from phoenix/reference/font.cpp
rename to hiro/reference/font.cpp
diff --git a/phoenix/reference/font.hpp b/hiro/reference/font.hpp
similarity index 100%
rename from phoenix/reference/font.hpp
rename to hiro/reference/font.hpp
diff --git a/phoenix/reference/header.hpp b/hiro/reference/header.hpp
similarity index 100%
rename from phoenix/reference/header.hpp
rename to hiro/reference/header.hpp
diff --git a/phoenix/reference/keyboard.cpp b/hiro/reference/keyboard.cpp
similarity index 100%
rename from phoenix/reference/keyboard.cpp
rename to hiro/reference/keyboard.cpp
diff --git a/phoenix/reference/keyboard.hpp b/hiro/reference/keyboard.hpp
similarity index 100%
rename from phoenix/reference/keyboard.hpp
rename to hiro/reference/keyboard.hpp
diff --git a/phoenix/reference/message-window.cpp b/hiro/reference/message-window.cpp
similarity index 100%
rename from phoenix/reference/message-window.cpp
rename to hiro/reference/message-window.cpp
diff --git a/phoenix/reference/message-window.hpp b/hiro/reference/message-window.hpp
similarity index 100%
rename from phoenix/reference/message-window.hpp
rename to hiro/reference/message-window.hpp
diff --git a/phoenix/reference/monitor.cpp b/hiro/reference/monitor.cpp
similarity index 100%
rename from phoenix/reference/monitor.cpp
rename to hiro/reference/monitor.cpp
diff --git a/phoenix/reference/monitor.hpp b/hiro/reference/monitor.hpp
similarity index 100%
rename from phoenix/reference/monitor.hpp
rename to hiro/reference/monitor.hpp
diff --git a/phoenix/reference/mouse.cpp b/hiro/reference/mouse.cpp
similarity index 100%
rename from phoenix/reference/mouse.cpp
rename to hiro/reference/mouse.cpp
diff --git a/phoenix/reference/mouse.hpp b/hiro/reference/mouse.hpp
similarity index 100%
rename from phoenix/reference/mouse.hpp
rename to hiro/reference/mouse.hpp
diff --git a/phoenix/reference/object.cpp b/hiro/reference/object.cpp
similarity index 100%
rename from phoenix/reference/object.cpp
rename to hiro/reference/object.cpp
diff --git a/phoenix/reference/object.hpp b/hiro/reference/object.hpp
similarity index 100%
rename from phoenix/reference/object.hpp
rename to hiro/reference/object.hpp
diff --git a/phoenix/reference/platform.cpp b/hiro/reference/platform.cpp
similarity index 97%
rename from phoenix/reference/platform.cpp
rename to hiro/reference/platform.cpp
index 2a9ff663..883b0b2d 100644
--- a/phoenix/reference/platform.cpp
+++ b/hiro/reference/platform.cpp
@@ -10,6 +10,7 @@
 #include "object.cpp"
 #include "timer.cpp"
 #include "window.cpp"
+#include "popup-menu.cpp"
 
 #include "action/action.cpp"
 #include "action/menu.cpp"
diff --git a/phoenix/reference/platform.hpp b/hiro/reference/platform.hpp
similarity index 98%
rename from phoenix/reference/platform.hpp
rename to hiro/reference/platform.hpp
index 059f7799..15cecec4 100644
--- a/phoenix/reference/platform.hpp
+++ b/hiro/reference/platform.hpp
@@ -16,6 +16,7 @@ namespace phoenix {
 #include "object.hpp"
 #include "timer.hpp"
 #include "window.hpp"
+#include "popup-menu.hpp"
 
 #include "action/action.hpp"
 #include "action/menu.hpp"
diff --git a/hiro/reference/popup-menu.cpp b/hiro/reference/popup-menu.cpp
new file mode 100644
index 00000000..1dea8c2f
--- /dev/null
+++ b/hiro/reference/popup-menu.cpp
@@ -0,0 +1,18 @@
+namespace phoenix {
+
+void pPopupMenu::append(Action& action) {
+}
+
+void pPopupMenu::remove(Action& action) {
+}
+
+void pPopupMenu::setVisible() {
+}
+
+void pPopupMenu::constructor() {
+}
+
+void pPopupMenu::destructor() {
+}
+
+}
diff --git a/hiro/reference/popup-menu.hpp b/hiro/reference/popup-menu.hpp
new file mode 100644
index 00000000..c1eb0641
--- /dev/null
+++ b/hiro/reference/popup-menu.hpp
@@ -0,0 +1,15 @@
+namespace phoenix {
+
+struct pPopupMenu : public pObject {
+  PopupMenu& popupMenu;
+
+  void append(Action& action);
+  void remove(Action& action);
+  void setVisible();
+
+  pPopupMenu(PopupMenu& popupMenu) : pObject(popupMenu), popupMenu(popupMenu) {}
+  void constructor();
+  void destructor();
+};
+
+}
diff --git a/phoenix/reference/timer.cpp b/hiro/reference/timer.cpp
similarity index 100%
rename from phoenix/reference/timer.cpp
rename to hiro/reference/timer.cpp
diff --git a/phoenix/reference/timer.hpp b/hiro/reference/timer.hpp
similarity index 100%
rename from phoenix/reference/timer.hpp
rename to hiro/reference/timer.hpp
diff --git a/phoenix/reference/widget/button.cpp b/hiro/reference/widget/button.cpp
similarity index 81%
rename from phoenix/reference/widget/button.cpp
rename to hiro/reference/widget/button.cpp
index e2a3dfbc..ad7c3362 100644
--- a/phoenix/reference/widget/button.cpp
+++ b/hiro/reference/widget/button.cpp
@@ -1,5 +1,8 @@
 namespace phoenix {
 
+void pButton::setBordered(bool bordered) {
+}
+
 void pButton::setImage(const image& image, Orientation orientation) {
 }
 
diff --git a/phoenix/reference/widget/button.hpp b/hiro/reference/widget/button.hpp
similarity index 88%
rename from phoenix/reference/widget/button.hpp
rename to hiro/reference/widget/button.hpp
index be591979..c4e697b1 100644
--- a/phoenix/reference/widget/button.hpp
+++ b/hiro/reference/widget/button.hpp
@@ -3,6 +3,7 @@ namespace phoenix {
 struct pButton : public pWidget {
   Button& button;
 
+  void setBordered(bool bordered);
   void setImage(const image& image, Orientation orientation);
   void setText(string text);
 
diff --git a/phoenix/reference/widget/canvas.cpp b/hiro/reference/widget/canvas.cpp
similarity index 100%
rename from phoenix/reference/widget/canvas.cpp
rename to hiro/reference/widget/canvas.cpp
diff --git a/phoenix/reference/widget/canvas.hpp b/hiro/reference/widget/canvas.hpp
similarity index 100%
rename from phoenix/reference/widget/canvas.hpp
rename to hiro/reference/widget/canvas.hpp
diff --git a/phoenix/reference/widget/check-button.cpp b/hiro/reference/widget/check-button.cpp
similarity index 100%
rename from phoenix/reference/widget/check-button.cpp
rename to hiro/reference/widget/check-button.cpp
diff --git a/phoenix/reference/widget/check-button.hpp b/hiro/reference/widget/check-button.hpp
similarity index 100%
rename from phoenix/reference/widget/check-button.hpp
rename to hiro/reference/widget/check-button.hpp
diff --git a/phoenix/reference/widget/check-label.cpp b/hiro/reference/widget/check-label.cpp
similarity index 100%
rename from phoenix/reference/widget/check-label.cpp
rename to hiro/reference/widget/check-label.cpp
diff --git a/phoenix/reference/widget/check-label.hpp b/hiro/reference/widget/check-label.hpp
similarity index 100%
rename from phoenix/reference/widget/check-label.hpp
rename to hiro/reference/widget/check-label.hpp
diff --git a/phoenix/reference/widget/combo-button.cpp b/hiro/reference/widget/combo-button.cpp
similarity index 72%
rename from phoenix/reference/widget/combo-button.cpp
rename to hiro/reference/widget/combo-button.cpp
index 0eac259b..d9f5d3b1 100644
--- a/phoenix/reference/widget/combo-button.cpp
+++ b/hiro/reference/widget/combo-button.cpp
@@ -1,6 +1,6 @@
 namespace phoenix {
 
-void pComboButton::append(string text) {
+void pComboButton::append() {
 }
 
 void pComboButton::remove(unsigned selection) {
@@ -9,7 +9,7 @@ void pComboButton::remove(unsigned selection) {
 void pComboButton::reset() {
 }
 
-void pComboButton::setSelection(unsigned selection) {
+void pComboButton::setSelected(unsigned selection) {
 }
 
 void pComboButton::setText(unsigned selection, string text) {
diff --git a/phoenix/reference/widget/combo-button.hpp b/hiro/reference/widget/combo-button.hpp
similarity index 82%
rename from phoenix/reference/widget/combo-button.hpp
rename to hiro/reference/widget/combo-button.hpp
index 9c183481..8b2b303f 100644
--- a/phoenix/reference/widget/combo-button.hpp
+++ b/hiro/reference/widget/combo-button.hpp
@@ -3,10 +3,10 @@ namespace phoenix {
 struct pComboButton : public pWidget {
   ComboButton& comboButton;
 
-  void append(string text);
+  void append();
   void remove(unsigned selection);
   void reset();
-  void setSelection(unsigned selection);
+  void setSelected(unsigned selection);
   void setText(unsigned selection, string text);
 
   pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {}
diff --git a/phoenix/reference/widget/console.cpp b/hiro/reference/widget/console.cpp
similarity index 100%
rename from phoenix/reference/widget/console.cpp
rename to hiro/reference/widget/console.cpp
diff --git a/phoenix/reference/widget/console.hpp b/hiro/reference/widget/console.hpp
similarity index 100%
rename from phoenix/reference/widget/console.hpp
rename to hiro/reference/widget/console.hpp
diff --git a/phoenix/reference/widget/frame.cpp b/hiro/reference/widget/frame.cpp
similarity index 100%
rename from phoenix/reference/widget/frame.cpp
rename to hiro/reference/widget/frame.cpp
diff --git a/phoenix/reference/widget/frame.hpp b/hiro/reference/widget/frame.hpp
similarity index 100%
rename from phoenix/reference/widget/frame.hpp
rename to hiro/reference/widget/frame.hpp
diff --git a/phoenix/reference/widget/hex-edit.cpp b/hiro/reference/widget/hex-edit.cpp
similarity index 100%
rename from phoenix/reference/widget/hex-edit.cpp
rename to hiro/reference/widget/hex-edit.cpp
diff --git a/phoenix/reference/widget/hex-edit.hpp b/hiro/reference/widget/hex-edit.hpp
similarity index 100%
rename from phoenix/reference/widget/hex-edit.hpp
rename to hiro/reference/widget/hex-edit.hpp
diff --git a/phoenix/reference/widget/horizontal-scroller.cpp b/hiro/reference/widget/horizontal-scroller.cpp
similarity index 100%
rename from phoenix/reference/widget/horizontal-scroller.cpp
rename to hiro/reference/widget/horizontal-scroller.cpp
diff --git a/phoenix/reference/widget/horizontal-scroller.hpp b/hiro/reference/widget/horizontal-scroller.hpp
similarity index 100%
rename from phoenix/reference/widget/horizontal-scroller.hpp
rename to hiro/reference/widget/horizontal-scroller.hpp
diff --git a/phoenix/reference/widget/horizontal-slider.cpp b/hiro/reference/widget/horizontal-slider.cpp
similarity index 100%
rename from phoenix/reference/widget/horizontal-slider.cpp
rename to hiro/reference/widget/horizontal-slider.cpp
diff --git a/phoenix/reference/widget/horizontal-slider.hpp b/hiro/reference/widget/horizontal-slider.hpp
similarity index 100%
rename from phoenix/reference/widget/horizontal-slider.hpp
rename to hiro/reference/widget/horizontal-slider.hpp
diff --git a/phoenix/reference/widget/label.cpp b/hiro/reference/widget/label.cpp
similarity index 100%
rename from phoenix/reference/widget/label.cpp
rename to hiro/reference/widget/label.cpp
diff --git a/phoenix/reference/widget/label.hpp b/hiro/reference/widget/label.hpp
similarity index 100%
rename from phoenix/reference/widget/label.hpp
rename to hiro/reference/widget/label.hpp
diff --git a/phoenix/reference/widget/layout.hpp b/hiro/reference/widget/layout.hpp
similarity index 100%
rename from phoenix/reference/widget/layout.hpp
rename to hiro/reference/widget/layout.hpp
diff --git a/phoenix/reference/widget/line-edit.cpp b/hiro/reference/widget/line-edit.cpp
similarity index 100%
rename from phoenix/reference/widget/line-edit.cpp
rename to hiro/reference/widget/line-edit.cpp
diff --git a/phoenix/reference/widget/line-edit.hpp b/hiro/reference/widget/line-edit.hpp
similarity index 100%
rename from phoenix/reference/widget/line-edit.hpp
rename to hiro/reference/widget/line-edit.hpp
diff --git a/phoenix/reference/widget/list-view.cpp b/hiro/reference/widget/list-view.cpp
similarity index 61%
rename from phoenix/reference/widget/list-view.cpp
rename to hiro/reference/widget/list-view.cpp
index e882907c..d774389d 100644
--- a/phoenix/reference/widget/list-view.cpp
+++ b/hiro/reference/widget/list-view.cpp
@@ -1,6 +1,6 @@
 namespace phoenix {
 
-void pListView::append(const lstring& text) {
+void pListView::append() {
 }
 
 void pListView::autoSizeColumns() {
@@ -21,6 +21,15 @@ void pListView::setCheckable(bool checkable) {
 void pListView::setChecked(unsigned selection, bool checked) {
 }
 
+void pListView::setChecked(const vector<unsigned>& selections) {
+}
+
+void pListView::setCheckedAll() {
+}
+
+void pListView::setCheckedNone() {
+}
+
 void pListView::setForegroundColor(Color color) {
 }
 
@@ -33,10 +42,19 @@ void pListView::setHeaderVisible(bool visible) {
 void pListView::setImage(unsigned selection, unsigned position, const image& image) {
 }
 
-void pListView::setSelected(bool selected) {
+void pListView::setSelected(unsigned selection, bool selected) {
 }
 
-void pListView::setSelection(unsigned selection) {
+void pListView::setSelected(const vector<unsigned>& selections) {
+}
+
+void pListView::setSelectedAll() {
+}
+
+void pListView::setSelectedNone() {
+}
+
+void pListView::setSingleSelection(bool singleSelection) {
 }
 
 void pListView::setText(unsigned selection, unsigned position, string text) {
diff --git a/phoenix/reference/widget/list-view.hpp b/hiro/reference/widget/list-view.hpp
similarity index 67%
rename from phoenix/reference/widget/list-view.hpp
rename to hiro/reference/widget/list-view.hpp
index 8a97a955..3b2e1528 100644
--- a/phoenix/reference/widget/list-view.hpp
+++ b/hiro/reference/widget/list-view.hpp
@@ -3,19 +3,25 @@ namespace phoenix {
 struct pListView : public pWidget {
   ListView& listView;
 
-  void append(const lstring& text);
+  void append();
   void autoSizeColumns();
   void remove(unsigned selection);
   void reset();
   void setBackgroundColor(Color color);
   void setCheckable(bool checkable);
   void setChecked(unsigned selection, bool checked);
+  void setChecked(const vector<unsigned>& selections);
+  void setCheckedAll();
+  void setCheckedNone();
   void setForegroundColor(Color color);
   void setHeaderText(const lstring& text);
   void setHeaderVisible(bool visible);
   void setImage(unsigned selection, unsigned position, const image& image);
-  void setSelected(bool selected);
-  void setSelection(unsigned selection);
+  void setSelected(unsigned selection, bool selected);
+  void setSelected(const vector<unsigned>& selections);
+  void setSelectedAll();
+  void setSelectedNone();
+  void setSingleSelection(bool singleSelection);
   void setText(unsigned selection, unsigned position, string text);
 
   pListView(ListView& listView) : pWidget(listView), listView(listView) {}
diff --git a/phoenix/reference/widget/progress-bar.cpp b/hiro/reference/widget/progress-bar.cpp
similarity index 100%
rename from phoenix/reference/widget/progress-bar.cpp
rename to hiro/reference/widget/progress-bar.cpp
diff --git a/phoenix/reference/widget/progress-bar.hpp b/hiro/reference/widget/progress-bar.hpp
similarity index 100%
rename from phoenix/reference/widget/progress-bar.hpp
rename to hiro/reference/widget/progress-bar.hpp
diff --git a/phoenix/reference/widget/radio-button.cpp b/hiro/reference/widget/radio-button.cpp
similarity index 100%
rename from phoenix/reference/widget/radio-button.cpp
rename to hiro/reference/widget/radio-button.cpp
diff --git a/phoenix/reference/widget/radio-button.hpp b/hiro/reference/widget/radio-button.hpp
similarity index 100%
rename from phoenix/reference/widget/radio-button.hpp
rename to hiro/reference/widget/radio-button.hpp
diff --git a/phoenix/reference/widget/radio-label.cpp b/hiro/reference/widget/radio-label.cpp
similarity index 100%
rename from phoenix/reference/widget/radio-label.cpp
rename to hiro/reference/widget/radio-label.cpp
diff --git a/phoenix/reference/widget/radio-label.hpp b/hiro/reference/widget/radio-label.hpp
similarity index 100%
rename from phoenix/reference/widget/radio-label.hpp
rename to hiro/reference/widget/radio-label.hpp
diff --git a/phoenix/reference/widget/sizable.hpp b/hiro/reference/widget/sizable.hpp
similarity index 100%
rename from phoenix/reference/widget/sizable.hpp
rename to hiro/reference/widget/sizable.hpp
diff --git a/phoenix/reference/widget/tab-frame.cpp b/hiro/reference/widget/tab-frame.cpp
similarity index 71%
rename from phoenix/reference/widget/tab-frame.cpp
rename to hiro/reference/widget/tab-frame.cpp
index 074a018a..065a89b7 100644
--- a/phoenix/reference/widget/tab-frame.cpp
+++ b/hiro/reference/widget/tab-frame.cpp
@@ -1,6 +1,6 @@
 namespace phoenix {
 
-void pTabFrame::append(string text, const image& image) {
+void pTabFrame::append() {
 }
 
 void pTabFrame::remove(unsigned selection) {
@@ -9,7 +9,7 @@ void pTabFrame::remove(unsigned selection) {
 void pTabFrame::setImage(unsigned selection, const image& image) {
 }
 
-void pTabFrame::setSelection(unsigned selection) {
+void pTabFrame::setSelected(unsigned selection) {
 }
 
 void pTabFrame::setText(unsigned selection, string text) {
diff --git a/phoenix/reference/widget/tab-frame.hpp b/hiro/reference/widget/tab-frame.hpp
similarity index 79%
rename from phoenix/reference/widget/tab-frame.hpp
rename to hiro/reference/widget/tab-frame.hpp
index 3efd9890..9984ef1d 100644
--- a/phoenix/reference/widget/tab-frame.hpp
+++ b/hiro/reference/widget/tab-frame.hpp
@@ -3,10 +3,10 @@ namespace phoenix {
 struct pTabFrame : public pWidget {
   TabFrame& tabFrame;
 
-  void append(string text, const image& image);
+  void append();
   void remove(unsigned selection);
   void setImage(unsigned selection, const image& image);
-  void setSelection(unsigned selection);
+  void setSelected(unsigned selection);
   void setText(unsigned selection, string text);
 
   pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {}
diff --git a/phoenix/reference/widget/text-edit.cpp b/hiro/reference/widget/text-edit.cpp
similarity index 100%
rename from phoenix/reference/widget/text-edit.cpp
rename to hiro/reference/widget/text-edit.cpp
diff --git a/phoenix/reference/widget/text-edit.hpp b/hiro/reference/widget/text-edit.hpp
similarity index 100%
rename from phoenix/reference/widget/text-edit.hpp
rename to hiro/reference/widget/text-edit.hpp
diff --git a/phoenix/reference/widget/vertical-scroller.cpp b/hiro/reference/widget/vertical-scroller.cpp
similarity index 100%
rename from phoenix/reference/widget/vertical-scroller.cpp
rename to hiro/reference/widget/vertical-scroller.cpp
diff --git a/phoenix/reference/widget/vertical-scroller.hpp b/hiro/reference/widget/vertical-scroller.hpp
similarity index 100%
rename from phoenix/reference/widget/vertical-scroller.hpp
rename to hiro/reference/widget/vertical-scroller.hpp
diff --git a/phoenix/reference/widget/vertical-slider.cpp b/hiro/reference/widget/vertical-slider.cpp
similarity index 100%
rename from phoenix/reference/widget/vertical-slider.cpp
rename to hiro/reference/widget/vertical-slider.cpp
diff --git a/phoenix/reference/widget/vertical-slider.hpp b/hiro/reference/widget/vertical-slider.hpp
similarity index 100%
rename from phoenix/reference/widget/vertical-slider.hpp
rename to hiro/reference/widget/vertical-slider.hpp
diff --git a/phoenix/reference/widget/viewport.cpp b/hiro/reference/widget/viewport.cpp
similarity index 100%
rename from phoenix/reference/widget/viewport.cpp
rename to hiro/reference/widget/viewport.cpp
diff --git a/phoenix/reference/widget/viewport.hpp b/hiro/reference/widget/viewport.hpp
similarity index 100%
rename from phoenix/reference/widget/viewport.hpp
rename to hiro/reference/widget/viewport.hpp
diff --git a/phoenix/reference/widget/widget.cpp b/hiro/reference/widget/widget.cpp
similarity index 100%
rename from phoenix/reference/widget/widget.cpp
rename to hiro/reference/widget/widget.cpp
diff --git a/phoenix/reference/widget/widget.hpp b/hiro/reference/widget/widget.hpp
similarity index 100%
rename from phoenix/reference/widget/widget.hpp
rename to hiro/reference/widget/widget.hpp
diff --git a/phoenix/reference/window.cpp b/hiro/reference/window.cpp
similarity index 95%
rename from phoenix/reference/window.cpp
rename to hiro/reference/window.cpp
index 416f2151..20b63eba 100644
--- a/phoenix/reference/window.cpp
+++ b/hiro/reference/window.cpp
@@ -23,10 +23,6 @@ Geometry pWindow::frameMargin() {
   return {0, 0, 0, 0};
 }
 
-Geometry pWindow::geometry() {
-  return {0, 0, 0, 0};
-}
-
 void pWindow::remove(Layout& layout) {
 }
 
diff --git a/phoenix/reference/window.hpp b/hiro/reference/window.hpp
similarity index 97%
rename from phoenix/reference/window.hpp
rename to hiro/reference/window.hpp
index 1039b9e6..b72efc84 100644
--- a/phoenix/reference/window.hpp
+++ b/hiro/reference/window.hpp
@@ -10,7 +10,6 @@ struct pWindow : public pObject {
   void append(Widget& widget);
   bool focused();
   Geometry frameMargin();
-  Geometry geometry();
   void remove(Layout& layout);
   void remove(Menu& menu);
   void remove(Widget& widget);
diff --git a/hiro/resource/GNUmakefile b/hiro/resource/GNUmakefile
new file mode 100644
index 00000000..97e085c2
--- /dev/null
+++ b/hiro/resource/GNUmakefile
@@ -0,0 +1,13 @@
+all:
+	sourcery resource.bml resource.cpp resource.hpp
+
+permissions:
+	chmod +w icon/action/*.png
+	chmod +w icon/application/*.png
+	chmod +w icon/device/*.png
+	chmod +w icon/edit/*.png
+	chmod +w icon/emblem/*.png
+	chmod +w icon/go/*.png
+	chmod +w icon/media/*.png
+	chmod +w icon/place/*.png
+	chmod +w icon/prompt/*.png
diff --git a/hiro/resource/icon/action/add.png b/hiro/resource/icon/action/add.png
new file mode 100644
index 00000000..1aa7f095
Binary files /dev/null and b/hiro/resource/icon/action/add.png differ
diff --git a/hiro/resource/icon/action/attach.png b/hiro/resource/icon/action/attach.png
new file mode 100644
index 00000000..529bb7f5
Binary files /dev/null and b/hiro/resource/icon/action/attach.png differ
diff --git a/hiro/resource/icon/action/bookmark.png b/hiro/resource/icon/action/bookmark.png
new file mode 100644
index 00000000..6cf6443a
Binary files /dev/null and b/hiro/resource/icon/action/bookmark.png differ
diff --git a/hiro/resource/icon/action/full-screen.png b/hiro/resource/icon/action/full-screen.png
new file mode 100644
index 00000000..ffdabd4e
Binary files /dev/null and b/hiro/resource/icon/action/full-screen.png differ
diff --git a/hiro/resource/icon/action/mute.png b/hiro/resource/icon/action/mute.png
new file mode 100644
index 00000000..af5a97b5
Binary files /dev/null and b/hiro/resource/icon/action/mute.png differ
diff --git a/hiro/resource/icon/action/new.png b/hiro/resource/icon/action/new.png
new file mode 100644
index 00000000..4c3efdd6
Binary files /dev/null and b/hiro/resource/icon/action/new.png differ
diff --git a/hiro/resource/icon/action/open.png b/hiro/resource/icon/action/open.png
new file mode 100644
index 00000000..ab940462
Binary files /dev/null and b/hiro/resource/icon/action/open.png differ
diff --git a/hiro/resource/icon/action/properties.png b/hiro/resource/icon/action/properties.png
new file mode 100644
index 00000000..ab0e8ea3
Binary files /dev/null and b/hiro/resource/icon/action/properties.png differ
diff --git a/hiro/resource/icon/action/quit.png b/hiro/resource/icon/action/quit.png
new file mode 100644
index 00000000..0010931e
Binary files /dev/null and b/hiro/resource/icon/action/quit.png differ
diff --git a/hiro/resource/icon/action/refresh.png b/hiro/resource/icon/action/refresh.png
new file mode 100644
index 00000000..3fd71d6e
Binary files /dev/null and b/hiro/resource/icon/action/refresh.png differ
diff --git a/hiro/resource/icon/action/remove.png b/hiro/resource/icon/action/remove.png
new file mode 100644
index 00000000..00b654e8
Binary files /dev/null and b/hiro/resource/icon/action/remove.png differ
diff --git a/hiro/resource/icon/action/save.png b/hiro/resource/icon/action/save.png
new file mode 100644
index 00000000..22ff4957
Binary files /dev/null and b/hiro/resource/icon/action/save.png differ
diff --git a/hiro/resource/icon/action/search.png b/hiro/resource/icon/action/search.png
new file mode 100644
index 00000000..fd7f0b07
Binary files /dev/null and b/hiro/resource/icon/action/search.png differ
diff --git a/target-higan/resource/advanced.png b/hiro/resource/icon/action/settings.png
similarity index 100%
rename from target-higan/resource/advanced.png
rename to hiro/resource/icon/action/settings.png
diff --git a/hiro/resource/icon/action/stop.png b/hiro/resource/icon/action/stop.png
new file mode 100644
index 00000000..ab6808fb
Binary files /dev/null and b/hiro/resource/icon/action/stop.png differ
diff --git a/hiro/resource/icon/application/browser.png b/hiro/resource/icon/application/browser.png
new file mode 100644
index 00000000..ac5957ad
Binary files /dev/null and b/hiro/resource/icon/application/browser.png differ
diff --git a/hiro/resource/icon/application/calculator.png b/hiro/resource/icon/application/calculator.png
new file mode 100644
index 00000000..9248971a
Binary files /dev/null and b/hiro/resource/icon/application/calculator.png differ
diff --git a/hiro/resource/icon/application/calendar.png b/hiro/resource/icon/application/calendar.png
new file mode 100644
index 00000000..106a592e
Binary files /dev/null and b/hiro/resource/icon/application/calendar.png differ
diff --git a/hiro/resource/icon/application/chat.png b/hiro/resource/icon/application/chat.png
new file mode 100644
index 00000000..f6e83254
Binary files /dev/null and b/hiro/resource/icon/application/chat.png differ
diff --git a/target-higan/resource/state-manager.png b/hiro/resource/icon/application/file-manager.png
similarity index 100%
rename from target-higan/resource/state-manager.png
rename to hiro/resource/icon/application/file-manager.png
diff --git a/hiro/resource/icon/application/mail.png b/hiro/resource/icon/application/mail.png
new file mode 100644
index 00000000..859251fe
Binary files /dev/null and b/hiro/resource/icon/application/mail.png differ
diff --git a/hiro/resource/icon/application/monitor.png b/hiro/resource/icon/application/monitor.png
new file mode 100644
index 00000000..8734e777
Binary files /dev/null and b/hiro/resource/icon/application/monitor.png differ
diff --git a/hiro/resource/icon/application/terminal.png b/hiro/resource/icon/application/terminal.png
new file mode 100644
index 00000000..c5b797a7
Binary files /dev/null and b/hiro/resource/icon/application/terminal.png differ
diff --git a/hiro/resource/icon/application/text-editor.png b/hiro/resource/icon/application/text-editor.png
new file mode 100644
index 00000000..188e1c12
Binary files /dev/null and b/hiro/resource/icon/application/text-editor.png differ
diff --git a/target-higan/resource/timing.png b/hiro/resource/icon/device/clock.png
similarity index 100%
rename from target-higan/resource/timing.png
rename to hiro/resource/icon/device/clock.png
diff --git a/target-higan/resource/video.png b/hiro/resource/icon/device/display.png
similarity index 100%
rename from target-higan/resource/video.png
rename to hiro/resource/icon/device/display.png
diff --git a/target-higan/resource/input.png b/hiro/resource/icon/device/joypad.png
similarity index 100%
rename from target-higan/resource/input.png
rename to hiro/resource/icon/device/joypad.png
diff --git a/target-higan/resource/hotkeys.png b/hiro/resource/icon/device/keyboard.png
similarity index 100%
rename from target-higan/resource/hotkeys.png
rename to hiro/resource/icon/device/keyboard.png
diff --git a/hiro/resource/icon/device/microphone.png b/hiro/resource/icon/device/microphone.png
new file mode 100644
index 00000000..53a03931
Binary files /dev/null and b/hiro/resource/icon/device/microphone.png differ
diff --git a/hiro/resource/icon/device/mouse.png b/hiro/resource/icon/device/mouse.png
new file mode 100644
index 00000000..eeda4db8
Binary files /dev/null and b/hiro/resource/icon/device/mouse.png differ
diff --git a/target-higan/resource/server.png b/hiro/resource/icon/device/network.png
similarity index 100%
rename from target-higan/resource/server.png
rename to hiro/resource/icon/device/network.png
diff --git a/hiro/resource/icon/device/optical.png b/hiro/resource/icon/device/optical.png
new file mode 100644
index 00000000..4ced6fe4
Binary files /dev/null and b/hiro/resource/icon/device/optical.png differ
diff --git a/hiro/resource/icon/device/printer.png b/hiro/resource/icon/device/printer.png
new file mode 100644
index 00000000..12a4e39d
Binary files /dev/null and b/hiro/resource/icon/device/printer.png differ
diff --git a/target-higan/resource/audio.png b/hiro/resource/icon/device/speaker.png
similarity index 100%
rename from target-higan/resource/audio.png
rename to hiro/resource/icon/device/speaker.png
diff --git a/hiro/resource/icon/device/storage.png b/hiro/resource/icon/device/storage.png
new file mode 100644
index 00000000..5c3b8587
Binary files /dev/null and b/hiro/resource/icon/device/storage.png differ
diff --git a/hiro/resource/icon/edit/clear.png b/hiro/resource/icon/edit/clear.png
new file mode 100644
index 00000000..e6c8e8b9
Binary files /dev/null and b/hiro/resource/icon/edit/clear.png differ
diff --git a/hiro/resource/icon/edit/copy.png b/hiro/resource/icon/edit/copy.png
new file mode 100644
index 00000000..8dd48c49
Binary files /dev/null and b/hiro/resource/icon/edit/copy.png differ
diff --git a/hiro/resource/icon/edit/cut.png b/hiro/resource/icon/edit/cut.png
new file mode 100644
index 00000000..dc9eb9a7
Binary files /dev/null and b/hiro/resource/icon/edit/cut.png differ
diff --git a/hiro/resource/icon/edit/delete.png b/hiro/resource/icon/edit/delete.png
new file mode 100644
index 00000000..ea03150a
Binary files /dev/null and b/hiro/resource/icon/edit/delete.png differ
diff --git a/hiro/resource/icon/edit/find.png b/hiro/resource/icon/edit/find.png
new file mode 100644
index 00000000..d072d3cb
Binary files /dev/null and b/hiro/resource/icon/edit/find.png differ
diff --git a/hiro/resource/icon/edit/paste.png b/hiro/resource/icon/edit/paste.png
new file mode 100644
index 00000000..24588a3a
Binary files /dev/null and b/hiro/resource/icon/edit/paste.png differ
diff --git a/hiro/resource/icon/edit/redo.png b/hiro/resource/icon/edit/redo.png
new file mode 100644
index 00000000..c3b0df03
Binary files /dev/null and b/hiro/resource/icon/edit/redo.png differ
diff --git a/hiro/resource/icon/edit/replace.png b/hiro/resource/icon/edit/replace.png
new file mode 100644
index 00000000..6edbef61
Binary files /dev/null and b/hiro/resource/icon/edit/replace.png differ
diff --git a/hiro/resource/icon/edit/undo.png b/hiro/resource/icon/edit/undo.png
new file mode 100644
index 00000000..8b0fef9a
Binary files /dev/null and b/hiro/resource/icon/edit/undo.png differ
diff --git a/hiro/resource/icon/emblem/archive.png b/hiro/resource/icon/emblem/archive.png
new file mode 100644
index 00000000..90154261
Binary files /dev/null and b/hiro/resource/icon/emblem/archive.png differ
diff --git a/hiro/resource/icon/emblem/audio.png b/hiro/resource/icon/emblem/audio.png
new file mode 100644
index 00000000..2bd5af93
Binary files /dev/null and b/hiro/resource/icon/emblem/audio.png differ
diff --git a/hiro/resource/icon/emblem/binary.png b/hiro/resource/icon/emblem/binary.png
new file mode 100644
index 00000000..003ded24
Binary files /dev/null and b/hiro/resource/icon/emblem/binary.png differ
diff --git a/hiro/resource/icon/emblem/file.png b/hiro/resource/icon/emblem/file.png
new file mode 100644
index 00000000..3c285b01
Binary files /dev/null and b/hiro/resource/icon/emblem/file.png differ
diff --git a/hiro/resource/icon/emblem/folder.png b/hiro/resource/icon/emblem/folder.png
new file mode 100644
index 00000000..65bd0bbd
Binary files /dev/null and b/hiro/resource/icon/emblem/folder.png differ
diff --git a/hiro/resource/icon/emblem/font.png b/hiro/resource/icon/emblem/font.png
new file mode 100644
index 00000000..bdbc1a80
Binary files /dev/null and b/hiro/resource/icon/emblem/font.png differ
diff --git a/hiro/resource/icon/emblem/image.png b/hiro/resource/icon/emblem/image.png
new file mode 100644
index 00000000..68da5027
Binary files /dev/null and b/hiro/resource/icon/emblem/image.png differ
diff --git a/hiro/resource/icon/emblem/markup.png b/hiro/resource/icon/emblem/markup.png
new file mode 100644
index 00000000..53014ab1
Binary files /dev/null and b/hiro/resource/icon/emblem/markup.png differ
diff --git a/hiro/resource/icon/emblem/program.png b/hiro/resource/icon/emblem/program.png
new file mode 100644
index 00000000..0d49f9df
Binary files /dev/null and b/hiro/resource/icon/emblem/program.png differ
diff --git a/hiro/resource/icon/emblem/script.png b/hiro/resource/icon/emblem/script.png
new file mode 100644
index 00000000..c923098e
Binary files /dev/null and b/hiro/resource/icon/emblem/script.png differ
diff --git a/hiro/resource/icon/emblem/text.png b/hiro/resource/icon/emblem/text.png
new file mode 100644
index 00000000..2d7f2d60
Binary files /dev/null and b/hiro/resource/icon/emblem/text.png differ
diff --git a/hiro/resource/icon/emblem/video.png b/hiro/resource/icon/emblem/video.png
new file mode 100644
index 00000000..3e4ced5b
Binary files /dev/null and b/hiro/resource/icon/emblem/video.png differ
diff --git a/hiro/resource/icon/go/down.png b/hiro/resource/icon/go/down.png
new file mode 100644
index 00000000..3dd7fccd
Binary files /dev/null and b/hiro/resource/icon/go/down.png differ
diff --git a/target-higan/resource/home.png b/hiro/resource/icon/go/home.png
similarity index 100%
rename from target-higan/resource/home.png
rename to hiro/resource/icon/go/home.png
diff --git a/hiro/resource/icon/go/left.png b/hiro/resource/icon/go/left.png
new file mode 100644
index 00000000..659cd90d
Binary files /dev/null and b/hiro/resource/icon/go/left.png differ
diff --git a/hiro/resource/icon/go/right.png b/hiro/resource/icon/go/right.png
new file mode 100644
index 00000000..6ef8de76
Binary files /dev/null and b/hiro/resource/icon/go/right.png differ
diff --git a/target-higan/resource/up.png b/hiro/resource/icon/go/up.png
similarity index 100%
rename from target-higan/resource/up.png
rename to hiro/resource/icon/go/up.png
diff --git a/hiro/resource/icon/media/back.png b/hiro/resource/icon/media/back.png
new file mode 100644
index 00000000..94381f54
Binary files /dev/null and b/hiro/resource/icon/media/back.png differ
diff --git a/hiro/resource/icon/media/eject.png b/hiro/resource/icon/media/eject.png
new file mode 100644
index 00000000..2084067e
Binary files /dev/null and b/hiro/resource/icon/media/eject.png differ
diff --git a/hiro/resource/icon/media/flash.png b/hiro/resource/icon/media/flash.png
new file mode 100644
index 00000000..bef542a1
Binary files /dev/null and b/hiro/resource/icon/media/flash.png differ
diff --git a/hiro/resource/icon/media/floppy.png b/hiro/resource/icon/media/floppy.png
new file mode 100644
index 00000000..f1d7a198
Binary files /dev/null and b/hiro/resource/icon/media/floppy.png differ
diff --git a/hiro/resource/icon/media/next.png b/hiro/resource/icon/media/next.png
new file mode 100644
index 00000000..758ec6f1
Binary files /dev/null and b/hiro/resource/icon/media/next.png differ
diff --git a/hiro/resource/icon/media/optical.png b/hiro/resource/icon/media/optical.png
new file mode 100644
index 00000000..760de938
Binary files /dev/null and b/hiro/resource/icon/media/optical.png differ
diff --git a/hiro/resource/icon/media/pause.png b/hiro/resource/icon/media/pause.png
new file mode 100644
index 00000000..c8b4fe22
Binary files /dev/null and b/hiro/resource/icon/media/pause.png differ
diff --git a/hiro/resource/icon/media/play.png b/hiro/resource/icon/media/play.png
new file mode 100644
index 00000000..a7de0feb
Binary files /dev/null and b/hiro/resource/icon/media/play.png differ
diff --git a/hiro/resource/icon/media/record.png b/hiro/resource/icon/media/record.png
new file mode 100644
index 00000000..2f66cdeb
Binary files /dev/null and b/hiro/resource/icon/media/record.png differ
diff --git a/hiro/resource/icon/media/rewind.png b/hiro/resource/icon/media/rewind.png
new file mode 100644
index 00000000..ffcac311
Binary files /dev/null and b/hiro/resource/icon/media/rewind.png differ
diff --git a/hiro/resource/icon/media/skip.png b/hiro/resource/icon/media/skip.png
new file mode 100644
index 00000000..4d7e2cd4
Binary files /dev/null and b/hiro/resource/icon/media/skip.png differ
diff --git a/hiro/resource/icon/media/stop.png b/hiro/resource/icon/media/stop.png
new file mode 100644
index 00000000..ede2815e
Binary files /dev/null and b/hiro/resource/icon/media/stop.png differ
diff --git a/hiro/resource/icon/place/bookmarks.png b/hiro/resource/icon/place/bookmarks.png
new file mode 100644
index 00000000..f3b5d9d9
Binary files /dev/null and b/hiro/resource/icon/place/bookmarks.png differ
diff --git a/hiro/resource/icon/place/desktop.png b/hiro/resource/icon/place/desktop.png
new file mode 100644
index 00000000..4c9787cc
Binary files /dev/null and b/hiro/resource/icon/place/desktop.png differ
diff --git a/hiro/resource/icon/place/home.png b/hiro/resource/icon/place/home.png
new file mode 100644
index 00000000..7b9110df
Binary files /dev/null and b/hiro/resource/icon/place/home.png differ
diff --git a/hiro/resource/icon/place/server.png b/hiro/resource/icon/place/server.png
new file mode 100644
index 00000000..068ffebe
Binary files /dev/null and b/hiro/resource/icon/place/server.png differ
diff --git a/hiro/resource/icon/place/share.png b/hiro/resource/icon/place/share.png
new file mode 100644
index 00000000..5234eab4
Binary files /dev/null and b/hiro/resource/icon/place/share.png differ
diff --git a/hiro/resource/icon/prompt/error.png b/hiro/resource/icon/prompt/error.png
new file mode 100644
index 00000000..3bbbb4a0
Binary files /dev/null and b/hiro/resource/icon/prompt/error.png differ
diff --git a/hiro/resource/icon/prompt/information.png b/hiro/resource/icon/prompt/information.png
new file mode 100644
index 00000000..8851b99b
Binary files /dev/null and b/hiro/resource/icon/prompt/information.png differ
diff --git a/hiro/resource/icon/prompt/question.png b/hiro/resource/icon/prompt/question.png
new file mode 100644
index 00000000..f25fc3fb
Binary files /dev/null and b/hiro/resource/icon/prompt/question.png differ
diff --git a/hiro/resource/icon/prompt/warning.png b/hiro/resource/icon/prompt/warning.png
new file mode 100644
index 00000000..a9e4ff39
Binary files /dev/null and b/hiro/resource/icon/prompt/warning.png differ
diff --git a/hiro/resource/resource.bml b/hiro/resource/resource.bml
new file mode 100644
index 00000000..3c3effbd
--- /dev/null
+++ b/hiro/resource/resource.bml
@@ -0,0 +1,92 @@
+namespace name=Icon
+  namespace name=Action
+    binary name=Add file=icon/action/add.png
+    binary name=Attach file=icon/action/attach.png
+    binary name=Bookmark file=icon/action/bookmark.png
+    binary name=FullScreen file=icon/action/full-screen.png
+    binary name=Mute file=icon/action/mute.png
+    binary name=New file=icon/action/new.png
+    binary name=Open file=icon/action/open.png
+    binary name=Properties file=icon/action/properties.png
+    binary name=Quit file=icon/action/quit.png
+    binary name=Refresh file=icon/action/refresh.png
+    binary name=Remove file=icon/action/remove.png
+    binary name=Save file=icon/action/save.png
+    binary name=Search file=icon/action/search.png
+    binary name=Settings file=icon/action/settings.png
+    binary name=Stop file=icon/action/stop.png
+  namespace name=Application
+    binary name=Browser file=icon/application/browser.png
+    binary name=Calculator file=icon/application/calculator.png
+    binary name=Calendar file=icon/application/calendar.png
+    binary name=Chat file=icon/application/chat.png
+    binary name=FileManager file=icon/application/file-manager.png
+    binary name=Mail file=icon/application/mail.png
+    binary name=Monitor file=icon/application/monitor.png
+    binary name=Terminal file=icon/application/terminal.png
+    binary name=TextEditor file=icon/application/text-editor.png
+  namespace name=Device
+    binary name=Clock file=icon/device/clock.png
+    binary name=Display file=icon/device/display.png
+    binary name=Joypad file=icon/device/joypad.png
+    binary name=Keyboard file=icon/device/keyboard.png
+    binary name=Microphone file=icon/device/microphone.png
+    binary name=Mouse file=icon/device/mouse.png
+    binary name=Network file=icon/device/network.png
+    binary name=Optical file=icon/device/optical.png
+    binary name=Printer file=icon/device/printer.png
+    binary name=Speaker file=icon/device/speaker.png
+    binary name=Storage file=icon/device/storage.png
+  namespace name=Edit
+    binary name=Clear file=icon/edit/clear.png
+    binary name=Copy file=icon/edit/copy.png
+    binary name=Cut file=icon/edit/cut.png
+    binary name=Delete file=icon/edit/delete.png
+    binary name=Find file=icon/edit/find.png
+    binary name=Paste file=icon/edit/paste.png
+    binary name=Redo file=icon/edit/redo.png
+    binary name=Replace file=icon/edit/replace.png
+    binary name=Undo file=icon/edit/undo.png
+  namespace name=Emblem
+    binary name=Archive file=icon/emblem/archive.png
+    binary name=Audio file=icon/emblem/audio.png
+    binary name=Binary file=icon/emblem/binary.png
+    binary name=File file=icon/emblem/file.png
+    binary name=Folder file=icon/emblem/folder.png
+    binary name=Font file=icon/emblem/font.png
+    binary name=Image file=icon/emblem/image.png
+    binary name=Markup file=icon/emblem/markup.png
+    binary name=Program file=icon/emblem/program.png
+    binary name=Script file=icon/emblem/script.png
+    binary name=Text file=icon/emblem/text.png
+    binary name=Video file=icon/emblem/video.png
+  namespace name=Go
+    binary name=Down file=icon/go/down.png
+    binary name=Home file=icon/go/home.png
+    binary name=Left file=icon/go/left.png
+    binary name=Right file=icon/go/right.png
+    binary name=Up file=icon/go/up.png
+  namespace name=Media
+    binary name=Back file=icon/media/back.png
+    binary name=Eject file=icon/media/eject.png
+    binary name=Flash file=icon/media/flash.png
+    binary name=Floppy file=icon/media/floppy.png
+    binary name=Next file=icon/media/next.png
+    binary name=Optical file=icon/media/optical.png
+    binary name=Pause file=icon/media/pause.png
+    binary name=Play file=icon/media/play.png
+    binary name=Record file=icon/media/record.png
+    binary name=Rewind file=icon/media/rewind.png
+    binary name=Skip file=icon/media/skip.png
+    binary name=Stop file=icon/media/stop.png
+  namespace name=Place
+    binary name=Bookmarks file=icon/place/bookmarks.png
+    binary name=Desktop file=icon/place/desktop.png
+    binary name=Home file=icon/place/home.png
+    binary name=Server file=icon/place/server.png
+    binary name=Share file=icon/place/share.png
+  namespace name=Prompt
+    binary name=Error file=icon/prompt/error.png
+    binary name=Information file=icon/prompt/information.png
+    binary name=Question file=icon/prompt/question.png
+    binary name=Warning file=icon/prompt/warning.png
diff --git a/hiro/resource/resource.cpp b/hiro/resource/resource.cpp
new file mode 100644
index 00000000..19096fbd
--- /dev/null
+++ b/hiro/resource/resource.cpp
@@ -0,0 +1,1891 @@
+namespace Icon {
+namespace Action {
+const nall::vector<uint8_t> Add = {  //size: 323
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,214,1,11,16,0,17,148,68,120,161,0,0,0,208,73,
+  68,65,84,56,203,205,145,189,14,193,96,20,134,31,210,197,96,183,114,7,210,186,129,198,96,54,91,59,116,113,7,68,
+  210,196,100,23,177,90,13,38,131,132,27,232,103,55,90,44,86,105,226,167,223,49,16,138,250,139,4,239,118,242,157,188,
+  249,158,231,192,175,147,184,247,96,57,61,31,48,143,163,242,187,21,43,110,47,249,160,220,244,92,27,207,181,137,20,189,
+  85,240,82,62,46,48,238,48,199,57,145,200,120,114,98,68,153,91,213,34,91,45,36,128,109,120,222,247,92,27,17,16,
+  57,88,175,119,198,230,205,15,0,86,235,144,249,50,64,4,180,22,180,64,176,17,194,80,216,105,65,107,200,101,82,241,
+  8,128,106,116,38,23,8,229,82,1,128,193,200,191,38,82,79,229,88,78,79,134,211,133,52,251,51,185,226,255,242,25,
+  243,217,244,107,103,140,137,170,181,79,182,21,127,155,61,84,114,75,11,139,178,119,142,0,0,0,0,73,69,78,68,174,
+  66,96,130,
+};
+const nall::vector<uint8_t> Attach = {  //size: 649
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,27,73,68,65,84,56,141,
+  133,211,223,107,146,81,24,192,241,175,250,170,140,114,25,221,76,47,212,205,233,88,233,162,112,51,2,7,131,193,198,168,
+  219,32,168,151,94,120,137,186,136,8,130,160,191,160,174,130,26,196,64,144,12,162,187,230,72,27,68,174,117,185,173,232,
+  231,42,236,23,25,182,145,166,108,110,234,252,213,133,89,175,154,235,92,29,158,243,240,57,207,121,120,142,170,90,173,210,
+  188,36,89,84,3,19,192,49,96,4,72,1,247,128,169,128,63,184,162,204,85,53,3,146,44,118,0,51,93,166,174,209,
+  189,253,253,216,29,118,54,55,54,137,197,98,44,45,62,253,94,169,84,60,1,127,48,81,207,87,183,92,15,151,157,125,
+  206,209,241,177,49,86,86,87,185,21,184,77,36,242,0,147,201,132,203,237,50,1,23,149,201,127,0,73,22,207,72,178,
+  248,25,56,231,243,249,152,158,14,177,252,102,121,106,107,171,48,148,254,153,62,31,141,62,198,235,245,2,156,250,39,0,
+  92,241,249,134,109,192,174,78,67,39,153,76,6,224,82,192,31,92,4,110,172,175,173,97,216,177,19,96,119,59,160,99,
+  208,51,8,128,86,208,214,99,21,73,22,143,2,215,0,4,65,7,208,208,52,65,177,87,105,117,186,102,192,12,132,204,
+  102,179,42,145,72,160,17,132,22,64,89,129,74,39,252,6,180,186,122,108,15,160,58,45,159,5,64,87,131,219,3,245,
+  155,133,191,21,84,149,21,105,212,255,1,234,39,149,114,21,181,90,13,208,3,80,42,149,0,40,150,138,219,3,249,92,
+  30,128,87,175,95,50,224,222,15,48,105,179,118,243,241,211,7,0,242,185,92,11,208,208,196,66,161,6,204,205,71,57,
+  113,252,36,86,139,205,104,177,88,9,71,102,0,40,20,10,219,2,235,217,108,214,160,215,235,73,38,127,100,38,111,94,
+  55,246,218,29,60,138,62,36,187,145,197,209,235,36,254,45,14,16,107,247,132,217,133,165,5,38,198,143,208,211,109,55,
+  106,52,26,222,189,127,75,185,82,198,181,207,205,161,161,195,204,63,153,3,184,218,174,130,11,207,95,60,27,72,165,146,
+  125,86,139,149,131,7,60,8,130,64,169,88,226,107,252,11,247,35,33,210,153,116,24,184,163,4,26,126,163,36,139,6,
+  106,179,62,2,12,83,155,131,52,48,11,220,5,194,1,127,176,172,4,126,1,41,32,186,81,242,76,218,213,0,0,0,
+  0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Bookmark = {  //size: 686
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,10,17,21,6,52,65,49,61,207,0,0,2,59,73,
+  68,65,84,56,203,149,210,65,72,147,97,28,199,241,239,255,125,158,247,221,187,185,114,91,35,107,172,21,45,17,44,47,
+  129,66,69,247,194,168,32,130,14,65,135,162,123,208,161,162,118,176,67,167,162,171,116,173,131,224,37,40,138,40,43,208,
+  166,65,33,120,113,83,9,209,201,212,68,90,178,215,109,239,246,118,152,168,43,19,252,93,158,231,240,252,63,252,255,207,
+  243,8,107,233,58,115,231,22,240,24,240,216,136,7,32,34,243,247,174,231,146,221,167,127,181,129,238,130,218,34,212,210,
+  192,162,6,232,237,237,237,207,231,243,151,38,38,167,200,46,198,228,193,221,171,245,114,169,19,15,31,61,143,13,166,93,
+  167,53,17,231,192,161,19,24,214,65,86,150,62,12,238,9,142,221,80,0,157,157,157,125,169,84,138,93,193,32,3,233,
+  31,132,91,162,124,207,228,152,156,249,137,83,170,48,49,62,205,237,155,199,232,56,122,4,171,249,2,166,125,8,67,251,
+  19,218,253,58,167,55,181,75,161,80,192,173,20,89,90,94,102,126,97,25,0,91,215,112,43,69,10,191,93,220,154,139,
+  174,58,136,50,40,23,103,240,41,41,24,155,129,106,181,74,181,178,74,185,228,80,114,86,40,59,43,84,86,29,220,178,
+  195,216,120,137,153,156,80,45,14,224,21,223,49,59,157,6,175,242,173,1,0,16,101,161,181,133,105,249,80,166,15,165,
+  77,196,48,217,31,75,16,217,119,22,167,214,129,152,49,70,70,77,160,58,218,0,136,8,218,244,99,218,1,44,59,136,
+  207,223,132,105,251,209,166,159,220,108,142,76,118,10,79,98,96,29,103,110,161,9,221,242,114,85,255,221,129,97,40,12,
+  67,161,180,9,128,82,38,134,210,196,227,113,218,218,218,64,4,17,141,231,9,0,13,128,101,89,235,251,189,145,32,181,
+  218,198,151,176,253,138,173,210,48,66,32,16,168,255,30,15,190,188,31,102,120,96,4,207,171,35,77,129,102,68,100,123,
+  192,231,243,1,240,233,237,16,137,216,110,14,39,66,124,124,51,132,231,121,228,243,121,50,153,204,63,64,195,8,182,109,
+  3,112,237,242,41,174,92,60,137,97,24,244,191,26,230,217,139,207,180,183,183,147,76,38,183,7,162,209,40,79,238,159,
+  67,41,69,54,155,69,68,232,104,13,243,52,117,158,112,56,188,254,82,255,5,66,161,16,161,80,8,17,89,63,184,121,
+  221,234,14,26,128,72,36,194,78,163,215,90,127,221,211,211,211,189,147,194,104,52,218,7,240,7,70,86,184,198,50,151,
+  228,191,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> FullScreen = {  //size: 650
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,214,3,16,19,13,53,226,119,223,154,0,0,0,53,116,
+  69,88,116,67,111,109,109,101,110,116,0,40,99,41,32,50,48,48,52,32,74,97,107,117,98,32,83,116,101,105,110,101,
+  114,10,10,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,144,217,139,111,0,0,1,214,
+  73,68,65,84,56,203,157,147,59,104,147,81,20,199,127,255,155,27,53,21,105,211,37,72,241,5,113,8,8,130,32,148,
+  42,118,42,20,95,80,197,201,199,224,208,193,42,213,197,65,112,112,72,249,154,224,168,96,6,193,65,80,2,237,224,179,
+  86,81,116,80,113,13,45,138,37,45,22,69,106,77,83,21,77,76,251,117,248,146,216,188,28,114,224,192,133,115,207,239,
+  254,207,185,231,200,137,69,111,0,253,52,103,9,156,88,212,109,214,156,88,212,181,37,212,238,254,219,21,232,39,195,189,
+  0,72,6,112,233,185,248,168,34,254,46,113,28,0,83,173,201,24,253,75,6,36,144,196,211,248,126,140,81,77,13,166,
+  58,121,67,192,207,133,235,111,188,151,101,80,209,79,57,47,104,89,107,177,62,213,7,72,48,230,244,178,177,61,64,184,
+  163,149,123,175,103,144,60,25,119,158,127,100,199,214,118,66,193,0,227,241,131,248,86,41,41,247,96,60,118,0,128,107,
+  131,123,25,125,53,77,40,24,224,253,167,69,172,53,132,59,218,88,99,125,92,58,177,11,23,241,236,234,161,90,128,228,
+  85,45,160,111,223,54,38,166,51,68,182,180,97,100,248,48,187,64,103,36,92,190,231,174,170,188,12,176,182,116,244,32,
+  126,191,197,250,252,200,136,245,45,235,240,89,11,18,194,5,87,245,122,96,144,132,145,33,241,96,130,185,133,63,76,125,
+  206,50,59,247,147,236,175,60,35,47,167,138,191,98,74,114,43,1,221,231,71,145,12,199,174,60,102,114,38,67,42,253,
+  157,237,155,130,108,14,181,146,74,207,243,118,242,43,71,46,63,4,96,207,217,100,45,192,117,161,123,112,132,204,143,28,
+  233,47,139,156,59,186,19,73,72,226,100,79,132,84,122,158,111,217,223,116,13,36,27,207,193,178,11,185,191,75,220,119,
+  14,123,157,40,2,36,49,22,239,99,105,217,253,255,32,149,172,107,32,137,49,194,200,115,100,232,60,115,183,238,54,217,
+  234,217,110,100,141,226,182,80,40,220,28,142,15,157,110,102,151,243,185,252,173,21,142,211,185,159,81,219,35,189,0,0,
+  0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Mute = {  //size: 632
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,10,73,68,65,84,56,141,
+  165,147,207,75,84,81,24,134,159,115,61,215,51,214,140,46,100,26,17,141,22,226,70,178,50,23,21,65,11,33,152,85,
+  139,113,156,153,173,155,41,92,70,250,31,68,45,204,173,182,82,180,140,68,72,9,4,17,90,185,147,72,200,12,209,96,
+  108,51,11,113,225,189,206,56,115,127,124,45,6,103,97,72,86,31,156,213,199,251,188,223,11,239,81,34,194,255,140,117,
+  222,98,48,55,216,159,207,231,237,127,2,100,179,169,190,6,212,90,181,90,109,248,107,64,54,155,234,179,27,35,159,68,
+  164,249,79,98,0,157,201,165,29,32,162,181,62,70,100,210,110,140,60,25,30,30,110,158,122,61,21,92,4,96,41,84,
+  100,98,124,66,143,141,142,181,8,140,142,140,140,52,223,189,115,15,173,245,69,244,104,65,136,199,175,16,143,39,16,17,
+  117,235,230,109,140,49,40,165,46,6,0,216,250,246,181,238,184,179,243,29,219,182,241,60,143,135,11,239,243,243,211,211,
+  207,4,34,103,133,10,78,4,158,107,0,199,113,234,0,207,247,16,106,221,176,124,255,101,39,24,109,12,150,49,4,71,
+  71,232,214,86,252,195,67,2,17,10,240,202,2,112,92,7,199,117,16,17,188,170,135,87,245,78,141,204,37,99,232,89,
+  93,229,250,250,58,177,238,110,110,108,110,210,61,51,67,83,45,162,209,0,174,235,82,46,151,1,40,252,220,167,179,163,
+  163,126,170,233,234,226,114,127,63,202,24,122,182,183,1,104,73,38,209,137,4,20,139,88,34,34,139,139,139,193,202,202,
+  202,137,165,172,169,165,165,15,238,126,97,191,14,56,217,218,226,71,38,83,203,109,213,106,179,51,48,128,95,44,214,98,
+  2,49,17,137,134,97,24,125,251,230,221,99,175,18,36,151,63,46,187,97,24,90,0,118,123,59,87,39,39,65,41,36,
+  8,64,132,107,179,179,88,177,88,205,65,68,126,123,233,116,250,126,58,147,58,158,3,249,210,214,38,149,66,65,188,131,
+  3,217,203,229,36,40,149,196,221,216,144,207,209,168,204,129,168,243,126,227,208,208,208,131,71,11,11,107,93,160,35,137,
+  4,13,209,40,149,189,61,154,122,123,169,236,238,82,45,149,216,5,255,92,0,192,188,82,47,66,120,122,218,151,51,227,
+  91,48,254,11,41,211,229,38,66,238,34,57,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> New = {  //size: 477
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,252,0,233,0,79,52,215,177,13,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,2,18,14,38,28,211,182,25,41,0,0,1,106,73,
+  68,65,84,56,203,173,144,79,75,91,81,16,197,127,115,239,141,86,36,125,37,130,208,202,91,22,220,212,238,92,102,169,
+  27,243,13,178,113,89,191,69,222,178,221,119,35,4,196,130,223,192,77,55,221,42,133,82,74,178,82,74,73,90,132,16,
+  255,69,98,155,222,119,199,133,38,198,24,147,84,122,54,51,12,115,230,156,51,82,46,151,183,107,181,90,145,17,152,157,
+  253,203,220,92,155,103,209,111,242,203,159,223,229,162,111,31,225,207,190,100,195,25,165,82,73,199,225,252,120,83,47,79,
+  223,104,231,188,160,151,39,43,157,139,198,226,174,182,178,235,218,50,51,174,171,210,108,54,9,33,0,32,34,61,245,140,
+  217,99,218,126,33,227,190,34,102,9,235,76,38,216,246,106,199,183,91,83,206,87,123,7,6,201,221,106,228,8,107,234,
+  136,121,133,184,2,96,49,42,70,211,95,139,224,94,186,254,172,131,228,235,94,111,58,11,76,129,56,16,11,130,0,23,
+  110,52,89,8,58,79,208,231,216,80,69,211,105,64,208,180,162,222,155,122,198,166,21,247,208,231,187,135,2,175,241,225,
+  39,224,177,90,1,20,239,95,232,233,89,235,211,204,252,247,131,145,17,68,4,229,41,29,93,67,117,129,52,253,1,60,
+  97,107,231,208,108,108,108,191,5,112,163,200,183,53,139,215,60,34,66,20,69,52,26,73,79,212,48,33,250,127,211,143,
+  123,17,134,57,120,136,124,199,193,176,165,193,217,176,29,51,169,221,177,17,114,185,28,143,129,139,227,248,67,146,36,197,
+  127,33,197,113,252,158,255,133,43,204,61,153,96,123,172,20,201,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Open = {  //size: 672
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,2,29,73,68,65,84,56,141,141,147,191,75,151,81,20,198,63,231,125,
+  175,111,73,160,107,4,225,210,82,208,24,168,75,63,164,37,13,114,106,208,165,197,193,160,230,134,254,130,134,16,55,135,
+  138,6,251,15,4,35,133,8,77,43,104,104,138,16,33,181,69,66,45,205,239,247,222,115,207,105,248,234,155,150,96,207,
+  112,238,185,151,123,159,231,57,231,222,43,67,67,67,28,68,202,205,134,136,148,238,46,34,2,64,81,20,34,34,180,181,
+  181,125,254,181,221,184,92,85,213,250,254,254,208,213,213,245,212,221,111,238,47,44,45,127,57,241,236,201,115,138,162,64,
+  68,216,39,17,17,38,95,76,158,159,158,158,250,128,115,17,248,1,16,204,108,112,112,112,176,211,204,200,57,51,54,254,
+  152,149,149,21,154,205,38,0,238,78,206,25,51,99,120,104,152,173,205,205,179,11,239,23,230,227,110,186,84,85,213,110,
+  72,41,185,170,178,190,190,142,170,238,91,102,241,221,226,161,210,122,186,187,1,24,29,189,43,229,68,184,48,55,255,102,
+  12,99,100,143,32,161,170,152,25,34,66,85,85,244,93,235,195,221,107,23,101,89,214,34,189,61,189,242,118,97,238,182,
+  25,35,65,85,77,53,147,115,174,29,132,16,152,153,125,69,206,185,118,112,245,202,53,98,140,173,137,128,153,21,80,16,
+  82,74,30,99,172,9,246,186,77,255,141,129,90,253,239,177,253,100,59,56,6,16,98,140,166,170,76,204,59,13,13,208,
+  121,139,129,135,47,113,107,137,249,129,216,34,216,75,218,251,59,202,162,88,13,170,74,74,137,157,8,143,238,95,231,40,
+  72,29,254,32,170,241,96,124,230,76,72,41,89,74,9,112,74,129,143,203,91,71,146,28,68,89,10,231,78,159,2,113,
+  15,41,37,83,77,8,80,136,80,133,226,88,130,108,142,27,8,98,123,215,152,17,9,196,100,148,114,176,226,195,208,236,
+  52,147,161,102,52,83,6,240,160,170,22,99,68,36,240,109,163,193,207,134,162,217,73,217,49,115,204,157,108,78,210,86,
+  14,16,74,97,245,251,46,136,88,16,17,51,51,4,216,216,142,108,55,148,164,255,122,8,165,212,157,44,10,216,220,137,
+  136,187,7,119,103,109,163,245,96,94,127,90,173,63,207,113,216,217,217,5,145,175,33,23,237,179,83,75,29,119,192,89,
+  94,90,251,175,195,180,188,108,100,228,222,111,248,2,75,29,117,50,217,195,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Properties = {  //size: 464
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,11,4,10,21,27,198,210,43,84,0,0,1,93,73,
+  68,65,84,56,203,173,147,189,106,194,80,24,134,159,175,186,25,13,145,108,14,14,130,34,222,130,102,117,232,216,161,215,
+  224,45,56,40,113,200,37,136,46,93,116,105,233,218,193,81,112,245,2,78,68,232,162,100,208,156,213,41,118,16,131,49,
+  162,8,125,167,195,251,113,158,239,239,28,1,24,143,199,223,65,16,188,241,156,222,251,253,254,23,0,174,235,30,159,149,
+  235,186,71,128,236,37,114,191,223,19,69,17,0,34,18,251,231,179,136,144,201,100,48,77,51,142,37,0,81,20,225,251,
+  126,226,194,165,68,132,122,189,158,240,178,215,141,213,106,53,0,122,189,94,236,117,58,29,74,165,82,10,8,240,114,157,
+  193,247,125,86,171,21,237,118,27,173,53,90,107,60,207,67,41,133,82,234,62,0,96,58,157,98,24,6,205,102,147,110,
+  183,139,101,89,88,86,129,106,181,26,87,119,23,112,206,184,88,44,240,60,15,17,65,36,203,118,251,123,115,151,169,22,
+  78,25,45,102,179,25,197,98,49,142,141,70,31,4,65,240,184,130,203,222,195,48,36,12,67,180,214,28,14,7,134,195,
+  33,187,221,238,254,22,90,173,22,142,227,196,21,109,54,27,38,147,9,133,66,129,92,46,199,124,62,167,82,169,220,6,
+  136,8,74,169,212,59,112,28,135,245,122,141,97,24,228,243,249,199,239,224,52,184,36,164,209,104,176,92,46,239,3,46,
+  135,118,45,211,52,41,151,203,41,63,11,96,219,246,207,96,48,120,125,230,43,218,182,253,201,127,232,15,224,142,163,230,
+  109,49,155,36,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Quit = {  //size: 799
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,2,156,73,68,65,84,56,141,117,147,79,104,20,73,20,198,127,175,170,
+  186,187,122,178,131,123,136,30,140,32,234,206,97,47,226,69,65,133,16,209,171,104,80,195,170,228,40,30,12,44,26,15,
+  146,187,46,30,246,224,31,140,160,87,69,80,47,158,68,80,20,36,106,4,133,21,28,221,29,119,66,38,108,64,103,162,
+  201,244,100,58,157,238,169,61,196,25,255,63,248,168,122,151,175,126,95,61,158,28,28,220,127,50,73,146,17,126,80,34,
+  130,239,251,216,192,98,140,1,17,178,44,197,6,246,207,179,103,206,159,48,73,146,140,244,239,218,67,185,92,38,138,34,
+  26,141,6,205,102,147,56,142,73,211,148,11,23,206,163,148,66,107,131,82,10,128,102,220,228,236,185,51,195,192,105,3,
+  16,4,1,65,16,144,36,9,105,154,210,106,181,112,206,161,181,6,64,41,141,49,230,99,47,104,109,232,89,217,3,144,
+  87,0,249,124,158,48,12,201,229,114,132,97,216,185,91,107,1,65,4,68,20,165,209,139,60,216,190,29,17,193,218,16,
+  192,152,118,78,107,45,206,185,78,110,17,65,41,133,8,184,44,227,217,209,33,178,106,149,15,197,34,245,191,95,195,127,
+  211,252,51,58,218,109,150,16,21,90,107,180,214,248,190,79,150,101,29,179,180,94,231,201,254,131,116,23,10,172,30,24,
+  32,46,151,25,223,187,143,101,201,2,79,203,19,247,58,4,109,19,165,84,71,54,138,120,216,219,199,218,29,59,88,190,
+  110,29,233,244,52,235,15,29,2,17,196,24,238,28,62,188,20,161,77,209,150,214,154,220,212,20,171,110,222,164,176,115,
+  39,63,25,67,116,255,62,40,181,36,17,242,125,125,136,82,116,12,218,175,183,207,238,91,183,88,93,40,96,227,152,232,
+  249,115,90,113,76,171,209,160,53,63,143,238,234,34,88,179,6,68,80,159,19,124,30,225,237,224,32,111,94,189,98,186,
+  88,68,133,33,139,149,10,217,220,28,46,77,201,234,117,146,74,5,151,101,159,8,218,255,208,54,74,86,172,96,234,200,
+  17,114,55,174,19,55,155,172,220,176,1,29,134,44,46,44,208,82,138,249,106,21,156,147,14,193,247,166,145,134,33,183,
+  183,108,193,21,126,161,92,169,144,68,17,127,141,143,243,239,228,36,47,138,47,241,186,186,158,126,97,96,140,233,104,98,
+  98,130,192,250,12,13,31,227,215,203,151,88,214,191,155,215,165,18,218,90,54,62,30,99,242,248,48,251,162,168,255,139,
+  8,158,231,81,122,83,194,134,62,191,29,24,32,159,207,163,181,38,106,212,89,254,251,16,178,170,135,217,187,119,153,157,
+  251,64,181,246,14,96,222,0,120,158,71,173,86,37,89,92,96,243,214,77,120,158,143,115,45,230,234,179,128,124,218,204,
+  109,189,252,188,173,151,247,239,107,204,204,204,44,25,248,190,127,234,234,181,43,35,181,90,21,17,97,236,209,216,55,235,
+  252,117,57,231,8,130,224,15,32,253,31,63,61,253,93,147,0,165,101,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Refresh = {  //size: 912
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,3,34,73,68,65,84,56,141,
+  109,82,77,108,84,101,20,61,247,123,239,149,249,173,51,109,7,91,168,182,10,134,18,166,161,162,51,141,127,209,152,70,
+  55,196,96,88,168,188,68,140,144,129,240,179,97,7,27,12,77,12,17,116,97,162,224,3,163,49,76,37,152,52,1,55,
+  252,4,33,146,212,194,171,113,209,208,146,212,210,198,1,70,59,180,29,232,204,180,243,222,251,190,235,162,243,146,89,112,
+  146,155,220,197,61,39,39,231,92,98,102,248,72,101,178,207,52,232,218,17,16,222,244,164,106,213,132,152,39,194,184,227,
+  202,19,0,46,216,150,233,214,221,30,4,112,130,124,129,116,38,187,69,211,196,64,79,215,42,125,93,103,179,209,214,20,
+  1,152,81,40,86,48,50,118,191,124,103,250,161,39,21,239,182,45,243,108,122,87,54,195,140,239,0,196,136,153,145,202,
+  100,215,27,186,24,217,218,215,29,138,199,130,208,137,208,28,13,32,28,208,17,13,25,8,26,2,51,197,69,28,207,14,
+  87,10,197,138,29,13,53,244,62,46,87,13,102,52,17,51,227,213,61,103,79,245,172,107,251,52,24,48,196,173,209,28,
+  170,174,7,67,215,228,154,213,177,197,190,244,243,225,222,245,173,164,9,130,16,132,95,174,77,168,183,94,108,23,7,190,
+  190,234,56,174,108,209,1,128,153,63,188,51,93,16,174,167,88,49,47,50,227,51,199,149,103,198,167,103,147,19,247,230,
+  143,94,239,104,238,218,183,117,83,40,17,11,225,227,119,187,68,45,6,2,160,4,0,72,169,194,85,199,243,146,47,60,
+  251,131,148,234,17,128,65,219,50,243,182,101,94,249,227,219,143,94,26,157,44,236,221,125,236,18,238,63,44,97,122,166,
+  12,169,24,0,3,0,11,44,175,170,233,169,72,127,99,36,240,85,103,251,202,110,0,83,117,105,235,186,38,118,110,123,
+  39,233,118,62,29,69,162,113,5,52,65,190,3,214,107,119,241,95,63,223,188,128,39,227,180,39,213,107,23,135,39,171,
+  87,237,169,37,223,188,227,170,0,0,246,91,168,2,48,106,4,174,155,223,0,236,4,224,62,65,88,183,45,243,158,47,
+  224,157,63,250,190,6,16,152,25,23,237,127,212,169,243,127,229,164,84,61,182,101,22,235,89,135,179,99,171,74,229,165,
+  142,223,71,198,111,0,136,233,0,64,4,16,17,238,254,87,134,33,24,39,7,255,36,0,95,212,147,83,153,108,152,136,
+  238,246,110,92,123,38,63,51,151,2,160,1,168,10,0,16,68,4,0,29,137,16,86,183,132,241,229,254,183,169,173,37,
+  114,236,149,61,63,223,74,101,178,233,154,198,246,21,134,174,231,242,179,7,242,133,226,235,154,70,174,109,153,174,88,118,
+  64,120,92,113,113,97,104,138,61,143,209,190,50,138,195,59,222,8,125,208,151,124,185,181,57,114,45,189,107,192,53,116,
+  241,141,84,50,62,247,104,1,77,141,65,210,132,24,4,0,255,145,168,255,199,161,165,137,220,220,228,165,225,201,206,204,
+  150,77,225,68,44,136,238,181,9,122,174,61,30,42,150,93,252,59,91,130,227,120,20,9,26,56,119,121,180,226,73,117,
+  4,0,150,31,73,49,77,228,230,78,223,60,185,45,249,160,176,240,73,255,247,55,230,143,15,220,44,13,223,206,35,63,
+  91,70,213,149,16,68,40,204,151,228,185,203,163,139,82,169,140,109,153,99,0,224,183,240,19,128,237,182,101,114,45,48,
+  3,192,123,13,134,182,87,49,111,144,82,197,13,93,155,33,194,80,213,145,135,108,203,252,219,15,247,127,46,85,118,106,
+  19,101,204,198,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Remove = {  //size: 247
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,214,1,11,15,59,34,0,92,18,194,0,0,0,132,73,
+  68,65,84,56,203,237,145,177,9,131,80,16,134,191,7,105,220,192,58,43,232,8,25,35,173,109,70,8,25,32,11,136,
+  173,115,196,5,116,136,212,182,34,68,243,238,79,33,202,179,179,51,133,31,28,28,7,255,119,28,7,7,251,227,230,38,
+  205,202,26,72,54,230,154,186,184,166,0,167,96,152,60,111,23,70,19,14,24,189,86,9,105,42,7,220,243,215,178,40,
+  20,208,125,60,239,182,71,2,51,97,130,126,16,222,139,175,9,51,56,199,209,74,28,10,154,71,94,109,62,225,248,254,
+  63,241,3,172,83,45,219,70,228,128,216,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Save = {  //size: 911
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,11,10,13,55,15,158,42,216,191,0,0,0,140,116,
+  69,88,116,67,111,109,109,101,110,116,0,77,101,110,117,45,115,105,122,101,100,32,105,99,111,110,10,61,61,61,61,61,
+  61,61,61,61,61,10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83,
+  116,101,105,110,101,114,44,32,10,104,116,116,112,58,47,47,106,105,109,109,97,99,46,109,117,115,105,99,104,97,108,108,
+  46,99,122,10,10,99,114,101,97,116,101,100,32,119,105,116,104,32,116,104,101,32,71,73,77,80,44,10,104,116,116,112,
+  58,47,47,119,119,119,46,103,105,109,112,46,111,114,103,103,138,199,71,0,0,2,132,73,68,65,84,56,203,157,146,77,
+  72,84,97,20,134,159,185,221,241,230,207,228,76,224,181,49,205,145,178,81,108,81,81,52,38,20,184,200,22,69,20,46,
+  66,40,44,130,138,169,193,86,45,42,202,69,17,84,16,6,82,180,40,10,34,176,40,40,106,91,208,34,137,68,138,166,
+  69,153,84,106,134,164,232,220,155,206,253,249,78,11,97,108,178,54,189,155,243,193,225,60,156,247,61,95,224,200,217,227,
+  39,63,91,21,218,216,184,51,9,84,1,91,129,134,160,38,217,208,194,159,63,22,25,206,205,187,231,207,157,224,31,10,
+  52,119,116,237,141,173,172,143,85,151,155,131,171,107,202,58,170,35,133,107,109,207,39,253,45,195,235,222,231,234,195,215,
+  81,226,145,55,203,186,59,239,12,255,21,144,60,125,190,219,157,26,59,236,137,198,168,31,102,218,40,99,129,30,164,173,
+  101,35,203,43,22,115,249,254,11,252,161,62,98,5,227,127,155,239,36,121,44,37,95,70,6,68,68,100,224,115,90,122,
+  211,3,146,186,246,68,18,7,47,72,207,171,47,210,245,244,173,180,158,186,46,239,6,135,229,119,89,150,37,169,142,164,
+  232,134,238,179,180,60,198,196,196,4,225,146,114,180,192,36,133,226,0,208,247,113,20,179,68,231,251,120,134,247,3,159,
+  88,178,168,16,2,0,66,65,65,1,0,186,231,130,40,193,87,138,237,39,110,204,219,177,119,112,2,215,243,185,216,243,
+  146,139,61,47,1,120,124,118,31,200,108,95,3,80,162,80,190,162,235,80,35,0,237,187,119,210,152,88,199,204,140,77,
+  52,164,209,148,88,207,222,214,237,0,236,105,94,129,242,21,34,50,7,16,17,34,145,48,21,101,49,46,29,88,199,205,
+  187,15,136,199,170,208,8,160,17,32,81,95,205,173,123,143,104,219,92,195,142,166,245,20,21,23,229,3,0,44,203,194,
+  52,77,170,163,181,57,72,227,170,229,212,198,42,185,124,251,33,109,155,107,216,181,41,129,105,154,56,142,147,179,168,3,
+  40,165,176,109,27,0,211,52,49,77,147,238,163,133,28,187,218,67,214,245,217,223,82,79,251,182,45,104,154,198,212,212,
+  20,182,109,19,10,133,230,0,134,97,80,89,89,153,23,222,154,134,13,60,187,178,1,0,219,182,113,28,135,233,233,233,
+  121,33,235,0,253,253,253,184,174,155,215,240,60,15,93,215,115,239,63,21,137,68,242,51,240,60,143,116,58,205,200,200,
+  8,153,76,6,17,65,68,48,12,131,161,161,33,66,161,80,174,90,150,133,101,89,249,87,80,74,81,92,92,68,48,24,
+  204,125,18,0,215,117,17,53,11,19,53,123,62,229,251,249,22,194,225,48,165,165,165,68,163,81,0,178,217,44,64,46,
+  237,120,93,28,165,20,241,186,58,148,82,44,137,70,115,150,3,169,142,228,25,224,52,255,167,206,95,147,59,51,76,213,
+  34,98,109,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Search = {  //size: 935
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,6,29,17,18,8,200,223,108,120,0,0,3,52,73,
+  68,65,84,56,203,165,147,91,104,84,119,16,198,103,254,231,178,231,156,205,222,226,110,54,169,27,52,136,26,139,151,54,
+  27,91,179,84,173,164,62,24,124,80,52,20,20,162,146,66,169,171,173,15,53,77,53,84,90,52,68,193,18,164,84,104,
+  107,21,164,133,38,47,125,170,180,198,16,47,72,172,104,64,163,193,91,196,100,147,205,222,183,155,205,238,158,61,57,243,
+  247,37,27,90,241,173,31,12,51,204,195,143,111,24,62,132,87,212,214,126,248,55,189,144,223,138,136,42,113,66,206,249,
+  140,205,102,191,216,121,188,235,19,120,141,196,210,240,69,251,225,13,185,124,254,175,230,157,205,178,195,81,142,69,195,228,
+  38,7,144,5,180,78,103,82,31,103,50,211,123,237,118,91,99,231,241,174,155,255,6,32,0,192,177,175,59,214,166,211,
+  233,91,193,253,159,97,223,221,201,226,72,82,44,40,154,166,154,196,165,92,54,155,241,191,1,74,227,219,62,185,251,204,
+  105,170,240,84,212,119,28,61,54,244,31,7,137,68,114,224,211,3,135,240,247,219,81,99,74,183,75,187,54,249,178,11,
+  23,104,255,152,68,174,161,231,233,196,141,145,216,162,233,191,195,197,131,193,67,114,247,153,111,175,1,128,173,4,16,58,
+  190,58,210,230,175,171,223,60,154,20,205,209,25,77,250,168,113,73,164,202,165,162,34,51,137,8,100,213,34,166,114,250,
+  236,195,231,73,170,177,80,206,240,186,20,92,189,102,85,246,234,192,181,91,0,0,44,145,136,159,244,215,249,165,193,23,
+  134,28,88,238,54,4,6,110,221,48,157,121,125,86,51,137,4,211,52,221,145,148,238,65,192,240,112,4,228,119,214,190,
+  171,68,163,145,238,249,19,36,73,226,4,12,173,118,7,40,178,120,47,147,55,152,98,114,93,20,176,72,196,151,246,220,
+  8,157,52,136,202,128,131,43,109,136,159,171,170,6,146,36,241,121,0,17,205,90,36,81,146,24,231,79,38,166,167,24,
+  218,152,171,12,76,189,104,134,199,98,185,144,78,20,23,56,18,48,174,204,26,134,169,235,186,64,68,198,60,192,235,245,
+  222,153,152,152,168,119,200,50,62,30,143,151,143,199,115,55,57,131,40,34,139,32,113,183,0,48,3,200,173,201,88,116,
+  125,237,162,114,76,38,19,122,101,101,213,96,9,192,68,81,252,177,127,224,74,120,171,223,43,168,86,53,96,114,178,1,
+  71,6,156,139,28,200,65,8,114,161,80,168,112,121,60,239,111,121,203,195,126,190,112,46,147,78,167,250,231,191,112,165,
+  175,127,104,197,155,181,109,86,139,168,175,92,86,163,141,198,139,245,177,112,200,163,170,234,51,226,84,27,25,127,177,221,
+  229,118,239,222,177,74,132,193,235,127,114,85,209,48,22,139,53,212,44,89,124,225,225,131,145,172,0,0,176,99,231,246,
+  203,195,15,238,239,231,185,212,212,174,166,128,221,0,177,114,166,200,63,224,40,172,169,91,90,81,181,205,239,134,115,103,
+  79,83,96,93,128,77,78,77,74,68,166,196,24,107,105,104,88,215,135,37,43,93,167,78,172,30,27,27,255,195,227,246,
+  20,156,206,114,217,95,231,119,86,87,87,219,122,122,123,30,61,121,250,72,213,243,198,55,78,151,253,167,166,45,77,112,
+  111,248,62,108,120,111,35,253,242,235,69,134,175,134,163,235,212,137,125,0,208,28,10,133,54,2,128,230,243,249,46,1,
+  64,111,123,219,209,243,193,96,176,19,25,255,114,79,203,94,176,90,203,224,251,179,223,1,190,38,96,108,174,176,148,149,
+  185,206,1,0,90,91,91,91,84,205,242,195,220,238,67,248,191,122,9,59,169,102,147,104,79,162,5,0,0,0,0,73,
+  69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Settings = {  //size: 611
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,9,26,12,19,57,139,99,194,72,0,0,1,240,73,
+  68,65,84,56,203,165,146,75,104,19,97,20,133,191,153,248,76,138,77,20,10,130,74,176,204,198,133,182,221,69,172,46,
+  52,20,98,102,235,66,112,33,84,164,168,181,45,18,82,2,21,3,67,66,160,107,65,116,101,64,138,226,194,137,141,208,
+  98,87,130,210,54,137,73,23,21,130,85,105,211,151,134,32,68,68,58,51,110,102,134,49,29,235,194,179,251,185,231,156,
+  123,239,185,63,252,39,4,231,35,157,81,162,128,10,200,241,88,34,247,23,77,52,157,81,108,142,216,82,84,251,175,246,
+  3,168,166,153,171,248,250,181,1,205,108,180,29,233,140,98,52,26,117,35,157,81,12,247,90,195,170,69,1,196,116,70,
+  137,90,100,171,171,215,219,230,102,28,5,120,240,240,62,241,88,66,6,114,0,34,160,14,222,28,50,158,62,155,48,0,
+  245,246,173,17,54,215,215,144,58,37,234,245,175,247,90,179,137,221,25,21,44,49,128,96,21,111,12,12,26,154,182,37,
+  232,186,110,232,186,70,97,126,78,40,188,47,2,200,59,5,43,180,116,176,17,233,187,72,185,82,97,185,246,5,32,25,
+  143,37,238,254,243,140,78,188,202,191,52,74,149,18,221,39,123,40,150,11,152,19,76,2,186,171,65,48,146,58,12,212,
+  172,247,149,144,78,215,9,137,163,71,142,209,108,54,121,55,247,22,183,53,60,78,241,248,112,152,190,80,39,29,1,47,
+  217,153,77,142,183,127,75,46,125,94,58,215,125,170,135,128,63,192,74,109,249,242,133,240,249,249,233,169,215,85,192,0,
+  16,131,145,84,23,80,123,52,38,51,91,94,100,182,188,72,199,65,31,0,230,222,114,46,255,2,73,146,56,123,186,215,
+  250,100,118,30,34,80,28,31,14,51,245,166,196,150,166,35,238,243,179,81,111,218,35,154,35,203,217,39,143,57,208,238,
+  231,76,168,23,96,204,105,0,192,238,93,30,246,248,14,241,243,199,47,178,249,5,128,112,171,73,46,175,82,253,88,253,
+  35,108,17,96,101,227,59,251,125,1,218,246,122,120,62,243,1,32,252,105,114,116,218,73,52,77,146,107,235,171,11,64,
+  210,190,66,48,146,186,4,76,56,184,219,196,59,225,55,55,226,213,246,234,188,84,188,0,0,0,0,73,69,78,68,174,
+  66,96,130,
+};
+const nall::vector<uint8_t> Stop = {  //size: 820
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,10,17,23,45,13,107,192,156,192,0,0,2,193,73,
+  68,65,84,56,203,109,147,187,79,83,97,24,135,159,115,233,5,109,141,198,200,224,37,134,198,64,43,120,105,106,184,9,
+  169,54,136,154,152,144,184,177,16,88,252,15,92,58,56,24,105,28,28,76,28,140,11,113,208,193,187,49,184,152,80,36,
+  154,176,136,193,196,14,14,40,181,212,22,74,105,161,7,225,180,231,59,159,3,165,130,242,38,95,190,228,77,158,223,123,
+  249,229,85,216,18,143,194,97,61,61,49,241,68,192,85,118,8,13,198,4,92,142,66,101,51,167,252,3,63,107,244,249,
+  46,93,188,118,205,173,104,26,0,82,74,16,2,105,89,124,120,248,208,252,50,51,243,94,192,149,40,88,53,129,42,252,
+  194,239,243,245,70,134,134,220,243,227,227,152,249,252,182,234,78,175,151,250,142,14,62,60,125,106,38,146,201,49,1,125,
+  81,176,180,145,80,200,145,157,156,124,25,56,118,172,55,50,56,184,1,175,174,130,101,109,19,16,170,202,90,58,77,32,
+  18,209,87,231,230,142,44,149,74,103,6,195,225,231,90,103,38,243,230,120,99,99,207,249,129,1,119,54,30,199,170,175,
+  71,143,197,32,153,68,102,179,27,179,159,56,193,222,187,119,169,36,18,24,95,191,114,60,28,214,141,84,234,232,143,68,
+  34,168,245,168,234,227,254,235,215,245,204,187,119,148,11,5,212,190,62,212,150,22,148,142,14,248,254,29,117,255,126,246,
+  12,15,227,218,183,15,69,74,74,227,227,252,46,20,104,14,135,245,169,207,159,3,186,162,40,32,37,229,66,97,163,213,
+  145,17,112,185,112,245,246,226,190,113,3,93,81,112,122,60,152,241,56,249,59,119,176,129,117,195,64,10,129,2,168,255,
+  121,37,37,226,254,125,228,212,20,206,93,187,112,122,189,88,211,211,228,98,49,132,109,99,3,54,32,171,59,82,119,244,
+  187,185,25,119,40,132,174,235,56,28,14,118,7,131,212,133,66,200,42,44,202,101,196,220,220,95,1,41,68,13,86,91,
+  90,240,222,186,133,211,235,101,61,30,103,117,116,20,167,199,131,239,222,61,60,237,237,152,185,28,198,183,111,200,149,21,
+  0,116,41,229,134,154,97,32,44,139,186,182,54,92,85,120,113,120,24,219,52,209,128,3,253,253,212,157,58,197,250,131,
+  7,160,105,216,213,162,218,5,41,187,43,197,226,225,64,91,155,86,74,36,40,189,126,77,229,231,79,22,110,222,164,156,
+  201,80,94,92,36,247,234,21,107,179,179,36,111,223,70,209,52,124,126,63,233,165,37,251,199,242,114,94,137,129,83,131,
+  183,39,27,26,186,91,131,65,87,42,30,103,173,88,172,45,107,235,83,28,14,26,154,154,72,23,10,226,99,58,189,96,
+  67,167,54,6,34,2,79,114,197,98,151,105,24,135,2,173,173,122,105,126,158,138,105,110,131,85,135,131,6,191,159,217,
+  124,94,76,254,250,149,178,161,61,10,169,218,49,109,118,226,63,120,176,251,236,233,211,46,69,8,108,203,66,90,86,237,
+  79,36,147,214,167,108,118,198,134,174,40,44,110,187,198,173,34,2,122,118,178,87,133,105,27,206,69,97,121,51,247,7,
+  199,77,82,246,215,134,247,20,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Application {
+const nall::vector<uint8_t> Browser = {  //size: 928
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,26,7,44,7,230,25,222,143,0,0,0,53,116,
+  69,88,116,67,111,109,109,101,110,116,0,40,99,41,32,50,48,48,52,32,74,97,107,117,98,32,83,116,101,105,110,101,
+  114,10,10,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,144,217,139,111,0,0,2,236,
+  73,68,65,84,56,203,173,147,93,104,91,101,28,135,159,247,156,247,156,99,147,52,105,155,230,163,141,109,214,208,162,44,
+  184,117,178,200,196,171,41,168,48,20,111,148,41,221,69,101,56,16,111,132,161,160,183,82,16,197,251,93,136,21,71,65,
+  196,233,141,76,134,55,179,48,181,179,150,141,118,43,174,235,92,63,179,156,44,31,77,114,146,115,146,156,215,11,217,196,
+  237,214,231,250,255,123,46,254,252,126,130,7,56,254,222,236,41,224,45,93,202,253,10,44,229,251,13,165,88,208,116,237,
+  236,220,204,212,23,15,222,139,251,193,247,191,156,52,117,237,199,137,177,100,226,200,129,52,99,169,40,1,203,162,92,111,
+  178,182,105,179,184,178,201,141,141,194,101,33,196,201,185,153,169,171,255,17,188,246,238,231,185,96,184,231,183,87,142,30,
+  20,7,39,226,148,235,77,236,157,13,100,40,14,61,253,224,216,164,18,81,22,86,182,57,63,191,114,23,33,158,189,39,
+  145,0,209,104,232,167,231,159,201,138,39,50,3,148,138,121,6,71,179,68,6,135,112,90,109,242,155,235,72,223,67,235,
+  54,121,46,151,161,235,171,232,133,75,215,207,0,79,3,104,111,126,248,213,71,201,120,127,248,104,110,28,67,243,233,200,
+  16,104,2,67,74,76,67,18,176,36,137,104,47,67,241,24,177,190,32,185,236,163,164,135,7,142,188,241,193,217,105,0,
+  45,28,9,156,60,156,29,165,82,46,114,199,13,16,31,222,135,82,224,43,64,104,8,223,99,183,212,224,251,139,203,92,
+  152,95,36,98,41,246,143,15,3,188,10,32,107,78,59,150,78,246,179,112,163,64,102,34,142,175,254,253,108,189,82,226,
+  169,201,44,66,8,148,82,108,239,222,161,213,108,50,20,139,0,28,6,208,90,94,71,179,44,19,45,20,167,82,111,97,
+  215,60,28,183,75,205,241,240,234,54,66,252,163,19,66,144,136,69,185,186,122,19,203,144,0,3,0,82,215,53,191,218,
+  104,105,193,214,14,33,105,81,104,12,82,52,76,58,93,159,86,169,195,100,187,141,235,122,244,134,130,56,142,195,190,84,
+  140,66,211,3,40,1,72,67,211,242,183,182,138,195,135,198,147,100,70,134,184,189,185,205,252,218,30,30,38,77,47,200,
+  167,223,94,193,18,93,222,126,249,0,181,90,141,165,107,55,161,55,13,240,59,128,166,186,221,115,75,171,91,120,190,4,
+  32,61,146,226,216,161,24,187,133,18,119,171,13,170,174,224,118,217,199,182,109,170,213,42,143,244,167,88,93,207,3,124,
+  3,160,47,253,124,238,252,120,238,165,211,66,215,205,72,208,160,199,212,249,101,113,153,229,188,162,214,112,217,107,184,236,
+  57,46,185,17,157,194,158,199,86,217,231,218,218,206,175,115,51,83,239,220,47,82,165,80,60,113,241,178,255,157,239,195,
+  147,143,37,248,225,74,149,178,103,226,117,186,184,94,7,203,16,108,84,20,127,21,218,92,250,99,173,44,132,56,245,208,
+  22,142,77,127,252,250,104,102,100,54,28,233,51,31,207,36,73,14,134,177,44,3,167,213,102,215,174,240,231,173,60,91,
+  59,197,235,66,151,199,31,218,194,61,250,6,198,204,23,78,156,158,213,173,192,139,232,178,15,132,80,202,239,168,78,123,
+  93,9,237,179,175,63,153,62,195,255,205,223,112,108,55,247,49,218,29,149,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Calculator = {  //size: 686
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,6,6,13,47,29,93,206,74,167,0,0,0,53,116,
+  69,88,116,67,111,109,109,101,110,116,0,40,99,41,32,50,48,48,52,32,74,97,107,117,98,32,83,116,101,105,110,101,
+  114,10,10,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,144,217,139,111,0,0,1,250,
+  73,68,65,84,56,203,157,147,193,106,83,65,20,134,191,57,51,193,94,210,214,146,54,8,90,109,11,117,97,82,72,218,
+  110,196,165,175,16,74,23,69,16,116,227,86,220,27,10,238,196,39,80,240,61,164,186,44,90,219,36,205,109,192,130,138,
+  74,139,168,237,197,132,18,99,114,231,184,72,188,165,139,34,118,54,195,240,207,124,115,254,255,204,152,165,229,210,67,160,
+  204,217,70,217,44,45,151,244,206,221,219,103,58,253,236,233,115,220,223,197,230,214,203,83,55,182,90,71,164,211,1,237,
+  118,7,107,133,161,161,115,44,46,220,4,56,6,52,155,7,167,2,62,188,223,195,90,33,58,108,209,108,30,113,253,198,
+  92,162,37,128,153,153,185,83,1,83,83,57,140,49,168,42,198,24,140,49,137,102,150,150,75,26,53,219,60,184,127,239,
+  191,252,175,62,122,194,165,11,153,126,5,221,110,143,242,234,99,126,117,58,125,85,21,253,7,64,68,142,45,220,90,89,
+  225,213,218,11,188,135,139,147,147,124,221,223,199,251,152,145,243,99,180,126,70,168,247,4,233,17,140,8,209,225,1,34,
+  134,254,13,30,23,4,1,149,173,55,92,157,157,69,85,9,195,144,124,62,143,181,150,48,12,201,229,114,136,8,141,70,
+  3,197,176,56,95,36,149,74,209,237,118,169,213,43,72,187,221,198,199,30,85,77,66,2,136,227,24,48,116,58,29,188,
+  247,0,120,85,172,181,232,96,78,44,92,153,154,225,245,250,58,24,195,68,54,203,206,78,3,239,99,198,50,227,188,219,
+  221,5,85,198,50,19,40,80,171,213,18,72,2,248,252,233,35,197,98,161,111,161,209,32,151,187,134,115,142,122,189,206,
+  124,177,136,136,80,173,214,112,206,81,40,20,146,42,55,43,27,200,32,118,156,115,120,239,241,189,30,214,90,156,115,136,
+  72,210,119,17,3,244,75,79,165,82,39,187,48,121,121,154,202,230,91,192,48,158,205,178,93,175,227,172,37,61,60,204,
+  118,24,98,84,25,10,2,126,15,218,60,58,58,74,20,69,39,95,226,252,194,2,0,214,90,226,233,105,12,96,68,208,
+  65,128,70,132,189,189,47,124,255,241,141,195,232,96,16,114,31,80,174,86,55,206,252,157,255,0,132,92,203,16,0,89,
+  204,210,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Calendar = {  //size: 603
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,251,0,251,0,251,85,89,109,150,0,0,0,9,112,72,89,115,0,0,11,18,0,
+  0,11,18,1,210,221,126,252,0,0,0,7,116,73,77,69,7,213,6,3,15,29,43,75,94,175,19,0,0,0,62,116,
+  69,88,116,67,111,109,109,101,110,116,0,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,
+  10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83,116,101,105,110,101,
+  114,39,51,239,88,0,0,1,158,73,68,65,84,56,203,157,147,177,138,26,81,20,134,191,59,115,103,28,80,80,176,16,
+  123,11,5,17,130,1,21,124,5,43,9,193,98,11,193,224,110,49,181,181,189,196,128,32,90,167,77,47,250,2,98,37,
+  34,193,38,33,149,133,173,144,197,217,48,222,20,50,119,103,214,221,37,155,83,205,156,123,206,119,207,255,159,25,241,225,
+  99,243,27,208,228,255,98,34,129,102,247,211,221,155,59,227,241,56,159,191,12,110,101,144,216,108,54,0,8,33,254,9,
+  80,171,213,0,144,0,153,76,134,82,169,116,213,44,132,120,54,39,165,228,124,62,95,0,229,119,239,41,22,139,36,147,
+  201,200,4,225,230,167,32,199,113,72,165,82,44,22,11,228,75,55,62,125,14,194,52,77,12,195,0,192,243,78,24,225,
+  230,201,100,66,181,90,37,159,207,227,186,174,206,151,203,101,178,217,44,167,211,9,33,4,190,239,107,96,4,208,233,116,
+  88,173,86,244,251,125,102,179,153,214,57,26,141,46,122,165,196,182,109,76,211,212,0,25,6,236,118,59,218,237,54,135,
+  195,1,215,117,117,161,109,219,186,198,48,140,136,36,35,236,65,46,151,99,189,94,51,24,12,24,143,199,248,190,31,209,
+  44,165,68,41,133,101,89,215,19,0,244,122,61,150,203,37,158,231,209,237,118,113,28,7,203,178,104,181,90,212,235,117,
+  230,243,57,141,70,131,88,44,246,188,132,233,116,170,71,12,118,45,132,96,191,223,235,13,72,41,35,18,52,32,157,78,
+  107,211,2,151,149,82,40,165,16,66,160,148,194,247,125,125,150,72,36,30,1,199,227,145,225,112,120,245,13,188,246,94,
+  40,20,46,128,135,135,63,108,183,91,42,149,202,139,197,225,8,12,13,124,144,219,239,155,27,224,235,253,253,239,55,255,
+  145,63,127,253,184,249,11,104,130,125,246,45,41,107,239,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Chat = {  //size: 422
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,6,6,13,5,59,224,168,3,114,0,0,1,51,73,
+  68,65,84,56,203,149,147,65,78,132,48,24,133,223,79,155,176,109,34,18,247,19,111,97,244,30,198,149,196,3,152,56,
+  25,131,58,139,137,16,96,244,4,70,99,226,17,52,46,245,2,156,132,160,48,43,23,99,169,27,74,42,3,136,127,194,
+  162,180,239,235,123,253,91,130,81,81,18,28,1,120,194,248,154,163,5,80,255,169,40,9,20,239,194,230,121,14,165,20,
+  148,82,189,91,187,174,11,0,224,81,18,92,1,88,232,137,120,25,14,122,222,223,59,192,100,178,219,140,57,128,197,108,
+  234,143,14,29,47,195,13,0,0,32,203,178,94,17,17,129,136,224,56,78,51,222,0,12,9,77,193,104,128,41,110,67,
+  180,147,78,64,151,176,237,64,159,69,85,41,143,247,217,29,3,241,207,47,31,248,95,150,187,0,70,171,137,155,185,138,
+  162,0,17,65,8,49,216,74,249,45,79,24,103,119,0,20,133,209,245,177,101,209,253,108,234,163,44,75,41,132,96,67,
+  151,105,123,107,231,214,243,188,179,198,130,126,3,250,50,197,203,16,159,31,197,225,203,243,235,187,94,196,24,171,108,219,
+  150,0,214,105,154,126,1,88,155,93,32,51,215,170,92,157,38,241,205,91,61,103,1,144,0,170,250,83,245,255,95,0,
+  165,115,93,248,243,71,0,172,22,178,122,77,213,130,72,51,210,15,141,12,145,160,100,148,198,107,0,0,0,0,73,69,
+  78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> FileManager = {  //size: 378
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,6,16,18,41,48,44,67,93,90,0,0,1,7,73,
+  68,65,84,56,203,189,146,189,74,3,65,28,196,127,251,145,69,177,176,73,44,124,128,128,207,225,19,8,130,136,31,129,
+  136,104,37,34,104,173,32,10,130,181,165,112,6,242,20,190,78,138,120,8,90,104,178,183,123,107,17,15,205,37,94,238,
+  82,56,229,252,255,59,179,179,179,226,168,221,4,216,19,130,136,10,8,129,125,224,73,103,135,175,78,46,73,146,1,225,
+  123,90,4,33,36,215,15,183,81,8,32,142,15,154,225,188,125,138,148,211,151,245,48,198,153,250,4,175,134,49,55,157,
+  14,58,35,30,163,59,250,214,148,186,254,138,177,180,118,47,70,6,25,217,183,134,237,213,151,82,2,221,94,131,193,231,
+  59,90,215,144,204,9,169,70,222,114,150,83,183,215,40,20,210,191,115,253,181,156,231,151,148,35,245,14,231,146,31,129,
+  245,250,219,92,81,52,128,181,31,60,199,203,149,90,216,202,71,168,218,130,79,253,236,71,44,213,6,64,234,221,255,182,
+  48,33,96,236,43,155,27,135,99,131,212,123,164,82,164,222,231,62,208,56,39,90,59,107,247,139,11,254,76,235,218,84,
+  7,231,146,194,8,95,134,90,101,183,231,143,210,134,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Mail = {  //size: 550
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,18,0,
+  0,11,18,1,210,221,126,252,0,0,0,7,116,73,77,69,7,213,6,6,12,23,54,231,47,101,43,0,0,1,179,73,
+  68,65,84,56,203,197,147,65,72,20,97,24,134,159,89,70,247,119,112,23,215,177,173,131,167,234,88,104,23,221,240,212,
+  146,134,134,129,6,65,25,102,97,68,68,224,69,139,60,111,108,219,65,17,4,145,144,78,118,232,32,136,29,234,166,201,
+  222,108,219,14,9,230,213,52,88,171,63,98,254,29,115,103,60,168,131,131,163,16,8,126,199,247,251,222,231,127,127,62,
+  62,56,238,210,130,196,116,38,85,15,228,2,90,23,158,14,12,126,62,148,152,206,164,234,211,153,148,27,84,233,76,202,
+  221,129,7,39,216,125,249,73,255,51,150,22,243,252,150,146,98,209,230,231,250,47,254,252,149,116,223,185,207,139,151,207,
+  125,73,180,131,204,185,124,158,120,252,20,85,85,49,52,45,132,82,54,170,88,36,153,76,250,32,250,158,0,185,219,183,
+  186,249,240,126,134,229,229,111,180,180,92,37,86,93,77,69,133,129,97,24,0,100,179,89,166,167,222,114,175,167,151,137,
+  215,175,114,128,182,23,128,105,214,96,89,54,61,119,31,120,166,221,42,20,10,152,166,137,8,235,68,34,81,79,247,1,
+  132,16,52,54,54,240,110,102,154,75,201,203,0,88,150,133,82,10,165,20,139,95,191,208,126,173,3,33,68,48,0,224,
+  68,60,78,226,98,130,55,147,147,156,61,115,26,128,242,176,206,202,202,15,58,175,119,162,135,252,155,215,131,86,25,141,
+  68,49,99,149,52,95,105,246,180,209,145,33,202,117,13,219,182,17,84,122,122,104,159,219,117,153,155,155,229,198,205,46,
+  79,114,74,155,60,124,244,152,143,243,243,40,101,249,198,247,1,62,45,44,208,218,214,198,230,134,141,235,56,148,254,109,
+  160,161,225,148,28,234,206,159,227,251,234,218,193,95,144,82,210,144,104,218,110,232,97,223,96,25,32,140,8,53,39,107,
+  145,82,6,2,134,199,198,71,251,254,227,142,134,143,228,26,183,0,252,253,172,100,78,184,216,168,0,0,0,0,73,69,
+  78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Monitor = {  //size: 611
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,10,19,17,58,52,159,194,36,103,0,0,1,240,73,
+  68,65,84,56,203,165,147,61,104,20,65,24,134,159,221,153,157,221,189,194,20,34,18,209,75,97,12,66,208,72,2,1,
+  33,96,97,115,96,17,36,106,180,17,82,88,137,193,70,17,15,46,133,164,176,178,49,130,149,86,134,147,72,136,88,156,
+  66,42,27,65,68,61,73,180,9,130,7,241,206,52,10,130,119,59,127,22,49,49,33,39,74,124,225,101,186,103,158,225,
+  251,38,40,149,138,15,128,115,108,35,222,251,105,74,165,162,111,181,154,190,217,106,250,191,197,57,231,51,157,173,183,84,
+  42,122,185,70,155,184,59,79,185,82,253,167,155,71,11,135,185,112,162,7,0,9,16,69,138,114,165,138,234,0,37,99,
+  136,20,103,142,46,67,16,50,251,166,11,156,7,60,24,139,54,134,114,165,202,248,233,62,0,194,141,100,21,167,68,105,
+  202,217,161,58,23,135,7,17,74,146,38,9,105,46,69,37,41,34,151,144,36,9,0,214,152,223,6,107,17,42,70,36,
+  49,253,61,123,8,115,5,250,187,27,168,232,19,143,23,14,32,188,3,35,201,66,13,128,177,118,171,65,148,40,70,6,
+  107,236,219,213,193,203,165,6,93,249,83,136,72,146,36,49,81,162,16,113,132,138,213,159,13,78,14,212,8,3,137,200,
+  21,224,219,44,65,48,130,144,130,171,199,159,179,80,223,141,179,14,235,44,83,111,3,140,109,3,184,60,122,157,128,128,
+  143,181,25,222,47,127,37,191,23,14,229,119,114,164,211,48,216,55,142,247,0,158,169,251,183,218,3,110,207,220,68,40,
+  129,148,17,66,10,62,55,230,232,221,63,202,195,215,53,204,247,59,88,99,177,198,254,122,130,221,10,120,242,174,155,40,
+  85,168,88,17,197,10,33,63,112,176,243,25,190,89,35,12,2,158,46,245,210,252,145,1,139,237,13,96,117,222,22,71,
+  232,29,206,57,46,61,234,192,249,29,104,109,208,86,99,113,171,6,27,1,90,103,140,13,15,112,111,238,213,38,220,226,
+  60,192,139,182,155,184,242,101,101,179,193,149,243,67,92,27,59,134,214,25,89,43,67,27,141,214,107,231,134,26,77,163,
+  81,95,135,73,96,122,114,242,198,246,127,227,255,230,39,112,67,0,83,217,168,244,129,0,0,0,0,73,69,78,68,174,
+  66,96,130,
+};
+const nall::vector<uint8_t> Terminal = {  //size: 668
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,10,18,21,37,27,70,252,216,25,0,0,2,41,73,
+  68,65,84,56,203,125,147,207,75,84,81,20,199,63,119,124,51,42,100,65,134,209,194,192,18,10,87,174,92,181,168,69,
+  203,54,237,130,210,106,145,186,8,23,65,96,173,130,54,181,14,74,176,69,98,130,254,1,181,45,37,8,194,162,52,80,
+  35,157,177,24,43,29,125,142,153,239,221,95,167,197,27,103,70,45,15,28,238,189,220,115,63,247,123,14,231,168,161,231,
+  131,7,141,177,253,214,218,238,56,142,27,140,49,84,220,98,108,245,57,113,103,221,122,20,199,143,181,214,15,2,165,212,
+  253,163,77,199,122,78,182,180,166,51,181,117,128,128,240,79,243,222,177,94,44,146,95,202,55,142,79,188,190,157,91,204,
+  166,3,224,250,137,150,214,244,149,174,78,166,63,77,179,159,137,36,228,211,109,167,232,237,233,205,228,22,179,61,129,136,
+  212,41,149,226,203,220,28,163,47,31,225,197,33,34,120,113,120,239,17,73,220,121,65,196,225,189,163,243,66,63,169,148,
+  66,68,14,5,21,121,194,212,199,89,192,225,189,148,64,201,227,109,144,23,193,121,11,128,117,201,90,5,240,52,54,53,
+  36,193,222,227,177,120,239,240,37,64,108,182,8,215,151,9,55,11,0,56,231,118,2,68,132,169,15,51,244,245,222,229,
+  217,200,19,150,11,121,4,193,57,75,33,252,73,20,109,38,129,74,37,31,238,1,120,161,190,33,195,171,55,47,184,209,
+  213,71,97,109,153,193,145,135,172,21,126,65,141,165,254,64,109,9,64,41,133,93,0,47,158,217,233,121,86,127,252,161,
+  163,253,28,153,160,150,247,147,159,17,239,203,143,80,149,237,158,20,16,32,112,220,236,190,197,240,216,0,111,39,39,8,
+  210,10,168,41,41,87,59,20,184,221,69,4,248,58,251,141,75,87,47,130,8,40,120,55,62,83,190,211,58,230,204,249,
+  246,242,217,57,191,171,6,128,243,46,169,145,74,1,208,113,182,173,74,186,66,165,82,251,40,16,40,174,110,253,191,13,
+  171,242,223,81,3,165,148,6,169,111,62,222,204,194,194,66,185,93,247,179,198,35,135,137,226,8,165,212,239,0,212,112,
+  54,55,127,109,108,108,52,216,216,40,18,134,33,97,184,70,20,69,24,107,176,214,98,183,39,209,90,172,53,104,163,89,
+  89,41,104,224,105,32,226,239,228,151,190,147,205,205,95,54,198,100,180,214,149,81,54,166,12,217,30,101,173,53,70,155,
+  216,88,59,36,34,247,254,2,215,162,130,23,152,77,245,29,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> TextEditor = {  //size: 574
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,9,22,18,55,41,59,82,2,72,0,0,1,203,73,
+  68,65,84,56,203,165,147,77,104,19,65,20,199,127,51,221,163,72,22,34,45,226,177,224,41,160,88,43,1,15,130,21,
+  132,120,236,69,208,182,39,175,165,23,21,60,138,32,197,32,245,34,126,128,154,22,138,49,189,91,26,17,77,92,16,237,
+  193,98,211,141,27,76,253,192,213,108,235,16,21,90,145,205,120,48,67,179,109,106,43,190,203,255,13,204,251,207,111,102,
+  222,131,255,12,97,146,226,93,250,128,25,224,216,86,122,120,136,188,169,147,0,151,71,47,157,1,102,14,30,119,216,142,
+  182,18,88,77,189,1,240,237,249,57,182,163,87,79,74,221,172,59,45,141,211,222,84,64,89,21,88,221,147,167,172,10,
+  252,232,122,216,86,95,76,75,6,199,138,28,233,31,6,24,55,4,196,227,113,196,137,165,63,139,125,77,101,77,125,223,
+  39,127,79,50,116,237,25,31,158,140,51,183,16,132,64,135,213,122,31,165,20,245,122,125,195,75,7,65,128,155,73,173,
+  21,123,203,124,45,77,117,0,61,17,3,219,182,177,109,59,82,236,251,62,110,38,197,224,88,145,197,199,25,94,149,151,
+  81,165,28,64,207,200,100,99,246,175,4,230,228,129,116,129,197,124,134,185,183,10,85,202,241,115,255,69,206,159,189,48,
+  11,136,77,9,180,214,20,210,189,12,92,121,138,55,125,135,202,231,239,168,249,7,188,239,26,166,179,205,55,70,8,180,
+  214,100,179,89,126,201,67,204,59,14,239,62,213,89,122,61,5,189,105,118,173,172,0,218,52,161,110,75,16,134,33,187,
+  63,166,121,244,165,155,251,185,28,221,242,37,59,251,110,145,76,38,113,93,151,55,149,5,140,139,92,79,80,173,86,241,
+  60,143,145,201,6,71,59,43,172,238,72,16,30,24,37,145,72,96,89,22,150,101,69,186,120,3,65,44,22,67,107,77,
+  173,86,67,107,141,148,18,173,53,66,8,132,16,173,219,27,17,3,165,20,142,227,108,58,117,198,100,125,24,131,137,155,
+  183,175,159,250,135,41,158,48,201,111,90,157,232,152,121,9,252,18,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Device {
+const nall::vector<uint8_t> Clock = {  //size: 897
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,9,15,8,58,5,128,132,46,85,0,0,3,14,73,
+  68,65,84,56,203,101,146,75,76,92,101,28,197,127,223,119,239,188,152,185,119,96,34,8,210,202,35,88,29,108,48,150,
+  190,226,162,177,59,220,152,138,193,186,32,26,54,38,68,87,181,53,38,44,77,99,26,31,137,198,68,77,172,53,177,139,
+  62,240,17,77,76,163,46,176,5,77,85,40,5,58,88,43,218,17,233,76,167,204,12,195,204,189,195,12,119,190,207,141,
+  109,40,158,228,236,206,57,255,71,142,96,19,142,28,24,25,210,146,23,5,226,97,148,14,8,67,122,90,138,27,85,181,
+  126,230,157,207,94,127,101,179,94,108,48,246,104,248,116,247,190,71,123,182,247,198,9,71,194,8,1,90,67,113,181,196,
+  204,84,130,75,151,230,211,131,195,231,142,245,60,146,10,34,117,146,80,242,180,185,193,124,97,112,120,192,46,27,138,85,
+  159,131,40,190,118,103,138,221,113,140,120,224,65,26,59,83,205,141,205,149,55,61,189,115,213,208,191,172,8,183,93,73,
+  0,141,62,62,56,60,96,223,88,43,208,254,80,136,104,245,237,187,214,180,235,45,182,110,107,161,119,111,142,104,195,46,
+  131,192,187,13,200,167,163,192,14,121,228,192,200,208,238,125,59,118,150,180,71,91,87,35,45,91,226,155,207,68,74,137,
+  16,2,51,16,67,178,68,165,116,2,167,52,19,4,253,131,4,6,182,247,198,89,46,103,105,109,109,166,144,255,135,248,
+  227,167,72,103,125,119,63,75,8,42,94,31,158,232,39,149,172,114,242,195,7,170,120,242,156,169,209,123,194,145,48,150,
+  23,66,41,133,207,103,81,118,139,132,234,44,70,94,218,134,223,222,203,115,109,57,108,219,66,200,24,210,127,16,195,187,
+  201,194,212,199,209,161,23,250,131,18,168,23,2,154,154,26,200,23,242,40,165,168,41,141,227,20,57,250,222,239,68,98,
+  221,76,76,76,160,148,66,107,133,97,26,152,134,4,165,5,208,101,2,43,90,19,43,187,85,58,58,218,169,84,214,80,
+  74,81,44,22,233,238,238,230,229,195,175,114,237,218,85,106,181,26,82,74,180,82,104,13,72,52,176,32,5,226,162,83,
+  114,88,252,59,141,82,10,207,243,240,60,143,67,135,14,147,72,36,200,100,210,88,150,69,173,86,195,178,108,202,110,25,
+  167,228,128,207,112,63,57,253,150,35,129,179,115,147,243,216,134,133,83,114,241,249,252,140,141,141,49,58,58,74,42,181,
+  116,39,48,28,142,160,53,44,95,207,50,55,57,79,133,218,12,128,124,227,203,163,39,126,62,63,53,217,24,182,25,255,
+  254,87,180,214,244,245,61,65,58,157,98,173,82,65,107,77,32,16,192,52,77,242,153,28,238,178,203,197,31,167,171,95,
+  77,156,121,6,192,0,232,188,183,107,110,97,54,249,236,254,253,143,249,255,252,99,145,92,33,207,253,109,91,8,6,130,
+  4,131,33,252,190,0,55,255,202,144,191,190,194,201,15,206,234,217,165,217,247,167,230,127,154,3,178,6,192,76,114,58,
+  239,55,253,87,150,174,102,246,52,53,196,162,109,45,173,120,69,143,74,126,141,114,182,204,173,228,45,46,143,207,242,245,
+  231,223,174,143,255,118,225,248,249,233,239,190,1,210,64,78,108,232,74,61,208,249,228,174,254,231,239,187,103,235,83,117,
+  254,186,22,41,164,169,209,202,93,119,139,233,66,58,241,197,248,169,143,128,20,112,5,88,4,180,224,255,136,252,23,118,
+  155,26,40,3,171,64,22,40,0,234,182,248,95,201,36,100,6,22,194,54,223,0,0,0,0,73,69,78,68,174,66,96,
+  130,
+};
+const nall::vector<uint8_t> Display = {  //size: 662
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,2,19,73,68,65,84,56,141,165,147,203,106,20,65,20,134,191,83,85,
+  115,109,72,50,32,6,98,18,35,38,11,183,222,54,186,112,99,6,29,240,25,124,4,55,130,184,81,80,4,241,49,124,
+  131,108,92,8,9,17,34,226,222,96,188,129,38,16,131,97,72,156,233,234,233,158,238,58,46,58,17,19,3,65,60,155,
+  42,170,234,255,207,119,206,161,164,221,110,243,63,225,58,157,206,29,17,121,168,170,181,127,17,138,136,87,213,187,78,85,
+  31,183,111,92,143,154,141,38,121,174,199,103,116,130,181,134,110,183,91,95,90,124,245,196,169,106,109,116,116,132,249,155,
+  207,73,233,3,160,122,132,209,222,81,77,34,94,190,184,205,200,232,40,64,195,237,223,167,161,207,133,171,99,136,200,30,
+  34,128,148,171,0,90,238,223,44,111,179,250,238,61,83,83,147,37,81,154,166,128,160,226,136,211,80,10,20,68,228,128,
+  73,54,204,217,238,254,4,28,103,103,103,49,18,74,3,239,61,33,20,24,99,24,228,82,102,43,155,4,148,194,205,31,
+  59,196,62,5,160,97,90,124,88,91,99,122,122,146,36,73,112,73,146,80,20,1,140,35,201,203,108,138,32,64,156,164,
+  236,244,18,84,29,149,250,94,181,137,101,122,230,52,21,103,240,222,227,226,56,70,131,98,76,133,65,112,251,0,36,233,
+  144,108,104,112,181,232,224,248,172,229,243,199,79,204,205,205,210,239,247,75,2,69,17,107,73,131,3,5,69,81,107,169,
+  216,35,134,97,135,76,156,154,192,24,202,18,188,247,0,24,227,176,213,232,111,197,161,24,154,62,69,81,160,8,113,28,
+  227,66,40,187,217,106,56,178,205,193,177,6,81,213,33,70,49,34,37,129,115,174,183,254,109,163,245,236,209,249,99,197,
+  191,41,210,156,245,245,13,128,29,233,116,58,183,106,181,218,3,17,141,64,12,48,83,169,56,87,169,86,197,26,3,64,
+  17,2,89,150,105,62,204,135,192,87,208,160,202,174,247,201,125,151,231,249,66,158,231,11,0,170,26,181,78,140,189,157,
+  111,183,207,93,186,120,153,241,147,227,168,42,91,91,223,89,121,189,162,75,203,75,171,73,111,112,37,203,50,191,79,35,
+  135,191,179,49,102,164,222,172,63,109,70,205,107,214,154,51,128,22,161,248,50,240,131,197,222,110,255,158,136,196,127,190,
+  255,5,119,143,242,70,185,147,13,30,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Joypad = {  //size: 812
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,172,0,77,0,0,52,214,215,123,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,7,15,10,39,178,201,163,153,0,0,0,140,116,
+  69,88,116,67,111,109,109,101,110,116,0,77,101,110,117,45,115,105,122,101,100,32,105,99,111,110,10,61,61,61,61,61,
+  61,61,61,61,61,10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83,
+  116,101,105,110,101,114,44,32,10,104,116,116,112,58,47,47,106,105,109,109,97,99,46,109,117,115,105,99,104,97,108,108,
+  46,99,122,10,10,99,114,101,97,116,101,100,32,119,105,116,104,32,116,104,101,32,71,73,77,80,44,10,104,116,116,112,
+  58,47,47,119,119,119,46,103,105,109,112,46,111,114,103,103,138,199,71,0,0,2,33,73,68,65,84,56,203,149,146,203,
+  107,83,65,20,198,127,147,220,228,38,214,210,164,177,54,245,209,135,160,184,73,176,20,255,130,130,123,17,138,136,130,168,
+  32,88,8,193,133,173,15,180,221,136,46,68,10,10,130,72,22,93,213,186,19,68,208,133,187,34,84,179,19,68,20,42,
+  21,53,53,183,105,244,230,117,111,230,184,184,105,154,66,178,232,129,3,195,240,205,55,191,249,230,40,58,212,199,179,132,
+  255,170,221,51,97,41,95,2,124,235,42,178,96,80,191,58,62,95,40,181,234,84,167,195,117,124,223,250,14,237,141,27,
+  225,16,162,53,142,93,102,237,251,186,181,161,122,6,79,204,231,237,77,173,175,157,129,70,165,99,67,177,184,175,43,140,
+  235,247,99,21,107,216,174,143,129,35,253,189,192,253,86,173,175,195,11,82,129,93,97,252,166,137,17,12,16,12,26,252,
+  94,181,136,14,198,137,200,198,153,86,161,145,74,79,206,0,119,90,55,203,86,6,167,84,193,202,217,116,71,187,40,230,
+  255,225,214,28,220,170,67,69,153,209,84,250,178,52,164,179,42,149,158,148,185,135,143,182,93,255,235,229,61,170,203,115,
+  88,185,34,249,181,18,162,96,223,240,30,162,177,46,234,135,47,112,240,212,109,108,219,230,198,173,41,12,0,173,53,133,
+  66,97,11,235,248,57,114,111,30,211,63,212,199,200,104,15,134,25,160,248,243,15,185,85,135,3,231,47,98,89,22,65,
+  51,184,149,129,104,241,90,188,86,129,16,3,215,151,88,212,167,249,186,244,133,207,239,62,241,170,52,206,254,155,239,81,
+  129,16,34,2,141,71,120,4,162,209,34,32,226,125,172,0,134,137,27,234,101,228,193,42,43,43,43,228,223,190,134,128,
+  137,214,26,192,51,105,18,136,96,189,184,198,143,233,97,214,23,167,26,68,26,183,238,182,36,35,72,93,35,186,209,13,
+  3,163,153,252,242,2,82,43,81,90,94,160,251,228,93,0,142,37,71,121,250,236,73,115,93,23,221,68,111,230,181,25,
+  98,104,108,130,202,135,231,132,198,38,208,90,163,68,72,36,146,36,18,73,68,20,74,137,135,223,48,216,70,96,154,38,
+  71,175,100,128,76,219,169,178,109,111,114,203,229,50,0,149,74,101,59,65,54,155,197,117,93,118,82,74,169,45,131,72,
+  36,210,68,2,168,86,171,0,212,106,181,142,6,142,227,120,70,237,70,121,7,53,251,31,168,192,0,159,97,230,172,204,
+  0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Keyboard = {  //size: 587
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,1,200,73,68,65,84,56,141,181,146,177,107,83,81,20,198,127,247,222,
+  115,95,95,8,73,159,136,180,209,38,177,21,7,165,21,26,16,204,228,34,102,8,8,34,212,168,127,72,193,191,160,131,
+  45,174,29,178,184,180,116,17,212,12,25,4,23,137,32,168,56,85,227,146,162,85,112,145,212,190,151,52,125,185,14,109,
+  53,29,164,193,226,7,151,115,46,231,124,223,189,231,240,169,82,169,196,113,160,15,146,122,189,174,142,37,48,191,48,159,
+  30,134,80,169,84,252,193,187,58,24,161,88,156,157,242,60,223,3,239,187,136,216,100,210,88,99,196,199,152,139,190,245,
+  175,2,87,28,76,3,30,74,189,72,38,82,229,106,181,26,203,31,45,127,195,57,123,201,250,186,108,69,143,59,103,50,
+  32,99,86,108,216,239,179,174,148,187,15,242,174,213,106,253,204,159,157,104,110,111,255,40,0,175,127,11,52,26,141,93,
+  224,205,254,249,219,247,131,252,228,196,50,142,157,173,173,206,251,67,59,56,10,115,119,230,174,163,226,183,202,169,200,104,
+  175,80,171,213,186,0,114,20,113,239,229,91,151,193,61,82,112,111,101,101,237,249,96,77,45,173,45,37,78,118,131,166,
+  159,24,57,163,181,70,27,141,86,123,209,104,131,214,10,173,13,206,57,0,246,123,94,61,88,120,88,4,144,157,86,120,
+  45,83,184,112,106,102,122,6,107,61,68,4,43,22,99,4,173,13,56,71,223,245,137,162,136,48,10,49,90,211,104,188,
+  156,173,84,110,158,95,93,125,220,20,17,239,118,144,62,225,133,97,135,205,175,159,72,37,211,160,0,20,74,41,218,237,
+  54,249,92,142,56,142,249,178,249,153,108,54,79,106,52,144,245,15,31,111,0,139,98,180,46,7,65,0,206,145,61,157,
+  99,183,31,31,154,127,52,157,166,215,235,1,138,169,201,115,116,186,93,50,99,227,102,196,179,119,129,69,137,227,120,227,
+  233,179,39,90,169,225,157,236,156,3,199,55,24,112,226,191,98,104,31,252,55,129,95,252,113,137,228,164,151,154,151,0,
+  0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Microphone = {  //size: 703
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,234,0,234,0,234,127,141,58,17,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,11,3,22,42,33,208,235,186,119,0,0,2,76,73,
+  68,65,84,56,203,125,147,95,72,83,97,24,198,127,147,205,182,83,179,72,234,44,200,180,11,239,148,4,217,148,88,151,
+  94,118,37,178,65,116,209,77,105,119,65,212,141,12,138,254,152,55,117,227,134,32,212,244,106,30,70,187,216,31,208,139,
+  188,137,8,141,109,72,77,93,218,137,153,108,122,108,24,56,143,121,56,235,34,119,218,104,250,192,199,247,247,121,222,231,
+  123,191,247,51,113,136,1,79,255,35,192,199,209,152,3,178,82,40,124,187,122,209,84,33,187,156,46,95,111,239,85,186,
+  174,116,213,176,138,197,34,0,91,202,22,35,47,158,1,220,147,66,225,87,149,253,134,195,222,231,118,95,163,245,82,43,
+  197,98,209,104,138,162,160,170,42,11,11,243,172,173,174,242,224,254,67,128,151,213,1,204,149,129,67,188,128,215,235,5,
+  32,22,139,17,143,199,25,27,27,51,14,90,173,39,176,9,86,132,147,2,245,4,30,139,162,232,243,251,253,104,154,134,
+  162,40,184,221,110,186,187,187,255,94,126,238,29,0,171,107,95,145,191,203,117,5,124,51,51,51,140,142,142,26,14,162,
+  209,40,129,64,160,198,129,165,209,66,211,105,59,117,147,24,124,61,229,91,89,89,65,211,52,44,22,11,130,32,96,54,
+  155,89,94,94,70,150,191,241,99,99,131,66,33,207,230,102,129,125,245,55,246,38,251,155,233,144,116,235,72,7,137,68,
+  130,137,137,137,154,104,103,206,158,198,227,241,146,74,37,153,159,255,116,3,48,4,158,244,245,245,13,183,183,183,163,235,
+  58,249,124,158,142,142,14,130,193,32,187,187,187,200,178,76,38,147,225,195,199,247,164,82,73,50,95,150,48,97,106,168,
+  206,193,240,236,236,172,225,32,18,137,144,72,36,24,31,31,199,106,181,26,14,218,46,183,145,74,166,41,151,203,216,237,
+  167,226,53,207,216,211,211,131,36,73,0,100,179,89,68,81,100,114,114,210,32,71,99,81,28,162,131,240,91,9,193,38,
+  80,218,43,93,175,17,40,108,230,17,207,59,72,167,211,0,168,170,202,226,226,34,154,166,161,235,58,7,218,1,219,63,
+  21,0,154,155,155,41,173,151,106,11,201,31,240,51,120,103,136,206,206,78,116,93,167,92,46,163,235,58,154,166,177,243,
+  107,135,204,210,103,82,169,36,192,158,211,233,178,229,214,115,255,4,164,80,216,52,116,119,112,255,249,200,211,70,142,199,
+  18,224,0,108,255,149,178,178,189,53,221,114,177,133,220,122,14,41,20,190,121,148,194,128,167,127,170,238,95,0,34,78,
+  167,235,92,197,218,49,136,84,79,254,0,227,216,1,121,79,52,137,54,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Mouse = {  //size: 720
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,8,13,24,4,51,189,102,0,0,0,0,53,116,
+  69,88,116,67,111,109,109,101,110,116,0,40,99,41,32,50,48,48,52,32,74,97,107,117,98,32,83,116,101,105,110,101,
+  114,10,10,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,144,217,139,111,0,0,2,28,
+  73,68,65,84,56,203,141,144,205,75,84,81,24,198,127,247,220,201,171,115,239,117,162,146,92,40,217,31,208,166,85,208,
+  252,5,17,148,182,9,114,178,77,66,187,208,106,22,5,35,76,16,238,91,180,107,51,109,98,80,35,218,181,9,68,87,
+  34,81,249,17,129,76,14,51,166,57,141,227,215,124,220,243,158,22,126,52,234,72,190,155,195,251,156,231,252,206,195,99,
+  113,104,210,233,116,39,48,80,46,151,111,27,99,60,192,120,190,251,170,251,102,207,147,122,223,216,187,209,235,91,155,91,
+  253,138,163,115,105,187,188,29,243,124,247,123,44,22,243,61,223,237,43,254,89,187,150,74,165,150,82,111,82,217,209,177,
+  145,27,187,190,23,97,55,60,109,29,126,157,76,38,95,182,182,250,189,197,226,218,38,240,49,145,72,244,213,165,187,80,
+  171,85,223,27,216,106,110,118,230,122,186,111,221,179,158,39,135,18,74,169,33,108,11,165,20,149,237,128,104,52,74,46,
+  159,101,97,33,67,147,99,163,131,0,9,164,168,148,250,240,244,89,162,183,254,195,80,40,20,26,186,223,255,192,216,182,
+  109,133,195,97,134,135,135,209,90,227,52,181,96,97,49,240,240,49,165,82,137,194,234,239,200,200,72,250,14,112,0,160,
+  0,42,213,170,245,115,113,113,95,204,229,114,84,42,149,127,123,62,143,215,26,177,26,244,197,145,18,227,241,56,249,124,
+  158,108,54,75,60,30,231,127,19,170,95,86,86,86,104,107,107,99,112,112,240,128,118,98,192,106,161,192,106,161,128,239,
+  251,116,118,116,48,51,59,123,178,4,27,165,117,206,157,57,187,47,214,130,26,203,191,150,15,104,27,165,245,227,1,158,
+  231,238,68,181,192,194,194,24,65,139,32,70,16,173,209,34,180,159,111,231,184,18,167,196,24,180,209,136,104,180,14,208,
+  162,17,17,68,11,34,6,35,178,231,159,106,4,152,213,90,99,43,155,32,16,180,214,232,96,23,38,130,136,208,116,202,
+  217,243,79,55,2,124,154,159,159,195,243,60,90,90,154,177,148,66,139,160,245,78,124,215,117,137,156,142,48,51,243,13,
+  224,71,163,14,222,126,253,242,185,207,113,156,104,87,215,69,60,207,195,0,24,195,206,97,200,100,22,88,90,202,1,188,
+  110,4,168,85,171,213,187,147,19,227,143,38,39,198,47,3,87,26,116,149,1,174,2,107,135,47,254,2,161,171,0,195,
+  167,31,206,166,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Network = {  //size: 408
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,18,0,
+  0,11,18,1,210,221,126,252,0,0,0,7,116,73,77,69,7,214,2,16,22,3,20,11,54,9,17,0,0,1,37,73,
+  68,65,84,56,203,157,146,61,75,195,80,20,134,159,155,92,242,63,164,46,221,2,226,238,32,226,38,82,255,131,17,10,
+  34,56,74,196,42,232,228,80,63,210,95,32,4,99,5,255,134,131,110,46,130,56,152,22,170,129,214,170,225,82,18,7,
+  13,26,242,97,240,29,207,121,207,203,121,206,189,130,2,181,246,182,61,160,65,185,58,178,164,217,88,152,95,164,86,55,
+  115,155,131,167,7,188,174,107,149,5,80,171,155,44,55,47,48,140,180,77,169,9,87,206,10,0,165,1,66,8,12,67,
+  98,206,78,3,49,32,0,184,189,190,39,138,190,60,178,140,245,180,189,207,220,20,168,151,30,143,98,134,247,15,197,235,
+  56,76,121,100,69,86,122,207,111,223,21,29,128,56,142,127,16,170,176,70,163,97,170,254,123,131,74,172,238,225,82,102,
+  179,204,17,117,93,163,63,24,231,178,58,71,7,121,132,118,42,224,230,174,15,177,200,101,93,91,109,166,38,53,77,227,
+  196,105,183,100,98,82,106,130,49,28,101,110,144,40,8,130,220,35,203,132,231,242,184,81,248,10,0,238,249,89,97,64,
+  199,235,186,214,31,127,222,182,183,118,118,139,2,172,141,245,77,194,48,204,157,76,88,129,194,0,124,223,231,191,146,101,
+  124,85,244,9,241,192,132,130,214,14,135,66,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Optical = {  //size: 720
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,5,17,7,21,115,1,202,165,0,0,0,140,116,
+  69,88,116,67,111,109,109,101,110,116,0,77,101,110,117,45,115,105,122,101,100,32,105,99,111,110,10,61,61,61,61,61,
+  61,61,61,61,61,10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83,
+  116,101,105,110,101,114,44,32,10,104,116,116,112,58,47,47,106,105,109,109,97,99,46,109,117,115,105,99,104,97,108,108,
+  46,99,122,10,10,99,114,101,97,116,101,100,32,119,105,116,104,32,116,104,101,32,71,73,77,80,44,10,104,116,116,112,
+  58,47,47,119,119,119,46,103,105,109,112,46,111,114,103,103,138,199,71,0,0,1,197,73,68,65,84,56,203,157,146,77,
+  107,19,81,20,134,159,51,153,102,22,195,224,100,96,22,83,157,44,252,248,3,69,212,141,40,110,10,34,234,90,93,184,
+  149,72,200,63,208,130,80,68,132,88,104,69,65,17,17,68,255,129,74,214,217,104,73,180,38,168,203,102,25,33,208,132,
+  4,146,185,199,197,164,205,4,233,34,61,171,195,185,231,62,239,123,238,61,82,174,148,30,2,15,56,90,172,81,174,148,
+  244,40,209,239,247,181,92,41,169,13,96,140,161,215,235,45,36,157,119,242,0,216,0,106,20,53,10,178,0,65,153,1,
+  140,26,140,42,232,20,50,61,76,211,255,10,233,125,213,140,3,85,140,73,0,65,208,131,78,157,245,131,128,152,140,129,
+  44,96,223,133,0,70,1,145,153,160,234,1,216,72,150,152,29,193,24,52,209,84,83,4,85,69,84,81,217,87,19,68,
+  20,213,67,70,112,28,135,229,227,203,135,190,215,96,48,0,96,56,28,2,48,26,141,230,29,52,26,13,198,227,241,66,
+  223,232,121,94,10,48,137,33,159,207,243,230,237,171,133,0,151,46,94,73,1,98,9,237,118,11,128,167,79,170,76,38,
+  19,70,163,33,221,191,93,154,205,6,182,189,196,141,235,55,169,213,106,4,65,64,20,69,172,63,126,68,16,4,83,128,
+  8,190,239,19,134,33,219,219,95,89,89,57,139,239,23,240,253,2,167,79,157,1,160,94,175,227,186,46,97,24,242,249,
+  203,39,28,199,193,117,93,0,114,231,47,156,147,223,127,126,93,222,219,235,179,243,243,7,190,239,83,140,139,0,116,58,
+  29,186,221,46,158,231,17,69,17,31,62,190,167,213,222,33,73,18,154,223,27,136,200,218,220,242,150,238,223,187,150,179,
+  173,103,241,137,248,228,234,234,85,138,113,140,101,229,216,237,236,154,23,47,183,44,160,5,172,111,84,55,223,101,246,139,
+  108,110,221,190,115,107,169,16,28,123,110,89,214,221,185,213,87,125,45,34,91,27,213,205,111,217,250,63,189,144,236,196,
+  0,156,143,39,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Printer = {  //size: 481
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,17,0,
+  0,11,17,1,127,100,95,145,0,0,0,7,116,73,77,69,7,213,8,18,15,53,11,122,248,175,234,0,0,1,110,73,
+  68,65,84,56,203,165,147,205,78,34,81,16,133,191,91,176,224,33,122,235,204,138,16,34,179,49,242,24,227,248,48,218,
+  254,60,129,154,209,103,16,120,2,122,199,154,37,59,113,167,38,36,205,95,104,18,187,105,238,45,87,32,61,252,77,244,
+  38,149,155,186,73,157,58,85,231,92,248,230,49,171,73,189,81,187,2,252,255,168,187,62,249,253,231,98,237,181,222,168,
+  105,146,36,59,35,142,99,173,55,106,186,168,201,111,130,111,183,219,91,91,87,42,149,76,190,17,160,88,44,126,206,104,
+  76,230,86,213,221,0,206,57,58,157,14,0,34,178,6,84,46,151,247,51,240,60,15,17,193,24,179,22,123,25,168,42,
+  173,86,107,217,81,68,168,86,171,187,101,60,247,207,174,0,191,80,40,240,227,224,39,206,185,12,253,213,241,140,49,116,
+  159,159,136,227,24,224,122,193,192,191,240,47,177,214,146,166,233,94,19,76,38,199,136,8,247,15,127,125,89,93,82,183,
+  219,37,73,18,130,32,64,85,9,195,144,209,104,196,96,48,96,56,28,210,239,247,233,245,122,52,155,77,222,94,95,178,
+  59,136,162,136,248,253,157,241,120,76,169,84,34,12,67,0,210,52,93,82,7,176,214,50,232,135,196,201,44,11,32,34,
+  76,162,136,32,104,50,157,78,1,152,207,231,136,49,56,103,153,91,139,72,142,124,46,199,209,113,117,179,10,214,90,14,
+  43,191,254,49,140,226,156,98,173,69,68,150,242,46,150,188,4,152,205,102,120,158,183,213,113,11,21,68,4,85,197,57,
+  151,1,120,188,189,187,57,253,194,111,126,252,0,186,104,203,229,25,65,188,29,0,0,0,0,73,69,78,68,174,66,96,
+  130,
+};
+const nall::vector<uint8_t> Speaker = {  //size: 592
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,1,226,73,68,65,84,56,141,
+  165,147,191,79,83,81,20,199,63,231,246,245,165,68,168,3,193,201,63,64,99,66,4,139,128,113,100,241,71,88,40,88,
+  162,46,36,242,106,58,233,0,127,132,49,93,173,12,13,137,160,169,105,2,196,133,196,168,131,131,137,16,23,69,172,147,
+  58,176,184,152,247,76,244,93,222,61,14,198,218,22,140,65,191,227,57,159,156,123,190,223,123,175,168,42,255,35,243,167,
+  70,126,58,159,11,130,32,253,79,3,10,133,137,193,20,242,56,142,227,212,129,7,20,10,19,131,105,63,243,84,85,179,
+  173,245,161,224,94,112,122,118,121,172,147,247,46,77,79,134,64,198,243,188,175,168,222,73,251,153,235,51,51,51,217,202,
+  221,74,210,10,58,167,239,141,104,53,23,60,236,223,168,76,126,105,110,32,72,166,124,187,236,205,207,205,31,86,152,43,
+  149,74,217,209,145,51,120,158,7,64,110,118,169,56,114,109,233,232,230,194,213,39,10,235,104,124,179,205,130,162,244,245,
+  29,225,248,177,19,168,170,12,156,60,69,38,211,133,136,0,32,66,111,34,148,1,68,101,69,144,209,61,25,188,217,122,
+  205,187,198,91,0,26,141,109,182,183,183,176,214,2,144,114,44,2,99,34,136,26,247,10,116,72,4,105,102,0,16,134,
+  97,115,101,187,107,81,126,191,141,93,163,137,168,164,0,172,209,196,79,164,45,120,3,16,70,33,97,20,162,170,216,216,
+  98,99,219,4,4,83,64,244,153,42,234,37,169,1,96,83,91,78,48,0,81,20,177,179,179,3,192,135,79,31,137,109,
+  220,26,191,167,164,111,252,132,245,130,194,139,182,107,84,85,173,215,235,137,136,88,35,102,113,117,117,229,242,248,197,241,
+  238,95,192,203,133,43,183,0,134,139,247,71,129,60,98,250,59,45,244,168,106,183,115,174,123,121,233,65,209,126,79,206,
+  173,61,90,139,156,115,109,94,93,226,134,65,139,27,149,233,207,173,117,217,239,51,77,77,77,157,197,184,245,67,93,61,
+  189,213,106,245,219,30,160,51,196,78,213,106,181,231,56,115,222,247,253,100,191,254,95,55,56,136,126,0,228,148,200,42,
+  201,231,90,24,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Storage = {  //size: 603
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,5,15,53,8,235,207,124,119,0,0,0,140,116,
+  69,88,116,67,111,109,109,101,110,116,0,77,101,110,117,45,115,105,122,101,100,32,105,99,111,110,10,61,61,61,61,61,
+  61,61,61,61,61,10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83,
+  116,101,105,110,101,114,44,32,10,104,116,116,112,58,47,47,106,105,109,109,97,99,46,109,117,115,105,99,104,97,108,108,
+  46,99,122,10,10,99,114,101,97,116,101,100,32,119,105,116,104,32,116,104,101,32,71,73,77,80,44,10,104,116,116,112,
+  58,47,47,119,119,119,46,103,105,109,112,46,111,114,103,103,138,199,71,0,0,1,80,73,68,65,84,56,203,165,147,77,
+  110,194,48,16,133,191,160,64,164,32,203,201,38,106,36,246,112,55,36,196,13,218,222,144,21,39,64,44,216,65,108,16,
+  249,153,233,34,224,40,237,162,85,153,133,237,25,207,123,126,51,182,225,69,139,54,219,245,7,240,254,79,252,39,155,237,
+  90,255,99,206,57,221,108,215,26,3,136,8,231,243,25,85,133,40,2,213,177,76,64,159,67,4,160,204,102,51,0,98,
+  0,21,165,19,233,179,69,6,0,16,17,33,170,125,140,168,199,247,206,64,32,42,72,39,68,15,144,2,168,62,0,202,
+  83,24,170,61,177,208,171,13,10,84,201,243,140,211,233,244,167,206,101,89,70,215,117,0,76,158,65,231,28,69,81,252,
+  10,46,138,130,186,174,131,31,154,232,189,15,9,0,215,235,149,170,170,0,48,198,144,166,41,147,201,132,203,229,130,247,
+  30,99,204,64,144,36,9,139,197,98,116,146,181,22,107,45,0,222,123,234,186,230,118,187,253,80,20,3,236,118,59,154,
+  166,25,109,180,109,75,28,199,97,253,221,242,60,31,247,160,109,91,246,251,61,199,227,145,170,170,80,85,84,149,36,73,
+  56,28,14,24,99,194,236,156,195,57,55,16,168,42,34,194,124,158,50,157,78,195,35,1,104,154,6,149,158,76,69,250,
+  220,199,13,132,18,178,44,195,90,75,89,150,0,220,239,119,128,208,237,229,106,137,136,176,92,173,16,17,222,202,50,148,
+  252,250,103,122,213,190,0,151,85,237,38,182,108,181,11,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Edit {
+const nall::vector<uint8_t> Clear = {  //size: 773
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,151,73,68,65,84,56,141,
+  149,146,75,104,148,103,20,134,159,239,155,153,232,76,18,155,219,72,66,147,72,188,213,168,20,43,38,139,10,210,130,139,
+  74,75,118,130,151,133,139,209,141,130,34,252,11,5,93,24,23,6,28,144,82,16,132,142,198,133,96,75,117,161,11,233,
+  141,22,49,45,180,105,52,49,138,49,141,154,113,70,73,147,201,101,204,63,243,79,254,255,251,142,43,69,99,33,248,172,
+  207,251,112,206,203,81,71,190,96,115,56,196,5,17,162,198,178,247,212,13,249,145,247,64,135,52,223,175,109,142,173,218,
+  176,162,188,49,164,185,122,116,155,218,250,94,2,96,113,69,84,179,180,42,194,199,45,177,88,72,115,246,244,126,165,83,
+  73,213,153,74,170,218,5,5,86,232,28,124,82,116,125,35,212,215,148,17,93,172,234,171,26,233,5,142,3,75,23,20,
+  136,240,141,235,153,139,183,6,243,238,76,201,167,169,157,202,149,173,27,55,196,227,77,47,128,186,133,4,74,68,0,232,
+  218,167,78,196,87,114,252,147,182,207,85,115,75,59,61,167,187,189,233,127,198,114,236,84,147,198,200,37,17,206,39,28,
+  25,255,95,65,42,169,118,105,173,190,253,116,115,71,180,174,174,129,127,191,254,155,167,87,31,178,254,236,118,106,215,85,
+  145,30,237,247,238,223,253,67,140,245,190,51,70,142,37,28,201,206,23,236,209,154,100,83,125,107,57,151,36,106,38,93,
+  182,116,111,101,81,141,6,21,133,80,53,126,80,206,189,193,95,253,129,254,223,13,226,255,96,12,41,224,230,235,19,82,
+  73,181,40,114,142,59,21,205,181,171,58,46,31,14,105,253,28,66,13,96,198,192,100,177,65,30,207,182,224,205,213,144,
+  201,12,200,227,145,1,119,106,50,91,212,175,86,73,56,82,242,119,211,85,123,176,177,164,35,101,96,103,33,24,1,147,
+  5,243,31,94,225,25,179,185,235,204,140,157,35,94,141,218,212,254,85,133,86,74,194,111,53,82,193,207,217,76,90,91,
+  171,80,254,8,34,62,254,156,135,87,154,165,88,152,161,80,152,70,68,208,225,74,210,79,122,3,148,252,166,223,204,39,
+  28,121,230,207,77,143,164,211,119,153,113,99,76,76,140,146,203,165,153,204,101,112,221,41,68,132,88,245,54,10,69,205,
+  208,131,254,146,49,56,111,9,0,2,35,135,250,122,127,41,250,172,198,117,61,138,197,60,214,6,232,112,13,149,241,29,
+  228,221,152,220,186,121,37,111,76,240,101,194,145,204,235,18,223,164,251,140,234,89,253,81,123,219,242,21,173,17,47,255,
+  39,101,177,53,232,200,50,30,13,247,4,67,67,125,227,214,242,89,194,145,135,0,225,119,210,128,49,116,12,61,248,235,
+  118,229,146,134,15,63,168,106,211,163,163,247,252,225,225,159,2,107,188,235,214,114,32,225,200,196,59,159,56,159,84,82,
+  45,83,138,62,165,16,224,154,181,156,76,56,242,120,254,220,75,126,7,67,8,40,132,18,218,0,0,0,0,73,69,78,
+  68,174,66,96,130,
+};
+const nall::vector<uint8_t> Copy = {  //size: 498
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,10,26,20,8,47,131,20,52,155,0,0,1,127,73,
+  68,65,84,56,203,149,147,75,75,2,81,24,134,223,35,51,99,218,128,253,10,207,8,221,22,253,143,8,90,182,136,160,
+  117,23,240,66,182,176,69,145,118,219,68,68,68,27,177,54,5,254,4,49,179,54,129,101,48,163,191,160,154,48,5,55,
+  205,237,180,105,116,70,71,177,111,245,157,195,121,31,222,247,227,59,228,232,36,51,175,235,250,44,70,40,193,207,189,109,
+  174,197,239,156,119,156,97,24,211,241,232,214,40,122,164,15,246,38,1,184,1,118,211,106,53,193,24,235,19,17,66,0,
+  0,161,208,132,39,148,115,30,106,181,26,94,171,149,133,198,119,99,102,144,139,253,204,110,202,238,121,158,191,117,1,36,
+  73,66,177,84,152,250,71,164,69,23,64,81,148,78,140,86,171,57,84,108,71,114,1,40,165,40,150,10,157,179,44,203,
+  158,226,72,36,210,63,3,198,24,100,89,118,13,210,249,112,80,185,0,148,82,148,202,197,161,14,122,161,156,45,182,44,
+  6,69,25,205,65,48,56,142,118,187,221,5,232,186,14,198,24,194,97,138,242,83,105,168,3,74,37,152,166,129,202,75,
+  165,11,208,180,31,4,2,99,176,44,54,212,238,229,213,5,30,30,239,221,17,4,158,175,158,157,159,186,22,135,49,203,
+  211,182,40,138,88,89,94,133,101,153,80,191,84,228,174,179,224,54,214,163,121,0,121,199,114,108,27,134,225,235,21,59,
+  75,85,85,228,110,178,0,240,236,243,26,146,166,233,240,251,253,16,4,1,162,40,130,16,130,122,189,14,128,224,227,243,
+  221,22,167,18,177,228,28,233,21,31,30,167,179,166,105,46,121,125,172,191,42,0,40,36,98,201,29,0,248,5,66,89,
+  166,3,21,136,247,216,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Cut = {  //size: 807
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,10,28,22,29,49,104,65,100,94,0,0,2,180,73,
+  68,65,84,56,203,133,147,93,72,147,81,24,199,159,115,222,119,175,175,219,212,125,165,219,108,248,21,104,120,209,85,16,
+  116,97,148,73,87,69,222,100,34,17,104,105,150,88,168,105,129,144,4,149,165,125,129,136,16,148,24,232,16,244,74,144,
+  36,186,12,2,181,48,109,51,54,242,99,239,54,117,155,238,117,95,239,118,206,219,77,206,97,90,231,238,252,255,15,63,
+  254,207,243,156,131,32,229,60,235,125,114,14,0,222,243,60,239,109,186,121,187,116,143,215,207,178,236,165,59,205,173,186,
+  84,29,167,94,24,134,25,187,213,216,172,141,70,163,203,125,253,175,59,83,61,5,171,184,112,230,116,185,230,197,171,158,
+  79,7,2,8,33,252,79,135,29,215,213,214,87,16,66,142,237,232,79,123,30,151,153,115,205,166,237,144,136,40,165,39,
+  14,4,96,140,67,75,75,191,128,79,75,67,177,88,172,116,39,5,203,178,83,133,133,133,224,113,123,40,165,212,241,175,
+  4,21,54,155,13,86,93,43,168,174,182,190,56,145,72,156,234,121,222,61,80,82,82,162,64,128,193,225,116,0,0,88,
+  15,4,220,109,189,247,25,99,188,188,184,104,7,62,141,71,146,36,29,215,235,244,215,100,10,176,190,177,70,1,96,162,
+  173,165,227,225,129,0,0,0,74,105,229,194,143,5,121,109,205,3,141,55,154,84,241,68,28,137,98,16,230,230,230,36,
+  0,232,218,91,255,23,160,173,165,99,26,99,60,100,95,180,209,96,112,11,107,52,26,16,220,130,44,203,242,187,182,150,
+  142,233,255,2,254,164,152,154,157,153,198,78,167,3,210,57,30,50,223,14,70,16,66,111,246,171,69,123,133,49,163,177,
+  151,80,122,61,190,185,169,38,229,101,50,51,249,17,21,24,115,64,32,116,11,33,52,80,233,241,180,239,11,24,51,153,
+  58,227,126,255,131,163,38,19,36,40,69,222,173,77,217,23,222,30,102,101,84,85,144,157,195,240,28,71,37,89,6,231,
+  250,58,40,178,178,174,86,186,221,67,201,22,198,205,230,26,201,231,235,42,54,26,233,194,202,202,84,48,18,150,2,65,
+  17,215,196,105,77,148,35,58,150,193,52,16,18,19,142,213,213,15,71,244,122,18,243,249,6,199,205,230,221,151,58,106,
+  48,8,243,69,69,100,132,97,238,3,0,204,88,44,33,171,82,153,216,241,71,24,134,124,201,53,7,1,0,134,49,110,
+  255,150,159,79,70,13,6,97,119,136,177,152,94,146,36,82,69,200,163,9,149,234,165,130,101,121,132,177,61,57,105,142,
+  155,81,243,233,170,201,140,140,190,203,148,118,131,44,83,32,196,176,11,224,56,65,173,84,50,95,243,242,98,38,157,174,
+  105,94,16,188,138,204,204,43,201,143,164,213,54,124,119,185,54,14,31,58,212,48,107,177,196,8,165,24,48,14,0,0,
+  48,0,0,213,90,173,207,229,247,159,244,75,18,27,0,144,57,181,250,236,69,65,72,238,220,42,138,238,234,236,108,143,
+  55,16,200,241,135,195,26,95,36,226,83,168,84,231,173,162,232,254,13,160,243,58,7,220,52,193,60,0,0,0,0,73,
+  69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Delete = {  //size: 680
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,141,0,142,0,139,33,244,163,126,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,214,6,19,15,29,9,72,179,203,198,0,0,0,15,116,
+  69,88,116,67,111,109,109,101,110,116,0,105,100,32,108,111,103,111,134,198,119,185,0,0,2,26,73,68,65,84,56,203,
+  165,144,191,79,19,113,24,198,159,251,165,119,45,180,165,215,214,90,42,20,162,12,180,14,110,110,80,40,131,236,154,56,
+  105,106,127,128,17,77,77,220,26,233,96,82,7,147,66,212,52,197,70,5,55,39,117,49,49,182,22,249,3,100,48,186,
+  181,139,169,24,19,56,174,114,87,238,122,119,14,114,23,67,137,81,124,167,247,251,230,121,62,239,251,124,9,236,213,226,
+  253,194,59,89,150,198,241,23,197,113,182,218,141,185,76,20,0,104,115,40,203,210,248,108,250,26,20,69,177,132,134,97,
+  116,153,13,195,192,147,229,178,181,136,14,77,231,231,1,228,0,29,146,36,65,16,132,46,163,249,102,24,6,36,73,2,
+  0,66,211,121,3,64,142,6,144,203,102,46,160,190,186,4,81,20,209,108,54,15,52,3,0,69,81,32,8,2,162,40,
+  34,155,73,225,78,225,121,206,138,240,177,209,130,32,8,104,52,26,127,140,96,106,135,199,246,34,152,195,186,28,68,252,
+  110,165,75,76,16,7,33,130,86,103,1,34,103,99,56,76,89,128,216,25,254,255,0,159,190,136,255,100,28,13,58,126,
+  69,12,157,203,215,64,96,236,80,235,13,172,90,95,52,119,253,170,113,249,82,92,125,84,46,41,147,147,83,204,218,218,
+  123,38,26,157,208,170,213,10,25,139,77,105,181,90,149,154,136,198,244,183,149,55,228,226,194,3,170,43,2,203,178,170,
+  218,81,183,121,175,71,107,181,126,16,67,161,225,94,93,55,228,193,193,33,90,211,116,201,225,112,248,105,134,217,228,56,
+  155,237,247,35,72,179,225,121,126,135,36,201,215,54,155,13,35,167,70,32,237,238,40,3,3,39,116,151,203,41,187,251,
+  220,109,0,136,132,195,71,189,94,175,218,5,40,44,220,43,1,208,34,225,211,23,125,30,223,177,141,111,95,125,0,100,
+  158,247,144,36,73,212,123,29,61,157,254,254,32,88,150,211,237,118,187,84,44,62,76,239,191,32,113,51,115,139,95,94,
+  121,172,3,248,188,181,181,41,240,110,222,239,114,58,233,93,165,205,172,175,127,56,9,0,43,207,158,58,211,169,217,182,
+  218,81,206,239,7,148,75,75,69,248,253,199,143,36,19,233,209,100,34,221,167,40,10,94,190,122,209,67,81,116,224,74,
+  60,73,164,146,51,68,32,16,152,185,61,159,253,206,178,220,134,9,248,9,228,204,195,8,165,247,44,101,0,0,0,0,
+  73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Find = {  //size: 617
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,240,0,240,0,239,52,6,103,27,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,11,5,16,15,5,37,253,173,47,0,0,1,246,73,
+  68,65,84,56,203,157,146,77,107,19,81,24,133,159,59,201,36,77,154,102,134,74,82,10,10,181,93,104,26,69,20,193,
+  133,184,20,10,85,119,226,38,191,192,111,169,244,31,136,182,36,65,68,20,116,89,119,85,16,197,77,168,59,65,236,162,
+  84,40,164,137,86,171,150,80,53,141,205,71,39,77,50,51,215,69,72,73,66,82,170,103,119,15,239,61,239,225,188,71,
+  60,121,250,120,49,247,39,119,140,61,64,85,213,231,19,183,38,47,182,144,211,209,187,150,220,35,238,77,223,145,237,162,
+  78,41,235,92,62,191,185,235,118,77,211,59,242,78,33,196,206,35,153,76,118,28,10,133,66,93,133,119,28,180,15,102,
+  50,25,102,95,204,2,144,152,75,0,208,180,107,119,7,223,190,175,146,74,167,57,122,106,140,109,197,139,105,43,184,100,
+  133,212,252,107,38,110,223,140,196,162,247,159,53,254,40,237,14,52,93,35,149,78,115,232,228,24,159,11,94,6,7,2,
+  140,28,8,82,18,62,180,209,113,252,154,62,115,237,198,149,227,93,29,36,230,18,12,135,79,179,82,112,115,233,204,16,
+  190,30,21,211,182,81,29,10,239,150,37,193,35,227,54,75,111,22,0,1,160,116,10,75,122,3,12,13,232,88,182,100,
+  219,52,169,212,44,202,85,139,126,159,155,95,101,181,37,9,69,74,137,148,118,75,48,110,183,11,132,160,92,181,168,214,
+  36,155,91,53,220,170,131,253,253,30,164,16,45,93,112,2,182,105,154,45,78,108,99,131,53,163,138,223,163,162,247,185,
+  176,45,137,223,227,228,211,70,129,125,61,150,157,107,114,174,212,43,234,66,211,116,52,77,231,194,185,243,164,23,222,18,
+  236,19,124,249,153,39,155,43,145,55,42,124,92,89,167,84,174,144,91,122,229,44,21,139,63,26,2,34,26,159,154,177,
+  44,43,210,124,13,195,48,112,56,84,78,156,141,176,188,86,100,203,84,24,236,149,172,47,190,100,248,224,8,0,31,230,
+  223,167,30,62,120,116,88,116,107,216,213,235,151,167,2,129,224,100,51,231,235,245,17,30,13,243,59,155,173,247,101,245,
+  43,130,127,68,44,30,149,13,145,255,18,0,148,88,60,106,1,148,13,131,191,6,140,246,211,127,51,231,46,0,0,0,
+  0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Paste = {  //size: 561
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,9,112,72,89,115,0,0,11,19,0,0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,
+  10,14,0,28,21,175,226,241,24,0,0,1,208,73,68,65,84,56,203,149,147,177,107,83,81,20,198,127,247,189,52,130,
+  36,49,141,212,148,12,133,34,6,92,92,180,106,98,4,209,18,112,118,208,33,56,20,69,109,29,196,197,65,151,130,56,
+  8,142,250,15,52,131,187,155,168,75,213,46,186,56,41,145,82,168,209,72,66,94,111,108,208,70,239,57,14,161,49,47,
+  109,32,126,219,253,206,61,63,206,249,224,24,6,84,42,149,140,239,123,207,157,147,217,126,223,247,189,23,206,73,177,92,
+  46,107,191,111,0,110,206,154,43,6,30,42,140,43,30,118,242,18,185,220,17,60,207,0,32,162,172,172,124,96,95,237,
+  41,6,1,104,170,234,173,199,47,89,50,243,103,137,248,158,9,46,156,57,24,251,210,201,240,46,200,210,180,29,166,167,
+  167,48,102,27,32,172,173,173,147,76,68,57,154,252,204,164,191,206,179,215,171,86,84,83,17,32,230,121,102,44,26,241,
+  121,95,207,178,120,255,17,65,16,80,169,84,66,171,205,205,29,34,149,74,113,239,238,109,46,102,190,33,170,49,32,26,
+  233,255,212,222,130,68,34,129,170,48,51,115,108,48,30,226,241,56,91,127,192,169,233,121,61,128,106,40,27,150,223,188,
+  13,189,11,249,28,187,41,194,16,157,62,149,231,31,92,16,113,195,1,186,75,97,123,2,85,69,69,200,157,60,254,127,
+  19,20,242,57,68,4,17,65,85,176,214,50,49,145,30,13,208,223,40,210,109,110,253,104,141,62,129,136,235,65,170,95,
+  171,52,26,245,29,33,135,1,3,53,231,186,128,86,203,210,168,215,41,22,207,15,219,20,111,216,4,214,110,80,251,94,
+  67,68,122,254,245,249,171,92,158,90,30,190,194,222,49,165,25,52,73,167,51,164,211,25,178,217,195,88,187,129,170,112,
+  99,225,90,175,217,55,26,2,108,138,232,239,246,47,183,231,68,242,35,15,22,239,240,211,237,140,166,176,255,19,171,237,
+  3,0,36,181,138,49,102,83,85,59,6,96,225,92,247,26,49,140,51,130,84,187,215,248,228,21,75,127,1,79,230,225,
+  152,120,105,196,218,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Redo = {  //size: 591
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,214,7,26,10,39,21,63,184,237,33,0,0,1,220,73,
+  68,65,84,56,203,157,147,77,107,19,81,24,133,159,59,95,77,140,164,66,76,99,131,31,109,54,213,118,211,210,42,126,
+  84,99,13,66,169,56,110,68,43,130,184,26,112,173,59,193,191,224,126,86,221,117,41,68,10,133,138,197,82,186,106,164,
+  168,27,193,77,117,33,41,98,76,38,77,154,201,100,198,205,24,39,157,65,130,103,249,94,158,195,185,231,190,23,250,144,
+  110,106,158,110,106,211,81,103,18,253,107,91,55,181,194,127,27,60,126,96,0,188,209,77,109,46,56,23,17,113,7,128,
+  60,176,8,76,1,147,1,3,150,150,77,128,27,69,195,94,15,25,232,166,118,1,120,62,39,180,84,182,112,117,88,77,
+  141,230,16,2,85,142,97,217,63,41,125,91,225,202,232,189,30,19,17,128,23,128,23,143,242,249,243,242,80,78,170,217,
+  101,188,218,167,158,116,31,155,53,128,174,201,185,61,123,86,248,240,4,240,242,225,181,235,55,99,153,51,88,149,117,44,
+  79,35,238,218,0,12,58,13,54,219,206,223,226,132,196,165,145,187,44,45,155,40,254,236,233,157,179,227,147,177,116,150,
+  250,175,119,36,90,13,18,52,24,110,85,120,69,2,71,234,237,58,61,120,178,123,13,5,96,64,213,102,143,143,77,167,
+  227,214,123,178,251,85,118,164,4,154,34,168,29,137,51,238,67,23,21,48,235,22,153,99,167,217,92,253,208,237,64,1,
+  232,184,110,70,200,130,84,195,98,171,84,247,214,90,149,208,235,76,92,62,17,130,187,123,224,116,156,228,209,198,87,182,
+  119,118,89,107,181,45,224,25,48,85,52,108,81,52,108,1,240,54,57,20,130,129,110,7,236,55,171,238,74,93,72,192,
+  253,162,97,175,30,78,224,195,51,69,195,46,69,110,162,170,200,7,128,23,5,251,10,193,193,4,27,205,189,178,90,144,
+  189,24,166,54,127,216,228,207,53,162,36,3,140,221,150,221,47,7,237,250,173,153,220,143,212,110,249,148,208,213,173,207,
+  175,59,118,63,127,36,184,137,35,192,2,80,5,54,158,36,221,239,2,188,249,69,167,243,47,131,223,215,75,186,5,94,
+  207,53,222,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Replace = {  //size: 776
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,214,1,6,22,4,3,245,118,104,130,0,0,2,149,73,
+  68,65,84,56,203,173,147,77,108,12,97,24,199,127,51,179,179,181,72,183,204,44,193,129,86,208,137,224,160,137,131,160,
+  184,52,113,32,82,73,137,175,99,75,138,90,65,164,68,16,130,138,34,113,173,68,19,196,54,18,209,131,75,125,180,68,
+  72,137,143,157,165,69,98,91,149,212,110,119,103,63,186,93,187,59,175,3,59,105,47,78,254,201,147,60,239,155,252,127,
+  121,158,55,255,87,98,156,246,29,104,212,52,77,139,20,207,209,104,84,191,218,122,45,202,63,36,21,27,255,161,3,107,
+  75,189,101,93,75,86,212,136,20,83,37,85,140,137,190,151,15,164,132,21,95,119,169,165,245,209,63,1,141,251,247,106,
+  186,238,139,44,172,170,97,32,237,97,229,226,89,152,225,24,233,100,2,43,212,137,203,165,160,40,178,99,82,85,53,224,
+  111,58,188,5,192,5,160,235,190,72,249,226,149,124,177,74,168,91,93,206,36,183,130,170,200,116,135,4,51,150,108,96,
+  248,125,39,199,142,54,59,128,243,23,207,214,22,123,151,115,59,217,199,188,233,165,20,108,65,54,87,32,155,179,241,149,
+  150,240,117,104,12,9,176,172,56,0,94,111,25,37,111,142,115,121,171,44,128,29,206,92,106,33,193,224,112,130,112,36,
+  67,60,157,103,142,230,193,45,23,208,167,216,0,132,66,33,0,90,183,41,236,186,210,67,117,237,126,128,155,50,64,194,
+  138,175,235,123,221,197,204,82,9,115,96,132,145,212,24,47,204,239,196,82,25,162,239,239,179,105,227,38,12,195,160,173,
+  65,99,247,213,103,12,62,109,231,157,57,88,0,144,1,166,149,77,239,170,40,159,79,248,213,61,42,124,37,244,132,126,
+  146,28,205,50,252,186,3,93,211,24,77,143,58,230,129,39,55,121,215,31,101,196,236,80,128,42,233,204,153,83,98,238,
+  188,114,124,186,78,208,12,146,74,167,156,103,89,182,116,25,213,107,170,105,107,208,216,121,169,155,112,79,59,111,63,69,
+  137,153,1,128,170,166,91,118,175,11,112,204,7,155,252,88,86,220,217,23,248,99,110,121,74,255,195,54,62,126,79,18,
+  51,3,252,90,84,207,145,147,215,123,1,73,110,110,62,33,5,205,32,249,194,47,187,104,50,12,3,195,48,168,172,172,
+  228,155,188,130,224,243,231,244,15,89,196,130,119,169,59,253,2,219,51,219,201,144,12,224,63,120,72,2,16,194,97,32,
+  132,224,198,30,157,207,63,162,220,9,4,136,124,232,96,85,125,7,147,180,5,228,114,185,98,4,132,60,46,149,118,62,
+  159,159,0,104,186,101,179,126,230,103,106,54,239,102,121,195,99,196,228,57,36,147,73,114,185,60,128,50,49,72,128,170,
+  186,241,122,221,14,32,147,201,144,72,36,200,102,179,40,138,130,170,170,127,203,5,96,79,0,40,138,114,251,66,203,185,
+  237,66,136,9,83,20,75,146,156,127,135,199,227,105,7,114,252,15,253,6,121,205,27,12,206,189,173,89,0,0,0,0,
+  73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Undo = {  //size: 650
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,214,7,26,10,14,0,22,35,150,33,0,0,2,23,73,
+  68,65,84,56,203,149,147,79,104,146,97,28,199,191,207,243,190,207,212,229,254,232,198,26,210,136,214,193,242,16,20,132,
+  65,131,21,4,66,176,67,120,8,130,98,68,80,93,59,140,60,120,40,199,140,29,34,186,116,169,88,65,16,52,233,80,
+  44,22,193,82,140,90,118,138,148,180,104,13,114,19,92,190,58,231,134,239,235,243,60,29,194,165,155,150,125,111,207,23,
+  190,159,231,121,126,207,247,1,154,40,58,5,137,22,164,110,53,230,238,161,151,41,200,162,69,209,218,133,223,199,108,76,
+  65,214,237,13,181,154,255,115,2,191,143,217,60,78,35,231,246,134,128,194,213,205,107,232,164,39,195,5,205,128,182,197,
+  195,201,236,197,241,9,189,84,11,32,91,195,70,110,28,42,213,0,139,23,144,21,112,46,1,136,242,155,217,199,95,25,
+  5,121,149,40,120,2,65,227,71,21,160,248,125,172,221,227,52,86,221,222,16,140,159,215,65,40,69,185,12,128,47,66,
+  95,79,65,91,137,65,202,146,58,232,58,209,103,182,40,189,54,228,79,15,236,171,188,142,68,197,50,0,40,19,23,132,
+  94,13,83,170,252,166,170,12,82,74,8,193,33,132,128,224,107,40,175,167,96,178,152,200,142,174,93,157,157,98,237,208,
+  238,253,250,163,72,84,24,244,101,146,217,231,67,94,176,142,147,0,0,206,117,44,124,251,136,197,239,113,164,211,95,234,
+  6,86,212,226,176,247,187,96,110,103,7,134,157,221,225,237,51,24,25,195,66,226,38,246,184,174,64,240,98,93,88,20,
+  239,34,147,101,40,235,28,61,142,75,248,244,118,186,116,252,108,218,170,2,64,32,104,104,240,49,59,48,153,115,143,140,
+  1,0,98,51,119,234,223,203,66,224,58,114,25,133,204,3,152,204,58,8,145,106,93,15,2,65,67,155,77,50,251,252,
+  179,201,205,204,208,40,200,208,40,8,128,131,165,188,60,149,152,187,253,190,111,224,28,184,232,0,145,210,132,191,213,184,
+  81,149,195,247,225,120,55,77,229,234,242,13,17,121,104,147,219,154,88,187,115,35,127,248,60,150,20,97,149,27,197,56,
+  132,106,207,55,5,52,131,68,167,48,216,237,56,44,180,236,18,0,220,106,248,153,254,161,163,93,59,247,206,124,142,61,
+  169,28,59,163,93,251,111,128,108,235,183,166,62,60,127,65,117,237,105,213,251,5,5,22,226,37,43,190,36,20,0,0,
+  0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Emblem {
+const nall::vector<uint8_t> Archive = {  //size: 540
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,6,16,18,24,9,181,27,210,224,0,0,1,169,73,
+  68,65,84,56,203,173,146,49,104,84,65,16,134,191,217,219,221,119,119,201,61,98,12,65,174,16,17,4,145,32,73,35,
+  138,77,196,198,82,9,40,135,141,88,40,150,17,11,59,175,72,41,30,164,17,148,20,177,9,104,21,44,4,69,15,181,
+  20,244,154,136,98,108,140,152,34,42,81,162,225,93,222,219,177,120,57,21,35,120,81,167,153,217,129,255,155,217,153,129,
+  127,52,1,104,212,204,48,240,108,147,218,145,241,153,208,146,70,205,52,129,209,179,87,23,244,253,252,99,21,91,54,160,
+  0,24,35,185,23,72,62,47,98,74,3,25,105,34,47,158,222,51,115,15,167,1,234,22,224,244,229,86,150,44,191,49,
+  179,147,167,196,59,7,192,218,90,27,31,69,8,130,115,14,83,112,248,184,90,56,118,241,14,75,111,159,179,251,192,24,
+  175,91,247,71,45,176,67,140,152,144,165,98,10,158,52,228,253,69,189,3,0,84,250,171,32,6,128,234,174,125,180,87,
+  222,17,40,80,217,178,13,13,217,176,5,250,146,149,15,188,124,114,155,147,23,166,64,179,13,159,85,5,80,8,109,210,
+  143,175,72,86,63,225,138,49,0,22,40,62,186,57,33,135,143,158,131,244,11,100,171,160,1,213,12,66,138,106,200,125,
+  88,7,107,96,207,222,253,60,152,189,14,104,209,2,126,112,251,144,98,140,224,43,64,229,199,122,80,36,175,189,254,166,
+  51,95,134,70,14,178,48,215,140,164,81,51,151,128,250,95,158,65,189,115,7,122,228,196,121,150,231,155,93,169,226,157,
+  135,184,123,235,10,227,51,65,108,39,233,163,50,61,241,214,174,0,206,71,223,227,159,0,37,136,251,187,2,152,168,184,
+  17,224,74,61,216,46,1,169,251,93,7,113,149,224,123,255,32,205,247,17,130,252,2,176,229,107,211,19,199,207,108,102,
+  252,95,19,189,193,255,176,111,224,210,135,204,19,41,165,180,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Audio = {  //size: 688
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,2,45,73,68,65,84,56,141,165,146,63,104,83,113,16,199,63,247,123,
+  239,165,125,201,139,173,182,52,181,162,130,72,139,184,57,84,193,81,232,32,69,28,44,22,43,213,65,176,168,139,131,139,
+  130,116,18,156,156,85,4,7,55,209,81,23,5,65,28,28,212,69,7,161,96,165,54,109,109,211,166,77,94,146,247,239,
+  119,14,117,146,196,165,55,222,113,31,190,119,124,68,85,217,73,153,29,109,3,110,187,230,165,27,123,159,138,232,49,85,
+  12,136,3,8,162,70,21,71,20,163,130,128,56,40,171,109,1,89,166,231,199,199,79,117,15,244,13,179,43,216,71,193,
+  239,195,243,2,140,113,200,178,136,86,84,165,222,92,230,209,147,7,253,109,1,0,131,3,251,201,251,62,65,16,80,44,
+  148,8,252,33,114,94,145,86,84,161,22,46,144,247,3,16,180,35,160,90,155,39,177,91,196,73,141,122,163,76,87,174,
+  7,193,33,73,67,162,100,147,40,222,0,171,210,17,144,164,13,94,188,124,95,111,54,50,81,139,108,255,3,81,85,227,
+  229,76,58,49,49,230,91,181,157,1,160,108,85,211,32,197,61,136,155,196,110,230,165,41,205,164,133,159,20,163,116,83,
+  85,81,254,147,64,213,34,34,60,127,88,94,208,127,100,153,154,41,97,53,67,173,74,71,15,172,166,128,232,236,44,210,
+  118,110,83,64,59,139,100,109,138,8,188,235,32,155,181,9,86,255,158,48,53,51,112,218,245,204,109,207,20,142,231,186,
+  242,226,122,110,166,196,136,160,195,101,100,242,106,105,172,203,53,247,242,249,222,67,34,226,56,174,177,86,99,80,196,189,
+  120,109,240,130,231,57,143,207,158,57,151,31,57,124,146,176,185,196,74,229,147,179,81,155,67,21,66,51,120,179,191,183,
+  231,238,244,228,173,194,158,221,67,132,205,21,150,42,31,217,10,231,0,196,117,140,92,25,57,90,200,71,250,149,239,63,
+  23,105,69,235,52,163,53,204,118,56,5,123,103,244,196,129,194,239,173,183,44,87,83,154,113,133,36,9,49,198,67,21,
+  227,42,250,225,199,92,99,180,216,83,46,248,254,10,105,170,132,245,140,122,173,129,181,214,184,142,172,46,150,127,21,115,
+  221,137,136,24,162,86,68,101,189,65,101,173,145,0,70,84,149,233,235,165,251,32,151,179,76,251,68,36,113,92,153,71,
+  245,115,26,235,43,208,111,142,103,158,101,153,30,49,70,98,133,204,49,242,37,77,236,27,140,121,253,7,117,49,14,53,
+  175,233,38,194,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Binary = {  //size: 560
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,4,22,20,7,56,114,185,172,74,0,0,1,189,73,
+  68,65,84,56,203,181,147,205,78,19,97,20,134,159,239,155,111,102,164,77,72,40,130,164,96,59,13,144,9,198,40,104,
+  32,97,163,27,72,240,6,184,0,55,77,220,176,96,227,206,112,1,46,154,184,97,195,5,52,222,128,209,149,113,229,66,
+  170,9,218,81,204,12,161,105,11,180,252,101,250,135,40,46,148,102,20,102,118,188,201,89,62,79,78,242,158,3,33,89,
+  92,90,157,156,89,120,122,182,184,180,58,73,68,100,24,236,57,238,250,243,103,143,241,28,119,61,74,34,163,224,98,105,
+  135,137,123,118,164,68,134,193,155,149,26,186,210,184,51,62,76,98,100,40,84,34,47,93,123,119,31,93,73,116,77,131,
+  51,232,49,12,44,59,115,169,68,252,15,151,234,135,8,4,66,128,16,130,79,197,18,166,161,56,61,253,201,23,183,138,
+  231,184,88,118,102,42,159,203,22,0,100,16,174,30,28,163,107,26,186,250,51,237,246,15,248,5,137,222,56,183,70,147,
+  12,247,247,5,55,153,3,80,231,112,237,184,129,174,52,132,16,8,4,141,86,135,218,190,207,221,137,155,92,51,21,239,
+  222,111,34,17,164,7,250,1,240,28,247,53,32,164,101,103,230,151,87,214,184,222,27,167,213,58,193,111,180,249,238,237,
+  178,181,93,167,92,57,164,217,232,80,174,28,161,164,36,102,26,108,237,213,241,28,151,212,88,234,62,128,204,231,178,111,
+  44,59,51,181,188,178,198,200,96,31,27,197,50,123,53,31,223,239,48,150,30,36,149,76,208,106,158,16,51,13,156,114,
+  181,11,191,124,241,228,67,183,133,124,46,91,56,151,60,122,120,155,184,105,16,55,13,142,14,154,72,33,80,66,242,209,
+  43,93,128,255,169,49,40,121,48,59,78,204,52,24,77,15,96,234,138,183,27,223,240,28,151,164,117,99,58,8,119,107,
+  12,59,166,175,197,29,94,21,62,95,168,46,82,16,148,252,173,44,20,142,76,224,27,231,184,202,252,6,31,155,240,68,
+  82,120,70,77,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> File = {  //size: 741
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,0,18,116,69,88,116,84,105,116,108,101,0,80,97,112,101,114,32,83,
+  104,101,101,116,115,185,175,145,249,0,0,0,23,116,69,88,116,65,117,116,104,111,114,0,76,97,112,111,32,67,97,108,
+  97,109,97,110,100,114,101,105,223,145,26,42,0,0,0,39,116,69,88,116,68,101,115,99,114,105,112,116,105,111,110,0,
+  119,105,116,104,32,97,32,72,85,71,69,32,104,101,108,112,32,102,114,111,109,32,74,97,107,117,98,134,84,7,179,0,
+  0,1,238,73,68,65,84,56,141,133,147,77,107,83,65,20,134,159,51,247,166,26,80,209,104,177,181,136,27,107,55,138,
+  127,64,68,220,248,1,42,184,240,71,40,226,170,11,23,130,63,65,20,44,85,132,46,42,22,63,176,208,160,155,254,27,
+  181,177,109,154,144,80,53,201,153,153,227,226,222,155,220,166,130,7,14,51,12,243,62,231,61,51,28,49,51,234,95,87,
+  111,136,200,98,8,241,52,24,102,134,25,128,1,252,1,89,143,49,60,191,123,231,222,58,227,97,102,172,174,125,252,190,
+  211,106,90,140,113,95,14,6,125,219,104,252,176,181,47,171,191,87,62,188,125,156,193,71,233,0,250,253,254,204,177,163,
+  53,118,127,237,210,233,182,217,105,111,179,213,108,176,241,243,27,155,219,13,186,157,14,55,175,221,170,78,158,152,124,178,
+  252,110,105,190,108,192,1,168,122,0,66,80,124,240,132,16,178,140,17,245,202,86,115,19,231,28,87,46,95,173,30,152,
+  56,248,244,205,210,171,75,99,0,5,216,39,142,49,16,99,36,196,192,251,79,43,212,235,117,46,156,191,88,197,108,126,
+  15,192,171,2,150,139,125,46,204,196,209,34,83,211,83,204,205,205,50,115,230,20,206,57,233,245,122,215,95,188,124,150,
+  236,113,96,249,131,154,25,6,8,130,147,81,86,171,85,142,28,62,196,236,217,115,168,250,68,85,43,0,105,249,13,138,
+  16,192,4,16,65,196,145,166,46,59,23,161,40,24,45,50,2,120,45,62,53,147,11,8,142,196,9,137,75,70,224,2,
+  224,21,139,86,2,104,1,112,56,103,24,130,12,189,148,151,108,227,189,39,134,184,23,32,128,115,110,120,187,168,38,185,
+  163,50,64,213,19,67,40,3,60,32,164,105,2,150,139,165,228,162,0,152,32,34,120,85,66,25,224,243,22,146,36,29,
+  86,23,100,228,34,95,45,27,16,84,149,48,222,2,64,226,146,76,92,206,146,131,108,182,254,1,16,145,70,187,221,154,
+  174,213,142,243,191,232,118,59,128,180,212,107,63,107,203,140,135,143,30,220,78,43,233,130,14,6,39,205,192,48,200,71,
+  186,188,7,163,82,153,216,86,213,251,139,11,175,63,3,252,5,198,186,65,227,184,230,144,207,0,0,0,0,73,69,78,
+  68,174,66,96,130,
+};
+const nall::vector<uint8_t> Folder = {  //size: 581
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,1,194,73,68,65,84,56,141,165,144,49,107,147,81,20,134,159,115,239,
+  87,44,41,226,224,16,186,20,28,156,50,118,235,46,153,28,130,208,213,95,160,187,208,63,224,226,86,87,255,135,40,196,
+  185,96,196,89,41,138,36,146,80,105,191,38,37,223,189,231,158,227,144,38,54,88,90,193,119,57,220,123,207,121,238,121,
+  95,233,118,187,252,143,170,221,221,221,55,238,254,248,154,183,11,119,127,50,24,12,142,110,4,152,89,175,215,235,221,51,
+  51,204,12,119,7,224,244,244,244,126,191,223,63,236,116,58,207,151,205,57,103,66,8,101,56,28,126,172,235,186,0,84,
+  57,103,87,85,38,147,9,165,148,21,36,132,192,246,246,118,7,120,187,132,2,76,167,211,170,148,242,170,174,235,131,43,
+  128,140,170,98,102,107,144,118,187,221,218,220,220,164,213,106,17,99,36,132,192,104,52,98,60,30,63,92,89,80,85,83,
+  45,28,126,72,204,154,63,63,129,0,224,204,129,249,234,214,61,0,123,251,236,236,237,199,24,190,85,57,103,79,41,49,
+  109,156,151,207,30,33,114,217,120,53,41,95,59,1,144,212,57,120,253,126,167,74,41,153,170,2,78,12,240,233,248,236,
+  234,2,203,178,158,124,20,30,180,183,64,220,43,85,37,231,140,136,16,131,16,227,117,35,235,202,106,184,57,130,120,149,
+  115,182,156,51,184,19,68,216,184,1,160,197,105,178,81,204,9,65,0,22,0,213,197,6,243,84,8,128,249,162,185,184,
+  99,230,152,59,89,23,117,105,161,73,182,2,184,106,65,164,98,82,55,12,127,205,105,212,110,180,80,69,97,114,54,95,
+  0,84,213,82,74,224,145,47,163,115,78,166,13,127,103,190,174,24,132,159,103,151,22,68,196,204,12,17,161,201,70,235,
+  78,117,107,136,34,194,69,82,128,82,185,59,199,39,134,8,124,254,58,186,117,120,169,217,108,142,192,81,101,27,91,253,
+  119,199,119,159,130,243,227,251,248,159,1,34,140,69,194,139,223,71,22,33,158,28,99,167,49,0,0,0,0,73,69,78,
+  68,174,66,96,130,
+};
+const nall::vector<uint8_t> Font = {  //size: 627
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,5,73,68,65,84,56,141,
+  157,147,205,75,148,81,20,198,127,51,119,26,103,116,104,132,176,15,130,138,172,137,92,181,16,218,106,6,173,219,105,229,
+  190,93,224,127,16,109,6,134,105,33,17,33,180,210,162,77,68,84,16,168,45,114,81,65,37,83,145,31,73,58,99,56,
+  78,227,152,54,239,124,221,251,222,247,180,136,25,169,209,192,14,28,184,220,115,249,61,231,121,224,250,18,201,248,85,32,
+  97,173,141,176,139,82,74,57,192,16,137,100,188,88,169,148,197,243,188,93,181,83,114,36,145,140,23,253,214,218,72,40,
+  20,102,108,98,14,128,177,137,57,68,132,209,241,89,60,207,50,58,62,67,177,92,101,248,233,60,90,107,180,174,81,171,
+  85,105,107,109,195,90,27,241,215,87,186,114,254,20,0,151,251,98,136,8,151,206,157,68,68,24,232,61,193,147,183,171,
+  164,243,37,106,198,34,34,136,72,195,74,3,80,87,254,187,231,87,138,188,251,186,129,118,45,217,245,202,206,128,186,242,
+  214,3,161,102,44,83,51,5,180,54,24,237,178,178,94,218,221,6,227,169,60,61,93,251,136,134,20,218,184,44,231,255,
+  1,168,123,174,171,103,214,202,212,140,203,209,142,48,29,123,131,104,227,146,206,57,59,3,238,77,206,55,134,174,245,120,
+  254,62,199,133,51,29,136,8,7,219,131,104,237,178,148,43,54,1,2,245,195,64,111,103,99,56,249,33,207,199,244,6,
+  67,119,215,208,198,160,181,139,49,46,217,82,133,82,85,19,14,6,154,1,247,95,44,208,223,211,73,110,179,74,106,241,
+  7,55,6,78,227,67,240,60,97,41,87,228,250,88,10,109,44,153,239,14,177,195,209,102,11,253,61,199,241,196,227,193,
+  203,12,23,207,30,162,173,69,17,14,42,90,91,20,7,218,67,104,227,162,141,203,98,246,231,246,25,120,158,240,248,205,
+  10,1,37,28,219,223,250,71,160,126,31,191,1,218,101,250,75,1,216,6,240,240,213,55,166,62,231,89,93,175,242,122,
+  54,223,0,204,45,111,50,252,232,19,65,191,16,218,227,99,122,161,192,200,179,217,173,12,148,82,78,185,92,138,12,246,
+  197,24,236,163,169,186,187,162,116,119,29,105,186,119,28,7,165,148,19,0,134,110,223,185,117,243,63,191,243,181,95,46,
+  181,156,109,120,254,28,88,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Image = {  //size: 558
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,11,10,16,7,18,54,148,178,182,0,0,1,187,73,
+  68,65,84,56,203,157,211,77,107,19,65,24,192,241,255,236,219,52,221,152,212,5,161,165,5,161,224,165,133,92,196,162,
+  23,83,232,201,131,159,64,232,103,40,158,235,49,24,131,245,11,8,222,122,245,32,210,64,181,216,30,10,213,158,84,68,
+  15,42,210,67,34,5,149,38,155,110,183,217,153,233,97,233,234,98,10,33,3,3,243,250,123,102,134,103,68,189,81,91,
+  3,238,51,90,121,66,189,81,51,90,235,145,106,189,81,51,14,128,49,134,7,143,158,178,181,243,110,168,176,75,213,5,
+  86,87,150,1,112,0,132,16,236,238,127,102,238,230,29,44,203,194,181,21,133,130,226,247,145,77,162,52,137,210,40,165,
+  1,72,148,97,107,231,77,30,48,198,224,216,130,160,236,51,51,25,115,183,90,36,40,5,108,236,126,231,237,123,63,67,
+  82,64,211,254,231,52,206,121,195,178,44,46,249,146,27,243,49,179,83,139,8,225,114,251,122,151,111,7,167,68,39,2,
+  165,83,64,41,147,187,206,95,64,8,74,227,146,78,104,19,39,45,92,187,204,159,163,46,190,156,64,58,160,207,1,125,
+  1,32,128,98,193,163,125,120,133,215,123,95,8,202,167,124,61,8,40,142,123,104,109,104,255,248,196,228,213,57,244,133,
+  128,5,37,95,130,16,244,142,125,122,61,131,231,128,235,167,111,244,106,239,37,133,49,143,153,217,249,193,64,210,239,19,
+  117,14,115,147,6,3,6,180,73,163,238,111,63,167,31,71,131,129,78,55,228,217,250,139,244,46,233,110,180,214,132,199,
+  17,97,24,81,153,78,135,155,205,38,120,151,51,192,202,105,158,196,118,36,8,135,190,22,196,137,193,113,199,168,76,159,
+  100,107,166,38,24,124,2,128,197,91,21,90,63,127,13,200,189,107,185,94,235,195,199,255,129,165,234,2,27,155,219,67,
+  167,114,14,16,66,176,186,178,156,165,231,48,69,74,153,1,235,141,199,15,239,141,250,157,207,0,181,88,225,150,139,66,
+  18,171,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Markup = {  //size: 709
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,22,20,19,51,203,197,162,151,0,0,2,82,73,
+  68,65,84,56,203,149,146,189,79,83,81,24,198,127,231,246,210,43,80,108,122,129,66,33,66,172,124,72,136,128,24,28,
+  28,140,68,93,48,33,38,14,70,101,209,77,227,232,162,49,33,93,252,7,12,38,24,163,131,31,3,49,46,234,224,228,
+  96,92,64,38,33,166,45,1,77,173,20,232,237,135,45,247,182,183,237,189,14,166,216,43,198,132,103,122,79,242,158,231,
+  252,222,243,188,98,118,118,246,101,34,145,184,192,222,116,113,122,122,122,14,128,80,40,100,239,85,161,80,200,174,58,201,
+  213,66,211,52,44,203,2,64,8,177,243,84,181,22,66,224,114,185,240,122,189,14,148,29,3,203,178,136,68,34,142,11,
+  181,18,66,48,48,48,176,107,22,185,246,208,223,223,255,95,130,127,73,174,109,172,37,168,234,243,90,134,165,175,89,178,
+  134,69,217,178,113,203,18,82,169,157,203,119,158,93,125,113,111,234,137,131,160,175,175,111,199,224,251,230,79,30,190,90,
+  32,208,166,50,121,230,24,7,59,155,105,80,20,210,121,131,149,216,22,139,203,177,199,151,110,63,189,238,48,136,70,163,
+  0,36,82,6,175,23,54,56,63,62,204,112,175,159,116,222,96,53,250,5,217,227,135,122,31,77,251,224,202,196,8,243,
+  203,241,49,199,8,85,130,185,71,239,153,60,53,196,145,160,74,42,153,160,165,107,16,111,75,0,189,80,34,17,91,69,
+  182,76,164,138,193,233,177,160,243,19,35,145,8,159,194,26,62,223,126,198,199,122,200,101,52,202,178,7,36,65,157,36,
+  227,174,179,105,80,100,124,13,10,1,127,43,138,226,222,157,194,219,197,143,156,24,237,34,147,78,146,52,27,241,119,116,
+  96,219,96,3,8,9,97,153,172,167,44,22,87,150,240,213,219,206,20,194,225,48,9,77,167,187,221,199,124,116,147,96,
+  175,31,203,134,106,46,249,76,138,227,35,131,8,33,176,109,155,248,250,6,210,223,4,5,179,140,162,184,145,60,126,50,
+  249,2,91,57,19,189,88,33,167,155,152,249,45,199,94,180,181,54,255,33,80,85,245,247,98,200,18,217,237,2,141,133,
+  31,120,100,133,205,237,22,146,117,110,202,21,139,66,170,204,72,169,68,177,104,210,228,105,68,215,117,196,204,204,204,155,
+  100,50,121,174,106,20,183,14,113,246,228,48,71,123,84,130,7,2,124,139,197,249,176,98,96,226,198,40,150,201,102,82,
+  40,162,194,141,201,33,52,77,99,215,126,94,187,251,252,126,123,64,189,57,53,49,202,225,238,86,0,210,153,44,15,222,
+  173,81,193,197,118,193,196,48,76,110,77,116,146,203,229,248,5,131,250,25,20,161,246,118,40,0,0,0,0,73,69,78,
+  68,174,66,96,130,
+};
+const nall::vector<uint8_t> Program = {  //size: 609
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,214,1,5,14,9,17,179,225,58,161,0,0,1,238,73,
+  68,65,84,56,203,165,147,61,104,83,81,20,128,191,123,238,123,105,140,177,105,82,7,155,162,173,70,170,77,148,214,90,
+  39,7,21,7,103,135,110,65,156,93,172,17,7,137,173,58,184,137,139,160,131,63,136,118,214,69,212,69,196,216,165,40,
+  175,138,105,165,224,79,138,144,34,181,146,180,9,36,105,124,215,193,164,160,77,176,234,129,51,221,251,125,231,222,115,207,
+  133,255,12,213,108,193,137,211,1,100,1,180,176,175,239,46,175,214,108,117,226,116,56,113,76,241,83,202,93,154,121,104,
+  156,56,38,125,156,45,141,246,74,179,202,59,206,63,49,75,233,123,106,254,241,9,182,158,188,70,165,202,108,35,137,52,
+  130,35,137,91,110,110,226,178,18,81,40,183,76,46,53,74,100,248,122,67,137,172,134,111,186,185,231,103,197,242,172,199,
+  246,111,194,227,11,98,89,54,249,241,139,68,78,173,150,72,13,238,7,178,219,19,55,220,252,139,17,209,150,141,29,236,
+  166,58,55,129,50,101,188,225,189,248,163,67,84,191,56,244,156,187,79,165,202,236,155,99,12,2,88,53,209,100,228,244,
+  109,55,159,74,138,214,22,118,107,39,246,134,48,170,180,128,39,180,13,29,236,1,37,24,99,112,23,51,196,174,76,49,
+  149,136,189,4,212,202,21,90,2,93,69,221,226,199,14,108,198,183,115,136,106,118,28,17,176,55,198,16,109,163,68,106,
+  169,49,197,249,82,157,171,159,32,60,61,122,56,219,155,124,144,255,94,204,4,150,63,62,66,219,62,172,64,23,162,53,
+  198,24,20,6,227,130,132,98,165,233,145,131,94,96,207,74,15,6,198,152,3,194,239,46,29,13,216,109,209,69,74,95,
+  49,133,207,136,183,13,37,130,136,70,41,141,213,190,171,14,135,7,198,120,253,203,43,212,37,83,23,142,180,174,235,27,
+  94,176,124,65,68,129,136,254,153,109,189,165,116,242,64,29,158,107,54,202,250,234,33,186,247,119,242,62,122,230,206,55,
+  41,204,132,116,168,7,183,125,112,249,109,98,183,253,59,252,199,81,46,125,120,90,168,100,158,149,157,56,166,54,39,127,
+  245,153,250,129,201,122,147,215,84,249,95,226,7,144,100,183,170,35,108,244,94,0,0,0,0,73,69,78,68,174,66,96,
+  130,
+};
+const nall::vector<uint8_t> Script = {  //size: 516
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,236,0,236,0,236,29,35,22,54,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,9,15,10,59,4,237,28,251,236,0,0,1,145,73,
+  68,65,84,56,203,149,147,191,142,218,64,16,198,127,131,86,162,12,206,3,32,10,10,55,72,199,91,68,138,168,82,240,
+  8,185,154,198,13,7,114,7,244,20,52,244,156,174,35,77,36,222,129,148,84,200,72,8,176,131,43,26,104,152,43,214,
+  235,24,78,119,209,141,52,187,171,209,206,55,223,252,147,201,100,242,114,56,28,126,240,57,105,247,251,253,103,0,194,48,
+  212,207,74,24,134,234,144,140,123,164,105,154,195,11,130,10,72,49,166,216,227,171,231,221,80,49,247,220,68,36,187,45,
+  16,128,170,3,211,55,185,152,98,4,231,128,8,162,5,187,100,12,84,222,7,144,34,3,205,12,130,5,18,7,252,17,
+  3,196,230,238,254,139,229,227,124,149,251,162,88,41,21,83,80,209,236,143,228,183,138,213,36,137,121,252,249,200,110,183,
+  123,7,64,29,135,2,227,76,255,38,49,189,94,143,32,8,232,116,58,92,175,215,183,0,82,228,231,10,42,16,39,49,
+  79,79,214,121,179,217,224,251,62,171,213,138,32,8,30,110,106,160,242,175,255,37,4,69,73,226,36,143,188,221,110,49,
+  198,80,175,215,73,211,148,40,138,150,65,16,52,239,230,64,17,205,134,72,36,119,222,239,247,24,99,242,25,41,151,203,
+  212,106,53,162,40,90,150,238,219,104,251,173,40,202,104,52,98,56,28,98,140,201,245,116,58,225,251,62,81,20,1,52,
+  115,0,207,243,240,60,143,74,165,98,245,75,133,106,181,202,96,48,96,58,157,98,140,225,114,185,0,176,88,44,0,154,
+  179,217,236,143,140,199,227,95,199,227,241,251,71,171,119,62,159,89,175,215,180,90,45,230,243,57,141,70,227,119,183,219,
+  253,118,195,252,191,251,219,110,63,0,75,23,217,217,95,1,152,44,191,17,163,238,139,75,0,0,0,0,73,69,78,68,
+  174,66,96,130,
+};
+const nall::vector<uint8_t> Text = {  //size: 333
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,22,20,15,56,186,96,38,66,0,0,0,218,73,
+  68,65,84,56,203,173,146,61,110,132,48,16,133,191,7,72,187,91,33,33,159,129,159,179,108,153,34,199,65,230,22,28,
+  33,81,218,28,6,115,1,26,250,173,76,138,136,21,36,94,88,162,188,106,44,123,222,124,51,30,181,109,251,49,12,195,
+  11,199,244,90,215,245,59,0,214,218,233,168,172,181,211,236,148,204,193,56,142,120,239,1,144,116,47,53,199,146,136,227,
+  152,52,77,87,40,119,3,239,61,206,185,85,194,82,146,168,170,234,87,47,201,242,80,20,197,38,65,72,201,242,225,146,
+  224,167,158,34,200,243,60,80,241,198,52,157,136,162,104,155,0,160,239,251,213,101,89,150,192,5,184,1,231,253,22,66,
+  4,146,144,46,15,23,98,69,224,156,219,28,222,55,209,206,47,132,9,180,79,32,137,174,235,130,149,231,233,135,140,130,
+  4,33,147,221,61,200,178,140,191,40,49,198,124,54,77,115,61,146,100,140,121,227,191,244,5,84,81,95,185,252,185,63,
+  236,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Video = {  //size: 592
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,4,28,9,32,30,1,50,75,149,0,0,1,221,73,
+  68,65,84,56,203,165,147,191,170,26,81,16,198,127,251,71,55,55,70,238,53,32,65,13,168,205,54,194,86,203,130,205,
+  54,10,130,104,231,59,228,33,146,34,16,244,17,52,15,96,17,172,197,70,43,27,219,72,34,9,11,130,141,165,138,112,
+  37,146,213,157,20,234,230,154,164,72,110,6,206,97,102,190,153,57,243,13,103,20,203,118,94,37,19,119,239,1,2,225,
+  44,114,190,5,4,68,4,80,78,30,57,97,135,195,33,248,238,251,175,177,108,71,122,189,158,108,183,91,25,14,135,82,
+  42,149,174,108,215,117,255,136,183,90,45,177,108,71,176,108,71,14,135,131,172,86,43,73,167,211,226,186,238,95,217,205,
+  102,83,44,219,17,149,255,20,237,69,58,243,54,249,60,129,105,154,56,142,195,100,50,33,22,139,133,246,120,60,38,30,
+  143,255,134,111,54,27,62,127,249,138,98,217,142,36,19,119,60,102,136,247,223,246,170,14,144,121,153,225,126,187,253,167,
+  214,159,221,222,6,31,63,205,84,21,224,137,97,0,208,237,118,169,86,171,24,134,65,52,26,37,18,137,80,175,215,209,
+  52,13,85,85,169,215,235,40,138,194,57,71,7,80,1,162,209,40,34,66,16,4,100,179,89,114,185,220,245,160,52,45,
+  76,20,57,209,208,117,29,128,83,149,51,56,157,78,201,231,243,152,166,73,173,86,3,192,243,60,42,149,10,166,105,226,
+  121,30,134,97,176,223,239,209,53,141,176,131,167,55,55,136,8,237,118,155,217,108,198,104,52,162,211,233,176,88,44,240,
+  125,159,76,38,67,54,155,101,50,153,224,251,62,34,18,210,86,1,34,145,8,0,229,114,153,245,122,77,185,92,166,84,
+  42,145,203,229,40,20,10,44,151,75,82,169,20,197,98,145,227,241,200,133,118,72,225,120,60,34,34,12,6,131,144,39,
+  64,191,223,7,32,159,207,211,104,52,174,102,16,4,193,207,2,23,231,229,132,191,225,172,207,231,243,80,255,53,78,191,
+  4,219,182,29,82,121,152,12,132,175,61,212,119,187,29,0,138,101,59,239,128,55,143,92,133,15,63,0,208,50,35,119,
+  229,61,168,67,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Go {
+const nall::vector<uint8_t> Down = {  //size: 683
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,61,73,68,65,84,56,141,
+  133,147,77,104,19,65,24,134,223,153,236,230,15,107,192,96,210,67,252,193,170,245,34,26,143,73,180,224,193,139,241,34,
+  20,47,30,42,226,65,20,234,161,85,241,42,8,210,21,60,169,80,132,156,20,33,162,82,83,91,114,177,214,85,8,181,
+  45,20,123,168,85,180,84,205,154,174,253,11,233,38,179,179,159,135,214,26,155,24,223,227,55,223,60,243,190,243,205,48,
+  34,66,181,226,221,106,30,132,48,234,137,193,208,123,68,115,117,73,169,105,34,132,111,93,72,195,118,4,108,71,96,201,
+  42,96,201,50,1,0,119,31,221,168,1,215,2,0,48,198,144,155,121,142,178,93,66,161,56,131,143,115,99,104,63,120,
+  181,174,41,94,175,88,29,202,163,248,235,110,108,8,0,17,136,28,16,17,20,238,110,8,80,18,151,85,157,28,196,214,
+  237,187,96,57,36,189,146,108,72,178,177,92,54,81,189,22,239,82,215,13,50,142,55,10,57,72,69,66,45,209,139,39,
+  175,251,20,151,10,34,199,43,73,130,152,4,184,68,81,22,160,122,57,184,194,112,254,212,53,47,8,16,162,130,116,54,
+  85,154,95,156,75,49,34,66,162,91,77,183,69,143,39,143,68,147,158,247,249,87,144,92,64,162,140,21,123,17,211,243,
+  57,16,104,45,22,71,219,246,211,24,157,24,41,79,78,141,247,189,238,17,237,124,45,114,199,203,177,140,241,197,152,162,
+  72,112,47,136,87,0,151,141,66,101,26,170,143,193,237,227,80,125,46,68,35,71,97,46,152,52,249,97,220,32,194,153,
+  245,75,212,53,81,4,33,249,112,240,158,229,229,77,8,248,131,40,209,79,148,93,139,112,123,57,220,62,23,182,109,217,
+  131,144,175,5,217,161,140,69,14,146,186,38,138,127,77,65,215,196,68,69,172,116,62,24,188,83,218,17,56,0,155,149,
+  160,122,86,79,222,188,41,128,253,161,99,232,203,62,46,9,81,233,212,53,49,81,119,140,186,38,122,103,141,79,47,134,
+  199,6,68,107,48,1,174,48,40,110,142,67,225,19,200,189,123,91,249,97,126,235,215,53,209,219,240,29,144,131,142,161,
+  145,129,239,166,177,64,187,155,98,216,229,143,193,252,186,76,185,81,61,239,200,213,220,13,1,186,38,138,36,145,204,100,
+  159,89,91,177,15,65,167,21,79,251,159,88,142,252,147,187,90,108,227,111,252,173,120,151,122,46,210,188,243,54,0,204,
+  230,63,95,218,104,253,191,0,0,56,124,69,189,15,0,195,55,197,217,127,245,252,2,115,74,18,42,134,104,28,203,0,
+  0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Home = {  //size: 606
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,10,14,20,37,19,83,42,210,59,0,0,1,235,73,
+  68,65,84,56,203,149,147,191,107,83,81,20,128,191,123,251,222,75,211,64,242,36,160,85,135,100,81,123,19,104,85,172,
+  17,92,28,140,66,19,167,135,212,74,39,145,162,163,24,92,58,180,110,193,169,254,24,234,212,37,139,245,199,96,19,240,
+  15,240,63,16,121,91,92,196,90,219,240,98,81,137,33,121,215,33,246,217,151,80,33,103,186,92,206,247,221,115,14,231,
+  194,1,225,41,85,172,128,174,128,246,148,122,200,48,177,7,123,74,105,79,169,61,73,113,104,120,177,56,163,203,55,111,
+  252,87,34,250,225,154,235,110,20,148,98,253,220,89,182,39,78,113,228,240,56,39,188,38,83,107,107,212,92,151,130,82,
+  215,14,185,110,117,64,176,31,126,83,156,97,247,248,49,148,202,50,22,141,210,106,181,24,219,252,74,182,92,30,144,136,
+  126,184,122,251,22,157,100,146,116,42,77,42,149,2,64,107,104,183,219,236,214,235,156,44,149,66,18,177,31,126,247,160,
+  68,199,48,201,229,46,96,219,118,208,154,214,26,0,223,247,249,185,181,69,114,110,46,144,136,10,232,130,82,188,127,84,
+  198,107,54,57,63,157,35,30,143,35,132,8,9,124,223,15,206,134,16,140,230,243,212,92,23,3,184,3,172,78,157,62,
+  195,253,210,61,54,170,111,3,240,201,202,51,86,30,63,69,139,17,116,167,133,48,70,209,221,223,44,47,45,209,238,165,
+  204,202,121,120,190,48,153,33,17,79,0,160,212,4,153,140,34,155,205,244,94,20,146,145,244,149,222,196,83,121,144,22,
+  90,107,22,38,51,204,195,186,236,239,211,178,44,76,211,196,48,140,224,254,75,227,7,0,31,234,59,116,187,126,144,11,
+  48,32,144,82,34,165,196,146,159,1,232,96,241,241,83,3,128,111,222,47,58,34,18,90,36,99,96,179,254,14,207,231,
+  40,0,151,46,78,115,53,26,67,234,113,46,75,147,237,77,66,21,24,253,21,252,155,126,239,165,239,222,14,162,217,56,
+  112,245,3,65,36,18,193,182,109,18,137,4,2,137,16,16,139,197,112,28,103,0,178,44,43,252,23,174,207,58,175,0,
+  135,225,98,245,229,139,215,119,255,0,86,248,213,163,133,187,128,26,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Left = {  //size: 655
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,33,73,68,65,84,56,141,
+  133,147,79,72,20,113,20,199,191,111,102,126,179,59,187,82,98,108,173,27,181,66,8,17,9,130,149,110,91,135,220,34,
+  86,211,76,68,34,136,14,37,116,178,162,45,186,122,137,16,3,59,196,30,36,240,208,37,10,130,176,110,29,36,87,138,
+  142,21,30,34,75,247,80,185,108,146,187,237,204,238,204,239,247,235,48,153,174,140,238,131,119,122,127,248,188,239,123,15,
+  82,74,212,242,163,55,181,43,241,91,90,198,43,166,97,11,139,167,88,29,17,38,163,225,230,174,133,31,159,13,175,28,
+  101,139,226,22,16,62,157,60,220,223,115,109,224,158,103,49,0,111,130,120,138,13,25,190,186,241,139,201,97,99,207,206,
+  102,82,104,115,208,170,200,42,242,190,200,129,174,193,83,87,13,147,175,224,227,247,105,28,137,246,128,84,88,241,20,147,
+  85,213,132,159,36,165,92,135,76,83,137,67,125,141,29,7,59,217,215,95,31,80,176,242,208,84,134,88,83,31,136,20,
+  16,0,9,64,74,1,33,5,238,164,47,184,4,241,20,27,10,26,219,31,244,159,184,228,175,175,111,160,247,139,175,192,
+  165,3,166,232,16,146,99,102,254,25,136,8,4,130,132,171,126,123,180,215,29,225,216,109,246,168,169,113,255,249,238,227,
+  131,70,174,180,128,119,223,222,128,49,31,116,77,7,87,4,44,81,128,105,22,80,180,86,80,182,75,80,72,133,159,5,
+  209,186,59,177,166,1,231,54,44,251,15,76,231,55,74,50,15,112,1,135,155,168,8,19,92,114,72,46,193,29,9,199,
+  22,224,142,59,114,174,152,117,215,56,51,106,95,206,46,125,185,254,248,101,218,132,237,147,173,123,19,144,122,25,66,47,
+  129,5,0,127,64,133,30,80,193,12,5,204,175,64,213,168,90,199,245,34,146,130,169,182,150,88,184,189,45,166,207,21,
+  94,35,95,153,135,144,192,233,200,13,136,127,20,220,118,29,0,210,79,238,174,53,248,191,70,21,147,187,118,68,146,231,
+  206,12,4,138,108,17,115,203,211,232,12,13,227,254,196,136,197,43,240,111,186,198,141,135,196,116,125,188,183,251,172,17,
+  14,135,40,168,132,48,250,112,4,153,49,155,54,230,122,158,114,102,204,158,176,43,149,142,231,47,158,102,103,103,223,150,
+  203,229,138,87,90,181,6,94,182,58,82,195,182,80,50,191,156,11,120,17,212,124,229,90,239,252,23,109,243,52,116,236,
+  202,203,9,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Right = {  //size: 676
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,54,73,68,65,84,56,141,
+  149,146,75,72,84,97,28,197,127,223,227,206,195,70,195,20,82,196,94,68,32,104,20,4,133,211,70,104,209,34,130,4,
+  91,70,129,66,184,201,114,140,54,66,43,55,141,144,139,40,114,19,20,189,164,69,98,62,112,17,45,102,208,41,164,20,
+  55,209,34,44,51,157,177,129,30,227,120,239,220,251,181,8,95,51,19,212,129,179,249,254,135,195,249,159,255,135,49,134,
+  124,134,187,116,172,177,83,183,21,155,229,83,82,4,198,163,113,119,245,129,190,112,151,53,16,142,88,161,98,154,53,20,
+  53,0,184,210,210,27,60,121,244,236,105,4,179,225,136,213,240,223,6,74,106,154,14,159,241,181,55,95,175,13,250,67,
+  19,225,136,213,86,76,39,26,35,250,43,134,157,91,30,21,217,155,237,131,129,169,207,99,84,149,237,195,39,183,241,120,
+  252,214,202,135,249,153,97,99,56,31,139,58,63,55,12,58,181,233,185,120,31,41,20,74,40,132,144,8,4,0,137,79,
+  67,120,184,148,249,119,176,171,188,158,248,244,184,51,54,57,176,128,49,167,98,81,103,6,64,3,184,94,142,216,199,103,
+  40,169,81,66,129,16,8,1,66,1,194,99,217,158,35,189,52,207,193,186,35,86,77,213,158,218,167,99,119,39,194,17,
+  171,35,22,117,250,53,64,206,91,37,157,89,192,118,179,216,110,22,165,52,165,37,101,148,4,66,248,45,63,70,184,216,
+  94,142,119,75,163,212,132,234,68,107,115,87,201,208,171,71,125,199,175,90,199,52,192,210,143,57,222,39,95,175,119,160,
+  125,130,148,35,209,89,137,214,10,191,47,136,79,6,80,248,41,205,149,163,76,144,172,157,49,235,43,108,41,80,130,212,
+  2,101,201,63,244,129,167,178,160,44,26,42,154,72,47,102,204,131,151,119,86,178,171,153,141,21,0,90,14,93,91,55,
+  80,150,64,105,201,200,92,47,218,39,169,14,237,167,110,251,9,38,223,196,237,196,219,248,87,227,178,169,68,193,226,237,
+  39,61,91,206,40,45,178,151,206,117,7,148,82,212,87,54,81,161,247,50,240,252,97,102,49,249,101,196,184,121,103,52,
+  198,20,124,142,112,196,50,151,91,187,249,229,37,73,166,82,230,197,232,224,138,237,216,29,177,27,78,127,190,182,160,131,
+  53,56,57,155,196,84,98,117,122,118,106,209,219,20,185,160,179,191,37,168,40,175,204,124,251,158,26,54,46,23,54,71,
+  254,167,4,66,18,95,78,167,238,197,162,133,145,243,241,27,65,222,9,180,110,118,229,182,0,0,0,0,73,69,78,68,
+  174,66,96,130,
+};
+const nall::vector<uint8_t> Up = {  //size: 652
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,30,73,68,65,84,56,141,
+  149,147,79,104,19,65,20,198,191,55,187,51,217,141,133,122,104,76,255,209,130,4,237,193,64,42,168,208,213,138,104,68,
+  98,69,45,20,114,107,74,201,73,144,98,22,193,171,199,82,145,160,23,15,69,79,30,165,66,241,226,77,42,94,68,80,
+  208,67,41,180,42,149,148,52,137,166,154,38,217,217,25,15,81,172,33,169,246,29,223,251,230,199,124,223,155,33,173,53,
+  218,213,201,155,124,30,0,150,102,189,233,118,26,214,110,224,184,60,221,223,21,73,246,117,29,76,58,46,79,239,9,224,
+  184,60,26,16,251,178,87,206,76,6,199,70,147,193,128,8,102,29,151,71,255,11,224,184,188,3,132,197,137,115,211,214,
+  150,151,199,150,183,137,248,200,37,11,132,69,199,229,29,255,4,16,225,209,232,112,162,167,55,52,72,159,75,31,240,177,
+  244,30,157,157,157,20,27,58,209,13,194,195,93,1,142,203,211,253,7,34,137,179,71,199,249,74,254,13,12,198,97,50,
+  142,87,107,11,136,13,29,23,161,253,61,137,230,60,216,142,195,81,193,237,108,42,225,218,235,229,101,104,242,32,132,137,
+  58,125,199,54,21,241,46,255,28,23,78,95,13,154,166,248,43,15,182,211,247,212,88,198,82,228,161,34,75,16,1,1,
+  30,224,40,203,117,8,219,64,65,174,162,168,86,113,254,212,69,139,216,159,60,216,111,223,241,99,227,221,135,250,98,148,
+  175,172,193,228,6,56,55,80,81,5,84,141,175,16,54,131,176,25,150,191,189,64,184,55,68,71,14,15,135,137,53,242,
+  160,145,140,153,30,232,142,100,111,76,220,177,5,15,64,65,194,135,135,215,185,5,188,45,60,131,100,21,16,35,36,6,
+  50,208,90,67,41,160,94,175,225,241,211,249,74,177,180,57,99,18,67,234,83,110,197,158,185,119,185,225,137,163,122,247,
+  250,19,171,44,115,240,141,109,112,193,192,76,130,193,9,115,15,110,87,101,77,91,191,236,7,137,33,101,46,205,122,78,
+  211,38,180,214,10,165,250,23,48,131,96,112,6,30,104,64,100,77,91,47,231,60,218,245,29,0,128,210,62,126,212,139,
+  141,43,251,26,178,174,160,85,235,63,99,182,106,250,74,162,90,219,134,50,53,160,21,124,73,240,189,61,0,164,47,17,
+  31,188,6,98,0,49,2,17,246,0,32,108,220,186,63,25,110,37,38,194,70,115,239,39,48,247,197,219,182,208,154,34,
+  0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Media {
+const nall::vector<uint8_t> Back = {  //size: 770
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,12,6,17,45,2,189,124,103,101,0,0,2,143,73,
+  68,65,84,56,203,165,83,75,72,84,81,24,254,206,189,215,251,56,231,58,51,62,152,115,238,216,66,169,241,65,153,20,
+  101,66,104,17,33,210,178,168,36,104,17,73,45,42,90,181,13,83,194,32,13,119,17,101,49,52,139,138,158,154,101,229,
+  34,136,44,180,32,113,81,173,194,69,138,66,216,99,102,238,189,94,157,211,194,81,52,90,8,254,240,111,190,255,255,126,
+  190,255,5,172,209,200,42,114,20,0,38,0,9,192,253,7,183,53,0,206,127,72,147,185,4,150,115,146,35,187,0,12,
+  0,145,92,28,26,179,25,154,154,26,39,22,153,3,207,95,196,8,33,150,162,40,249,144,80,67,225,112,69,60,190,97,
+  255,200,200,135,11,65,16,148,100,179,217,226,104,52,26,41,47,143,31,24,30,30,238,208,120,84,0,0,238,222,185,135,
+  195,205,135,32,184,3,69,85,34,233,116,58,182,62,94,118,212,102,246,9,0,129,174,235,215,77,211,204,171,170,170,56,
+  98,231,219,167,8,33,243,130,59,29,138,227,136,21,218,253,89,63,42,132,104,222,182,125,107,255,142,218,218,99,125,189,
+  253,150,150,167,233,149,149,241,157,117,117,181,189,155,106,170,79,62,126,244,196,84,85,85,229,142,128,198,163,28,148,90,
+  0,0,74,41,118,237,105,232,41,42,40,44,107,109,109,43,8,133,66,68,102,37,108,102,27,156,243,203,29,23,47,177,
+  130,194,66,34,179,18,148,82,104,170,14,77,56,2,127,82,191,150,20,116,95,233,174,9,135,34,154,55,235,34,157,74,
+  129,82,134,150,227,45,164,190,190,193,246,102,189,37,140,217,12,69,5,22,52,193,57,130,57,31,0,96,154,38,166,166,
+  167,164,174,235,200,184,41,120,190,15,198,24,92,207,133,231,187,200,184,25,120,158,11,198,24,24,165,40,46,226,80,162,
+  130,195,48,116,0,128,97,232,120,248,224,254,183,151,131,3,191,45,147,33,38,214,65,81,84,188,29,122,35,147,201,132,
+  171,231,25,75,152,197,44,112,206,161,56,92,44,155,129,133,205,213,91,118,127,249,252,245,106,34,209,243,243,211,232,71,
+  55,152,11,64,41,243,77,202,206,222,74,220,152,233,127,214,231,7,115,1,24,165,16,66,64,227,130,195,52,141,92,11,
+  6,4,231,136,149,236,235,118,51,153,107,239,222,15,181,143,141,141,30,228,130,207,111,172,172,126,154,158,75,189,154,153,
+  254,113,250,118,242,230,153,108,86,18,193,57,52,46,22,214,216,214,126,30,0,192,29,1,85,81,167,33,101,184,180,180,
+  244,220,248,196,120,215,235,193,193,206,198,189,194,39,144,105,25,91,223,57,49,57,158,156,248,62,217,197,29,1,34,165,
+  92,113,202,158,231,193,178,172,73,0,240,124,95,39,144,139,103,235,27,134,57,227,186,158,10,192,6,36,93,237,51,65,
+  74,105,0,0,33,196,95,134,145,133,66,107,180,191,249,146,220,194,180,154,76,242,0,0,0,0,73,69,78,68,174,66,
+  96,130,
+};
+const nall::vector<uint8_t> Eject = {  //size: 628
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,6,73,68,65,84,56,141,
+  149,83,77,104,19,97,16,125,179,155,152,118,105,44,65,232,154,221,110,18,130,43,98,2,66,65,5,181,90,35,136,138,
+  20,255,218,24,155,24,5,65,17,4,79,94,148,30,60,121,82,207,158,12,90,170,130,224,201,131,182,122,240,162,222,60,
+  24,17,84,234,15,173,105,77,211,109,218,36,187,109,50,30,154,72,210,36,37,14,124,48,135,247,30,111,102,222,71,204,
+  140,102,69,68,118,0,196,204,86,51,140,109,13,242,122,77,211,54,51,179,64,68,159,153,217,104,89,128,136,236,46,151,
+  75,219,179,119,215,83,129,32,189,126,245,230,32,17,37,153,217,172,3,51,115,205,3,64,0,60,39,78,29,127,50,54,
+  246,98,233,221,251,183,203,131,225,147,227,0,124,229,113,106,240,141,28,108,216,183,191,247,72,32,24,232,239,235,11,217,
+  64,192,246,29,59,119,103,178,115,209,151,207,199,239,1,152,110,234,0,128,164,235,122,111,44,30,205,230,242,57,206,46,
+  204,243,124,214,96,211,50,249,252,133,120,174,167,103,219,97,81,20,59,106,56,149,70,81,20,209,239,247,111,58,61,20,
+  254,144,252,148,44,230,11,121,78,205,76,241,239,233,73,94,88,204,242,196,247,137,210,153,88,248,71,48,24,220,234,245,
+  122,109,117,35,248,124,190,141,221,30,229,202,129,80,104,139,174,235,66,102,46,13,34,2,17,33,151,95,132,170,40,20,
+  30,136,200,204,143,110,78,254,156,186,10,224,23,42,75,137,68,34,157,197,98,241,144,236,238,186,127,231,246,221,54,81,
+  16,27,158,150,153,113,99,248,122,225,219,151,175,215,28,142,246,135,137,68,34,99,3,0,85,85,59,102,254,164,6,102,
+  211,233,117,151,46,95,180,168,106,55,88,213,155,166,101,111,107,119,196,84,165,251,25,128,21,1,143,199,179,228,118,187,
+  135,13,195,184,69,68,171,51,81,215,75,146,84,112,58,157,22,80,14,146,166,105,210,232,227,145,143,13,125,55,169,254,
+  163,199,2,0,82,21,7,34,0,140,60,24,109,137,124,246,92,180,168,40,138,88,237,160,36,8,194,114,44,62,68,107,
+  83,255,21,203,178,92,2,202,87,0,208,9,160,11,43,49,110,73,0,64,26,192,44,85,125,103,250,79,1,6,128,191,
+  20,183,247,30,217,9,206,54,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Flash = {  //size: 607
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,1,220,73,68,65,84,56,141,133,147,59,107,148,65,24,133,159,249,110,
+  43,209,40,134,40,152,8,171,177,49,177,241,2,134,32,66,44,20,4,11,11,27,11,123,241,31,216,139,32,22,234,191,
+  208,50,8,130,133,63,32,44,22,65,188,173,96,52,70,99,130,65,55,155,221,253,246,75,230,61,22,123,55,31,241,133,
+  153,102,230,60,51,115,206,59,78,18,103,166,103,230,128,171,128,241,255,10,128,183,102,54,187,80,154,175,68,0,146,174,
+  92,186,56,19,7,1,4,8,156,90,91,165,1,165,112,136,128,242,167,197,169,229,149,213,57,96,54,234,95,62,57,242,
+  156,201,177,175,56,6,133,173,213,214,92,254,121,28,239,175,37,75,223,87,46,0,116,1,50,99,114,236,51,51,215,31,
+  18,186,117,0,178,44,35,77,155,164,105,147,102,179,137,56,132,74,143,121,179,234,187,224,46,192,100,56,28,97,60,74,
+  90,253,64,218,104,80,175,87,169,109,254,96,227,207,55,178,172,198,248,177,91,4,65,56,240,180,222,13,4,56,71,181,
+  178,200,175,229,87,212,106,117,26,141,12,23,12,179,119,120,156,131,133,136,125,251,71,219,15,177,28,128,9,73,84,42,
+  155,100,156,35,26,218,102,228,0,36,145,17,199,34,137,33,9,214,144,60,222,231,1,36,22,190,28,5,61,2,183,51,
+  187,78,189,91,58,130,247,189,13,81,199,99,147,241,162,116,138,151,175,167,112,78,168,63,9,117,124,2,111,142,98,49,
+  199,3,83,11,98,190,35,112,109,173,6,32,32,212,215,110,189,62,48,33,19,166,157,39,247,131,28,96,214,139,49,232,
+  233,141,213,245,13,146,56,160,16,135,173,145,244,198,158,36,162,144,132,172,253,222,196,219,191,38,10,228,141,208,109,115,
+  251,242,225,60,239,186,117,239,89,21,41,39,5,51,99,203,66,206,222,184,191,43,192,63,189,137,229,196,168,45,111,156,
+  152,152,224,99,249,125,238,95,104,93,212,81,44,22,241,38,104,255,92,39,137,211,231,167,31,32,238,8,134,216,181,11,
+  218,28,104,2,119,23,74,243,79,254,2,78,177,239,207,22,156,213,133,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Floppy = {  //size: 561
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,1,195,73,68,65,84,56,141,
+  149,147,191,139,19,65,20,199,63,51,187,185,132,228,20,148,52,70,136,44,28,92,35,40,34,28,136,86,138,165,133,181,
+  216,28,162,127,131,98,103,165,157,40,254,3,162,133,216,88,164,201,157,88,168,136,88,92,171,22,129,92,97,227,110,147,
+  236,50,155,157,247,198,34,119,249,33,39,196,47,51,195,131,225,251,229,243,30,60,179,117,239,245,91,17,185,201,84,129,
+  185,22,235,35,255,172,181,175,98,17,185,241,112,251,42,91,31,158,130,170,9,170,160,74,8,106,56,172,85,65,101,94,
+  135,192,206,245,7,60,123,243,249,118,12,32,26,248,253,226,49,65,132,85,165,215,238,3,16,3,70,68,56,245,232,57,
+  132,191,169,255,173,202,203,44,0,47,129,147,183,238,174,108,6,144,119,223,23,3,20,17,97,56,28,174,100,78,146,100,
+  137,192,136,40,81,20,145,36,201,234,4,94,231,4,114,48,188,193,96,176,146,185,219,237,46,207,160,58,72,251,31,130,
+  229,22,188,178,253,228,35,141,236,43,81,177,127,180,195,76,159,73,227,12,121,235,44,27,167,143,207,9,188,8,76,10,
+  162,98,159,43,151,47,81,175,215,169,213,106,179,107,173,165,44,75,242,60,103,119,247,61,249,218,6,222,183,0,176,135,
+  56,170,138,181,17,237,118,155,126,191,79,175,215,163,217,108,34,34,140,199,99,70,163,17,69,81,128,177,168,42,126,177,
+  5,149,128,170,98,12,180,90,173,25,117,154,166,148,101,137,115,14,231,28,101,89,18,66,32,168,80,85,11,67,156,84,
+  158,216,78,55,36,203,178,89,64,150,101,75,102,231,28,1,80,85,42,239,167,1,214,152,47,123,63,127,157,223,236,172,
+  55,198,89,32,77,83,58,157,14,222,123,210,52,93,50,59,231,32,192,137,99,107,51,2,115,241,206,203,117,99,248,84,
+  183,126,243,92,252,173,110,76,76,32,48,61,7,187,177,176,35,134,192,158,191,144,139,169,27,145,240,227,15,253,243,15,
+  58,103,73,91,81,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Next = {  //size: 771
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,12,6,17,45,27,217,23,207,165,0,0,2,144,73,
+  68,65,84,56,203,189,146,205,75,84,81,24,135,127,231,222,59,247,156,123,103,70,156,132,57,231,14,153,180,8,196,116,
+  6,197,153,6,218,180,16,170,255,32,136,136,218,180,206,2,147,74,164,54,130,81,88,67,209,70,8,103,81,145,86,138,
+  50,72,43,69,173,69,148,203,180,175,77,230,76,126,204,140,51,222,249,242,158,22,142,34,214,50,122,224,133,195,195,121,
+  15,191,243,242,2,255,128,26,0,202,62,103,0,48,255,226,255,128,0,176,170,103,7,64,26,64,17,64,109,245,17,9,
+  32,95,45,103,207,221,93,52,183,219,68,36,18,233,94,88,88,28,78,165,82,105,69,81,86,92,46,87,33,28,110,239,
+  94,92,252,60,146,205,100,62,129,192,112,28,103,67,74,137,83,167,79,46,237,52,39,18,147,1,69,112,11,134,201,46,
+  6,67,205,137,104,52,114,206,235,245,114,93,215,3,204,160,231,91,130,71,39,66,109,193,46,143,199,91,111,89,129,90,
+  193,183,3,60,123,250,28,0,192,253,2,10,183,4,84,85,85,95,189,124,205,154,67,45,151,162,209,200,104,99,227,145,
+  227,154,75,211,199,70,199,141,99,145,200,133,246,112,219,184,16,226,76,177,84,244,239,141,111,89,2,26,247,251,161,83,
+  13,138,162,226,193,64,204,179,190,182,230,238,190,126,173,63,159,203,83,41,37,250,250,250,107,178,217,172,183,183,183,231,
+  198,161,195,245,103,21,16,0,128,105,154,224,126,14,205,18,2,133,178,13,0,200,231,114,160,6,37,15,99,143,60,211,
+  211,83,187,78,115,169,228,78,255,221,3,153,108,186,166,179,243,242,110,2,97,9,104,92,8,172,172,38,65,8,176,158,
+  89,5,99,6,32,37,236,130,13,66,128,181,204,42,24,165,144,142,68,50,149,148,140,49,0,0,99,12,130,115,40,156,
+  115,24,110,3,138,162,34,32,14,66,119,81,196,227,79,236,153,217,105,185,227,12,230,198,228,155,68,118,100,248,197,55,
+  74,117,0,0,165,58,252,130,67,19,66,32,249,235,7,202,149,50,38,38,198,138,95,191,127,217,244,249,234,186,76,211,
+  125,191,92,41,179,143,243,239,237,183,115,179,197,236,70,254,113,176,165,117,96,118,110,106,105,123,6,6,44,46,160,9,
+  206,241,97,94,146,161,248,96,201,231,171,139,181,134,219,99,110,205,83,172,84,202,247,134,226,131,165,92,62,63,210,26,
+  10,223,52,76,211,118,42,21,194,24,173,126,129,130,11,14,141,91,2,170,162,204,52,53,54,93,9,88,13,203,68,193,
+  166,4,209,150,39,127,190,59,209,209,113,181,33,208,176,76,128,2,8,201,108,57,91,28,0,110,221,238,217,222,3,33,
+  64,108,219,174,174,39,217,4,144,51,12,182,85,44,22,124,0,40,0,71,130,164,25,165,37,0,176,109,219,218,25,226,
+  46,82,74,175,148,146,236,115,84,74,73,241,63,248,13,221,115,236,68,32,12,77,214,0,0,0,0,73,69,78,68,174,
+  66,96,130,
+};
+const nall::vector<uint8_t> Optical = {  //size: 931
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,13,215,0,
+  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,4,1,17,53,53,42,125,41,75,0,0,3,48,73,
+  68,65,84,56,203,109,147,203,79,92,101,24,198,127,223,119,206,249,56,103,206,64,231,82,24,40,101,46,13,139,138,181,
+  11,77,105,162,27,218,196,109,53,154,214,148,82,117,211,24,132,181,43,255,0,141,91,40,184,211,136,216,164,186,108,216,
+  168,69,161,216,185,64,36,182,44,96,8,129,4,40,48,131,23,152,14,115,174,110,74,98,136,239,242,201,251,203,179,120,
+  158,71,112,226,134,134,7,47,3,31,2,125,64,238,133,188,6,76,3,95,141,142,140,229,255,251,47,78,192,99,201,68,
+  242,245,219,183,63,216,239,232,232,120,213,178,172,102,128,122,189,94,219,218,218,124,50,49,241,77,164,186,95,157,27,29,
+  25,27,228,127,156,167,30,205,205,126,231,121,94,176,180,180,20,46,44,44,132,165,82,41,92,92,92,12,203,229,114,88,
+  173,86,67,223,247,131,217,217,153,169,161,225,193,169,99,78,59,118,238,239,31,16,47,157,239,185,145,207,231,229,193,193,
+  1,190,239,35,132,64,8,129,97,24,72,41,105,52,26,34,155,205,117,39,18,137,253,136,109,190,86,44,148,30,104,67,
+  195,131,151,147,137,228,199,183,250,7,174,46,47,47,75,223,247,169,213,14,57,172,29,80,171,29,80,175,63,71,211,53,
+  154,148,66,211,116,194,48,164,187,187,187,51,159,127,28,121,229,226,133,121,173,183,247,210,167,119,238,124,148,118,29,55,
+  85,175,215,113,93,7,93,55,104,110,110,33,213,214,78,107,107,27,137,68,146,74,117,151,38,213,132,227,184,248,190,79,
+  170,45,229,46,46,254,110,233,64,223,217,206,179,185,74,165,130,82,10,215,115,137,218,205,172,174,174,146,47,252,6,16,
+  222,184,254,158,200,229,206,177,82,94,38,17,79,226,121,30,103,206,116,158,6,250,36,144,51,77,83,41,165,48,77,19,
+  33,32,30,143,147,47,60,14,35,150,125,209,247,253,214,239,127,184,31,196,98,49,170,213,10,158,231,225,56,14,134,97,
+  24,64,78,63,142,83,41,69,16,4,232,186,142,109,219,72,41,130,63,255,218,223,0,208,52,205,1,76,199,113,240,60,
+  15,33,4,190,239,11,64,72,96,173,209,104,120,74,41,148,82,156,58,21,163,209,104,240,238,59,215,165,82,234,153,174,
+  235,171,111,93,123,91,173,175,175,163,84,19,174,235,226,121,30,135,135,135,62,176,166,3,211,219,219,219,201,116,58,221,
+  10,208,158,106,103,119,111,135,158,158,30,241,249,103,95,152,128,185,177,177,193,207,15,127,36,151,61,135,235,186,68,34,
+  17,118,118,119,234,192,67,173,183,247,82,165,188,186,114,237,74,223,213,184,166,105,232,186,142,101,90,236,85,246,152,159,
+  47,242,228,201,31,84,170,21,218,90,83,8,33,145,82,210,213,213,197,189,123,147,255,212,143,234,159,104,197,66,105,243,
+  229,11,61,231,99,177,88,71,186,43,221,34,132,64,74,13,93,55,136,70,155,137,199,19,216,118,20,41,37,71,71,71,
+  100,179,89,138,197,194,223,79,151,158,222,191,59,58,254,165,6,80,44,148,30,88,17,243,77,219,142,158,206,100,50,166,
+  148,18,199,113,8,130,128,48,12,1,176,44,139,76,38,195,220,220,35,231,151,95,167,103,238,142,142,223,4,52,237,184,
+  211,197,66,233,219,104,115,36,59,59,59,147,75,165,218,149,105,154,178,165,165,5,203,178,0,216,220,218,244,39,39,39,
+  158,175,148,87,190,30,29,25,187,249,2,11,197,137,101,234,183,6,250,223,176,237,200,251,186,166,95,17,82,116,9,33,
+  68,16,4,155,158,231,253,100,24,198,248,201,57,255,11,186,149,81,110,74,152,180,58,0,0,0,0,73,69,78,68,174,
+  66,96,130,
+};
+const nall::vector<uint8_t> Pause = {  //size: 464
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,12,6,17,32,43,74,96,129,68,0,0,1,93,73,
+  68,65,84,56,203,205,146,75,110,20,65,12,134,191,42,187,153,238,40,234,213,216,138,56,64,114,12,136,224,72,72,112,
+  164,40,202,25,0,133,59,68,16,16,172,103,150,72,48,105,250,97,22,253,200,0,19,177,4,47,74,165,175,92,191,126,
+  63,224,95,71,218,187,159,254,61,59,45,159,34,226,35,16,58,191,173,215,107,206,159,61,121,191,47,250,246,245,245,89,
+  12,67,62,127,254,244,102,159,95,191,121,119,182,217,110,19,16,121,134,102,134,170,198,229,197,21,151,23,87,168,234,224,
+  102,172,15,112,115,95,76,45,14,220,29,17,161,239,123,62,125,249,128,136,96,238,116,93,183,240,219,207,55,35,55,251,
+  83,192,204,16,17,118,119,223,41,180,64,84,48,115,186,182,69,68,184,107,118,136,40,162,130,63,36,208,180,59,154,166,
+  65,84,16,17,220,215,64,26,118,205,55,218,246,7,229,170,90,156,205,113,223,3,31,29,52,109,67,34,33,121,116,48,
+  246,70,136,8,202,85,137,228,95,29,44,2,110,62,38,14,3,67,12,168,42,102,134,79,165,229,44,148,101,133,170,96,
+  110,15,59,32,160,239,187,169,4,95,184,100,229,81,177,154,154,120,104,10,54,78,225,228,228,49,146,5,45,70,7,36,
+  16,17,170,163,106,226,197,92,66,28,154,66,188,124,245,2,0,201,57,215,245,81,189,42,143,191,254,206,143,235,170,158,
+  5,150,237,218,108,55,167,68,34,165,105,99,35,17,41,72,115,74,26,143,116,191,185,183,252,23,241,19,86,58,109,31,
+  74,23,56,251,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Play = {  //size: 660
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,12,6,17,36,33,206,217,173,94,0,0,2,33,73,
+  68,65,84,56,203,157,147,75,107,19,1,20,133,207,60,50,201,36,77,38,49,153,105,98,19,19,90,73,48,35,186,74,
+  209,212,72,82,108,138,5,193,103,116,39,88,16,193,66,182,221,249,7,92,244,23,72,151,130,47,186,114,163,136,32,46,
+  12,66,77,21,177,88,108,160,17,74,250,208,164,51,77,50,51,185,46,196,166,98,41,177,119,117,238,230,131,115,239,57,
+  64,119,120,28,96,184,93,58,2,192,1,160,9,128,122,5,176,187,151,252,248,216,235,96,48,152,6,224,235,21,192,252,
+  17,138,44,31,25,62,149,250,198,48,76,199,178,204,47,229,15,159,110,182,90,173,207,181,90,77,239,201,66,52,22,147,
+  188,62,169,248,236,233,28,239,145,60,254,106,117,101,178,191,95,25,233,48,244,102,32,52,80,175,213,106,180,47,32,169,
+  170,146,40,58,138,215,11,55,216,72,100,128,157,56,63,193,245,185,221,225,141,245,245,219,162,83,76,58,69,215,187,100,
+  242,88,99,121,185,178,55,96,56,149,146,56,142,41,94,189,114,141,5,1,174,62,55,212,164,202,165,79,167,5,151,83,
+  76,252,168,255,156,52,45,107,117,104,112,168,178,180,180,212,252,7,144,203,102,165,182,217,46,94,186,120,153,221,210,235,
+  48,140,54,12,163,13,187,195,142,120,34,193,229,243,227,14,34,186,64,48,83,137,120,252,125,230,76,166,81,42,149,204,
+  157,223,7,100,5,13,173,14,0,208,116,13,32,2,1,176,241,54,120,61,62,232,164,131,200,98,252,135,252,134,215,19,
+  144,120,142,117,3,104,238,0,20,69,70,245,251,111,127,186,190,5,155,205,142,195,193,48,44,211,196,236,236,3,107,241,
+  235,226,102,44,22,189,163,4,66,111,1,90,187,59,53,101,252,149,62,89,150,201,38,8,0,8,209,240,32,120,158,199,
+  227,39,143,172,133,143,11,70,60,113,244,94,102,36,251,176,211,233,172,22,10,133,22,246,138,175,44,43,16,4,27,117,
+  136,240,242,213,11,42,151,231,45,183,199,243,60,123,118,116,90,16,236,149,92,46,171,237,245,198,46,64,9,64,20,237,
+  52,51,115,223,210,116,109,101,52,59,122,43,32,135,230,143,171,234,198,126,65,234,222,32,32,99,123,187,181,121,110,44,
+  53,125,66,61,57,7,48,155,138,162,80,207,81,38,162,48,0,3,192,26,195,48,214,127,215,146,136,216,131,212,249,23,
+  48,70,193,31,65,224,78,98,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Record = {  //size: 653
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,31,73,68,65,84,56,141,
+  165,146,191,79,83,81,20,199,191,231,222,190,246,181,233,15,250,196,244,17,219,69,23,41,97,0,177,9,152,72,28,28,
+  193,137,85,7,255,2,13,187,131,110,44,46,50,59,43,115,19,226,212,116,168,77,108,9,67,141,44,150,0,173,37,64,
+  109,74,222,123,188,182,247,29,135,214,154,98,49,38,124,147,179,220,156,243,201,61,223,243,37,102,198,117,36,174,53,13,
+  192,55,238,177,64,20,212,128,199,18,88,80,64,18,64,81,2,249,57,230,175,151,123,233,242,10,59,68,15,69,52,250,
+  97,106,109,205,8,77,79,251,68,56,76,238,254,190,247,51,151,235,182,10,133,247,12,188,188,199,108,143,5,148,136,30,
+  197,102,103,179,169,245,117,189,231,186,232,53,155,240,92,23,50,24,132,223,52,225,148,74,170,186,185,249,101,190,219,93,
+  196,96,112,8,40,18,69,3,177,216,247,187,27,27,55,156,131,3,168,118,27,96,238,151,231,1,74,33,152,78,227,60,
+  159,239,214,183,182,94,204,51,191,27,49,81,2,79,110,173,174,70,187,167,167,232,213,235,128,101,1,142,3,216,54,96,
+  89,96,203,130,93,46,35,190,188,172,105,209,232,171,191,76,20,192,3,61,153,212,46,118,119,161,26,13,144,223,15,105,
+  24,32,162,225,15,188,94,15,189,86,11,122,42,101,20,136,140,69,230,230,159,43,16,221,145,74,161,83,46,131,29,7,
+  66,8,176,148,16,145,8,40,18,1,2,1,176,235,66,213,106,240,199,98,42,208,191,78,115,184,130,98,46,116,78,78,
+  88,155,156,132,32,130,16,2,130,8,100,89,64,163,1,175,90,133,119,116,4,65,132,139,90,77,234,192,183,17,15,8,
+  200,183,247,246,58,250,204,12,136,8,52,128,72,41,251,48,33,160,153,38,72,215,97,55,26,149,52,115,103,4,176,192,
+  252,233,172,88,252,204,68,28,202,100,32,164,4,6,32,16,65,24,6,66,75,75,168,103,179,29,184,238,243,171,114,48,
+  165,133,195,229,228,202,202,77,221,52,165,58,62,6,92,23,52,49,1,17,143,163,190,189,221,57,175,84,94,207,41,245,
+  230,202,36,150,136,66,36,196,91,61,145,120,22,74,165,60,95,56,76,78,173,38,156,195,195,31,174,109,63,189,207,156,
+  251,103,148,127,235,35,145,188,13,164,25,72,120,192,78,134,249,108,92,223,149,128,255,213,47,18,206,244,197,113,23,107,
+  46,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Rewind = {  //size: 764
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,12,6,17,39,32,146,243,206,11,0,0,2,137,73,
+  68,65,84,56,203,189,147,203,75,84,97,24,198,159,239,59,183,153,243,157,51,206,120,59,103,108,186,200,232,228,128,102,
+  58,102,74,58,16,65,16,109,211,69,101,33,129,45,170,93,23,108,17,210,133,90,212,34,250,11,2,105,85,102,19,66,
+  132,82,139,194,114,22,225,37,44,138,74,37,203,209,105,200,60,115,142,142,206,215,102,144,2,187,64,208,11,239,230,247,
+  240,91,189,239,3,252,135,161,0,220,191,224,30,241,15,34,203,173,3,192,206,113,5,128,55,151,67,88,67,36,30,143,
+  238,82,20,197,87,84,84,92,29,137,212,118,204,37,147,143,100,89,6,33,196,160,148,110,240,251,253,129,186,186,72,199,
+  236,108,226,9,249,209,12,133,66,50,231,220,107,89,214,250,96,121,105,155,198,180,14,0,153,145,225,177,45,182,109,123,
+  40,165,82,56,188,121,191,166,107,199,8,33,43,175,199,223,148,83,0,136,70,163,180,185,57,234,181,109,39,236,55,205,
+  3,117,219,106,251,182,215,215,183,223,143,245,185,37,89,146,40,165,222,138,138,242,29,13,13,245,177,202,234,170,163,189,
+  119,239,185,4,65,16,12,191,9,177,181,165,213,55,58,54,186,33,176,49,16,138,238,108,58,83,224,203,47,237,234,58,
+  239,243,120,60,132,103,57,152,202,196,166,230,198,27,76,211,106,46,95,186,194,124,249,249,132,103,57,84,85,133,40,200,
+  16,13,191,225,42,44,246,157,211,116,125,215,169,147,167,89,158,199,43,58,75,54,172,133,5,168,42,131,174,107,66,87,
+  215,133,198,194,130,2,209,89,114,86,57,211,24,10,124,110,80,211,48,225,86,93,94,213,237,146,102,18,51,220,118,210,
+  176,172,111,248,242,53,9,66,0,93,215,179,51,137,207,220,89,180,145,78,91,72,229,56,83,85,24,166,9,106,154,230,
+  76,184,162,170,197,178,210,157,61,119,110,191,127,216,255,96,222,237,98,40,49,3,160,84,128,172,72,188,183,183,231,109,
+  119,247,77,91,150,148,85,238,102,110,24,134,1,33,22,139,241,88,44,102,119,118,158,29,46,49,215,221,26,123,57,42,
+  189,26,31,171,20,37,145,23,23,27,210,80,252,121,166,166,58,18,89,90,201,188,27,124,246,180,105,110,118,150,6,131,
+  101,226,208,208,224,114,89,48,116,253,167,51,198,227,113,2,112,150,78,219,133,47,70,226,23,53,198,90,178,28,153,173,
+  149,181,65,14,80,107,121,65,73,37,146,199,83,169,228,137,169,169,143,124,207,238,189,165,100,173,23,156,156,156,164,224,
+  60,239,195,244,196,166,199,253,253,87,15,31,58,178,143,3,243,4,156,241,44,212,233,79,19,102,255,192,192,181,182,182,
+  246,131,228,119,37,112,22,23,101,2,238,5,144,81,20,87,10,0,108,219,17,0,104,0,87,255,186,77,156,115,101,13,
+  70,56,231,250,63,87,245,59,186,53,233,69,8,225,63,138,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Skip = {  //size: 782
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,12,6,17,39,8,167,70,102,241,0,0,2,155,73,
+  68,65,84,56,203,189,147,59,76,19,113,28,199,191,255,123,244,238,218,43,82,192,222,181,64,33,62,18,104,4,68,211,
+  68,133,193,129,81,6,28,28,25,157,92,140,38,74,84,210,85,33,44,190,66,28,12,218,184,24,37,6,69,67,116,84,
+  112,144,71,124,64,76,120,212,96,160,143,163,244,122,215,235,113,45,127,23,32,81,97,51,126,167,95,62,223,228,51,125,
+  127,192,63,72,25,0,102,23,46,237,193,127,11,11,160,2,128,12,192,3,160,8,160,180,213,201,0,246,1,32,91,156,
+  238,42,240,120,220,222,246,246,182,30,199,113,82,133,66,65,230,121,158,147,36,201,113,9,2,215,214,118,170,183,88,44,
+  102,1,106,10,130,80,178,237,141,210,95,130,80,109,200,171,6,149,135,138,226,239,246,251,247,219,186,158,91,100,24,166,
+  92,150,101,82,93,19,28,84,20,127,119,69,165,207,109,26,249,21,85,85,139,85,85,85,142,166,105,59,34,182,49,28,
+  246,122,60,238,139,207,159,13,139,147,51,83,205,101,178,124,214,227,145,150,114,57,35,93,93,27,60,255,98,120,68,154,
+  155,251,22,22,68,225,28,207,242,122,42,173,25,45,45,45,118,125,125,221,70,60,30,167,164,171,171,43,224,18,184,133,
+  216,227,39,34,97,8,50,107,107,180,231,218,85,211,52,140,41,128,156,120,52,20,227,9,67,160,235,58,141,70,123,51,
+  90,102,109,49,153,76,221,92,142,47,127,111,58,210,244,131,11,168,42,10,142,5,0,48,13,3,130,36,144,123,119,238,
+  203,105,77,59,25,141,222,96,182,57,199,179,164,191,111,160,34,171,175,151,245,245,223,122,112,248,224,129,119,44,235,186,
+  192,41,170,138,180,150,0,33,64,38,171,65,20,37,128,82,36,146,171,212,235,245,110,18,2,102,45,171,65,20,4,208,
+  77,138,68,50,65,221,146,200,151,74,78,121,69,185,31,156,162,40,48,11,58,24,134,69,80,173,129,97,26,136,197,134,
+  172,68,50,181,228,18,248,67,219,220,178,242,24,123,251,70,159,253,58,187,106,154,249,187,141,13,77,49,2,178,206,169,
+  170,138,68,234,39,156,162,131,209,209,17,123,97,105,62,239,243,85,94,57,222,90,255,114,230,243,212,130,83,116,248,233,
+  153,79,214,196,248,7,91,207,153,131,199,142,70,6,40,161,122,231,153,206,2,0,144,137,241,241,192,235,177,87,139,181,
+  181,213,196,231,171,188,237,243,87,222,241,112,178,77,128,205,233,47,147,243,12,1,111,152,230,211,214,230,200,117,183,91,
+  74,3,196,140,68,34,59,163,226,148,128,10,150,97,222,135,27,194,151,130,129,186,85,194,32,79,65,76,2,148,173,142,
+  173,124,60,221,209,113,185,62,88,183,4,66,178,161,80,104,243,207,33,17,203,178,2,91,103,30,128,33,73,98,9,0,
+  108,187,224,3,192,83,144,117,81,16,54,246,124,6,74,169,151,82,74,118,225,2,254,71,126,1,54,156,22,72,126,132,
+  128,72,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Stop = {  //size: 429
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,12,6,17,40,46,242,211,255,195,0,0,1,58,73,
+  68,65,84,56,203,205,146,95,74,3,49,16,135,191,236,164,175,22,10,102,60,128,88,240,20,34,30,66,196,75,8,226,
+  137,68,188,130,136,215,16,84,4,69,41,138,62,172,44,180,187,221,221,196,135,253,171,173,138,79,26,8,204,100,50,95,
+  230,55,25,248,235,101,122,246,198,47,115,111,128,96,27,111,52,26,177,189,179,117,105,140,145,159,50,207,207,46,198,113,
+  28,155,15,0,117,142,193,96,96,78,142,79,191,77,222,219,223,245,170,74,28,199,0,68,77,96,213,41,34,213,227,183,
+  119,87,60,78,238,121,121,125,230,45,137,73,179,20,239,61,0,214,90,156,106,11,108,1,170,29,64,196,214,91,144,72,
+  48,192,60,207,234,152,160,206,45,2,156,186,22,16,69,2,6,124,8,20,101,65,154,165,204,102,211,10,96,5,183,12,
+  176,214,171,32,4,79,89,150,20,197,156,44,75,153,165,83,166,105,5,176,34,104,79,130,237,122,224,152,60,61,0,80,
+  150,37,222,123,202,162,226,251,26,216,200,115,234,22,1,234,58,9,121,158,19,8,24,32,16,240,222,183,77,20,43,168,
+  91,82,129,246,122,48,94,223,252,242,27,165,147,16,62,85,160,136,72,56,60,58,248,118,14,36,138,34,231,58,64,59,
+  202,73,146,212,163,28,170,227,80,217,193,84,66,42,191,185,17,24,174,12,175,249,23,235,29,41,108,117,184,218,69,135,
+  206,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Place {
+const nall::vector<uint8_t> Bookmarks = {  //size: 753
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,10,6,3,42,40,185,100,186,202,0,0,0,53,116,
+  69,88,116,67,111,109,109,101,110,116,0,40,99,41,32,50,48,48,52,32,74,97,107,117,98,32,83,116,101,105,110,101,
+  114,10,10,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,144,217,139,111,0,0,2,61,
+  73,68,65,84,56,203,149,147,77,79,83,65,20,134,159,153,222,185,247,246,131,182,96,63,80,106,20,98,76,140,49,38,
+  198,104,252,13,154,96,220,152,16,226,194,133,127,193,164,11,8,44,76,252,31,132,61,145,159,96,226,194,132,13,10,6,
+  48,86,180,84,161,32,45,183,237,237,237,244,142,139,98,161,194,66,79,50,155,153,57,207,188,239,57,103,4,192,84,113,
+  97,14,152,225,223,99,126,241,213,244,44,128,152,42,46,204,141,72,61,243,226,249,67,60,95,227,181,58,4,90,83,243,
+  218,0,72,41,0,16,199,153,13,63,160,94,89,230,217,189,34,169,236,228,75,11,152,121,252,232,65,24,134,70,118,131,
+  14,217,84,140,141,242,62,141,202,1,67,229,61,84,171,77,168,34,120,163,25,90,153,20,249,145,56,117,32,149,157,164,
+  182,183,244,218,2,112,28,71,26,224,224,208,35,26,115,161,222,36,93,170,80,187,50,74,144,136,97,249,109,82,165,10,
+  93,21,129,145,56,49,39,232,123,177,0,92,87,1,134,150,223,59,144,95,42,212,199,178,68,130,14,185,213,45,140,16,
+  252,186,86,32,249,109,23,198,243,68,237,206,32,192,118,108,194,110,216,223,20,135,30,237,66,142,204,90,137,122,33,71,
+  226,199,62,218,181,81,77,31,128,119,159,124,58,106,131,143,43,49,36,128,173,20,230,84,161,8,13,70,8,164,238,98,
+  34,17,16,2,231,168,137,142,218,103,218,33,1,132,16,128,57,33,196,93,84,195,199,31,30,234,189,110,43,146,219,63,
+  169,95,206,247,248,36,6,45,156,68,143,16,142,143,146,220,44,83,187,122,17,29,117,64,136,129,91,110,73,241,100,17,
+  110,175,55,123,0,131,1,3,185,11,41,0,76,54,77,189,173,73,126,223,69,53,90,116,109,69,245,230,196,185,19,213,
+  83,96,64,41,139,116,58,129,223,209,228,135,227,36,163,14,92,31,235,235,74,13,104,252,11,224,53,90,188,89,126,139,
+  37,13,161,233,45,173,13,66,10,108,21,65,8,129,0,186,221,144,175,229,106,152,111,30,201,207,235,235,0,79,45,128,
+  15,171,91,220,191,17,239,143,237,121,33,165,164,41,210,108,236,7,114,123,252,22,119,143,197,88,0,135,94,131,137,220,
+  16,133,194,165,51,137,226,84,1,215,54,119,206,173,193,252,251,205,234,140,110,53,195,157,106,32,93,87,161,59,122,192,
+  240,31,132,238,134,76,222,201,152,165,149,170,56,221,55,57,85,92,152,253,207,239,204,226,171,105,1,240,27,115,187,219,
+  217,106,190,108,255,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Desktop = {  //size: 722
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,2,23,0,42,10,182,151,62,76,0,0,2,95,73,
+  68,65,84,56,203,165,147,93,76,207,81,24,199,63,231,247,79,180,48,179,24,27,235,154,102,46,136,205,172,197,52,46,
+  112,161,194,13,174,72,102,140,214,100,105,11,115,229,109,35,204,186,112,41,11,139,27,47,155,41,169,141,44,111,49,47,
+  75,72,249,139,213,164,214,239,229,156,231,113,81,254,254,255,113,97,115,110,206,246,61,223,231,57,159,243,221,115,204,162,
+  101,197,84,87,149,51,240,125,8,128,246,246,118,94,63,171,227,111,107,199,238,179,188,121,215,3,64,247,135,78,154,239,
+  63,192,44,204,91,87,239,197,98,69,201,198,135,119,235,99,207,59,26,5,64,84,177,97,200,246,178,218,67,198,152,170,
+  100,159,170,222,51,139,150,21,235,191,16,244,13,206,166,186,106,255,31,4,105,0,111,222,245,208,210,246,130,129,175,189,
+  184,225,247,156,58,126,44,81,248,139,64,129,178,202,163,108,43,41,165,225,102,43,3,95,123,1,70,27,0,163,66,24,
+  167,114,95,49,95,250,186,82,110,247,253,17,172,141,216,185,117,49,53,231,207,49,61,123,126,226,204,251,133,67,24,167,
+  98,239,154,148,66,21,193,218,8,99,60,98,177,52,198,165,167,179,107,251,18,250,62,60,77,120,98,51,179,231,204,253,
+  220,211,153,115,160,124,19,198,132,191,209,69,80,21,84,65,85,176,206,34,206,33,78,200,156,48,204,203,87,189,136,198,
+  14,123,143,154,174,108,152,58,49,78,86,214,20,0,156,115,88,107,113,206,34,162,168,42,65,24,160,162,88,107,241,131,
+  17,188,152,146,53,169,151,182,198,203,7,19,25,8,58,214,192,162,40,6,16,129,48,244,113,206,33,170,132,81,136,136,
+  36,191,210,75,203,205,47,186,248,237,71,72,198,248,76,6,198,8,140,49,40,6,107,35,48,6,85,37,138,2,212,9,
+  34,14,128,254,161,25,228,230,23,174,242,140,49,27,243,150,175,165,116,207,73,84,21,17,71,24,133,248,254,8,65,224,
+  19,4,62,97,20,224,156,224,156,195,57,199,195,39,131,108,44,94,143,49,222,117,15,160,171,59,206,226,165,43,57,81,
+  115,11,39,163,70,113,14,17,193,217,209,93,196,225,196,113,237,198,123,118,148,150,209,252,248,99,234,28,116,117,199,153,
+  183,160,128,51,181,119,88,189,98,22,126,56,12,40,145,181,140,197,67,75,219,32,155,183,148,112,225,210,237,68,8,105,
+  201,137,116,117,199,153,144,145,73,221,181,183,76,155,252,41,101,38,134,252,41,172,40,40,162,225,102,107,138,110,114,243,
+  11,59,140,241,114,146,69,17,119,228,81,211,213,211,64,255,152,52,46,55,191,168,226,111,159,137,255,93,63,1,185,254,
+  98,48,219,255,91,223,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Home = {  //size: 679
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,2,36,73,68,65,84,56,141,165,146,77,107,83,65,20,134,159,51,115,
+  107,211,196,170,88,48,196,66,193,69,237,34,27,161,226,162,123,201,170,139,32,116,235,47,208,189,208,63,224,198,93,221,
+  250,55,68,20,226,78,40,24,237,198,34,149,86,41,77,73,168,54,183,105,155,59,51,119,142,139,124,180,129,226,198,179,
+  25,230,204,57,207,249,120,71,106,181,26,255,99,201,242,242,242,27,85,93,189,226,237,76,85,159,52,155,205,205,127,2,
+  98,140,245,122,189,126,51,198,72,140,17,85,5,224,248,248,120,174,209,104,108,84,171,213,231,163,96,239,61,198,152,252,
+  224,224,224,115,154,166,57,64,226,189,215,16,2,157,78,135,60,207,199,16,99,12,149,74,165,10,188,27,65,1,122,189,
+  94,146,231,249,171,52,77,215,47,1,60,33,4,98,140,19,144,114,185,92,44,20,10,20,139,69,172,181,24,99,104,181,
+  90,180,219,237,197,241,8,33,132,24,66,206,198,71,199,105,118,81,9,4,0,165,15,244,199,94,85,3,172,172,177,176,
+  178,102,173,249,153,120,239,213,57,71,47,83,94,62,123,140,200,48,240,242,166,116,226,6,128,11,202,250,235,15,11,137,
+  115,46,134,16,0,197,26,248,178,219,189,220,192,232,152,220,188,21,238,149,75,32,170,73,8,1,239,61,34,130,53,130,
+  181,23,41,215,11,9,143,22,111,147,24,225,211,247,223,164,231,126,160,70,136,104,84,4,209,196,123,31,189,247,160,138,
+  17,97,202,10,198,8,247,43,215,89,186,59,139,53,3,96,237,65,153,111,251,41,95,247,186,184,160,152,129,127,0,8,
+  97,208,65,223,229,220,185,49,205,210,252,44,51,215,44,103,46,31,143,175,170,204,207,205,112,171,52,197,214,94,151,204,
+  197,49,64,67,200,17,73,232,164,25,219,251,39,108,238,252,97,245,97,101,84,101,176,11,129,204,71,222,54,15,177,70,
+  88,152,155,25,0,66,8,209,57,7,106,217,105,157,112,212,203,80,224,168,231,198,138,140,150,233,66,36,61,247,88,35,
+  28,118,135,35,136,72,140,49,34,34,100,62,82,156,78,0,232,251,124,152,120,65,201,163,82,154,78,16,17,206,92,0,
+  200,19,85,101,247,40,34,2,91,63,90,227,224,237,189,195,73,9,135,237,140,190,245,233,105,31,129,205,36,78,149,26,
+  239,119,103,159,130,178,255,171,125,133,234,87,155,8,109,17,243,226,47,238,171,54,255,32,151,176,79,0,0,0,0,73,
+  69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Server = {  //size: 642
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,96,0,96,0,96,101,20,102,74,0,0,0,9,112,72,89,115,0,0,11,18,0,
+  0,11,18,1,210,221,126,252,0,0,0,7,116,73,77,69,7,213,5,24,13,41,13,225,69,167,57,0,0,0,62,116,
+  69,88,116,67,111,109,109,101,110,116,0,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,
+  10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83,116,101,105,110,101,
+  114,39,51,239,88,0,0,1,197,73,68,65,84,56,203,141,146,189,138,34,81,16,133,191,235,54,218,6,6,98,172,160,
+  145,8,3,254,177,6,194,226,62,192,198,70,3,247,5,140,134,13,124,2,31,192,23,16,246,17,54,22,100,2,3,55,
+  145,53,53,145,246,47,16,49,209,139,77,119,215,6,59,221,171,163,50,123,224,66,113,239,61,167,234,84,149,226,26,79,
+  90,235,223,124,128,193,96,240,25,248,197,29,178,44,151,75,241,125,95,124,223,23,207,243,196,117,93,113,93,87,140,49,
+  98,140,145,241,120,44,90,107,1,190,94,177,181,214,114,62,159,175,200,247,4,78,167,147,236,247,251,80,4,235,82,68,
+  41,197,104,52,250,200,1,173,86,43,138,173,247,143,167,211,137,114,185,252,144,60,157,78,49,198,252,19,208,90,127,1,
+  190,135,23,201,100,18,99,12,34,130,136,16,4,1,34,130,239,251,127,9,150,69,16,4,161,237,159,86,183,219,5,160,
+  215,235,1,112,60,30,153,207,231,0,136,72,148,41,140,67,33,128,78,167,51,179,66,98,136,70,163,17,125,10,43,80,
+  74,33,34,24,99,136,199,227,40,165,0,232,247,251,79,214,96,48,120,5,94,181,214,98,89,22,195,225,144,213,106,69,
+  62,159,167,84,42,177,221,110,241,125,63,170,64,68,200,102,179,225,62,124,139,189,111,146,227,56,20,139,69,102,179,25,
+  74,41,226,241,56,133,66,33,58,151,22,110,166,32,34,20,10,5,38,147,9,237,118,27,219,182,113,93,151,197,98,17,
+  85,96,219,54,135,195,225,241,24,235,245,58,213,106,53,234,124,46,151,187,219,204,55,124,186,177,224,121,30,65,16,68,
+  222,29,199,193,243,60,28,199,1,136,70,248,134,68,236,209,194,236,118,59,0,54,155,13,137,68,130,245,122,77,44,22,
+  139,54,54,220,187,43,11,135,195,129,76,38,3,64,165,82,33,149,74,209,108,54,73,165,82,212,106,53,210,233,244,77,
+  162,75,129,231,151,151,151,31,252,63,158,1,254,0,124,80,17,254,250,115,5,147,0,0,0,0,73,69,78,68,174,66,
+  96,130,
+};
+const nall::vector<uint8_t> Share = {  //size: 697
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
+  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
+  97,112,101,46,111,114,103,155,238,60,26,0,0,2,54,73,68,65,84,56,141,125,147,207,75,212,65,20,192,63,111,230,
+  107,172,38,90,80,45,66,132,68,157,194,91,93,130,46,65,7,15,29,36,242,210,41,130,14,65,157,69,188,117,144,16,
+  58,85,215,46,5,37,228,31,32,84,22,221,52,163,31,134,66,96,65,184,225,82,173,185,201,126,191,243,102,94,135,93,
+  55,53,233,93,30,243,230,205,231,253,28,25,29,29,189,111,102,231,249,87,54,204,236,194,248,248,248,236,46,119,109,145,
+  145,145,145,218,208,208,80,111,74,137,148,18,102,6,64,173,86,99,102,102,102,54,165,116,99,211,57,132,128,115,46,174,
+  172,172,204,79,78,78,70,128,44,132,96,170,74,181,90,37,198,216,134,56,231,232,235,235,59,1,76,111,66,1,234,245,
+  122,22,99,188,13,140,109,1,4,84,149,148,210,54,72,185,92,238,42,149,74,116,117,117,225,189,199,57,71,165,82,97,
+  117,117,245,248,38,48,83,213,164,26,185,251,162,224,119,254,55,18,8,0,70,3,104,180,173,102,14,56,61,124,234,234,
+  195,97,239,221,151,44,132,96,69,81,80,207,141,91,215,207,33,210,114,220,218,41,219,118,2,160,80,99,236,222,211,35,
+  89,81,20,73,85,1,35,115,240,102,121,173,25,95,118,233,120,75,59,47,28,45,239,5,49,203,84,149,16,2,34,130,
+  115,130,247,187,188,220,33,170,9,51,16,196,178,16,66,10,33,128,25,78,132,142,255,0,52,26,121,72,196,100,248,102,
+  138,77,128,106,51,131,70,17,145,86,201,26,141,104,70,74,70,50,35,104,83,3,100,94,104,20,218,6,152,106,68,36,
+  163,250,43,167,242,163,65,174,233,191,37,100,94,168,174,229,77,128,170,166,162,40,192,60,159,42,235,124,175,231,237,9,
+  104,99,125,219,56,178,206,110,64,240,78,248,182,214,42,65,68,82,140,17,193,104,228,129,206,142,102,15,150,95,62,96,
+  189,178,216,126,159,98,162,251,80,63,199,206,94,193,249,140,141,70,142,64,204,82,74,242,122,225,51,165,159,57,243,207,
+  22,219,209,194,215,247,76,76,76,48,55,55,215,222,204,39,83,83,124,120,254,24,201,246,16,130,146,113,96,73,222,190,
+  91,88,46,237,235,235,223,89,231,181,203,23,25,24,24,64,90,11,97,102,44,46,46,113,115,226,14,61,189,251,241,94,
+  180,167,179,227,149,12,14,14,30,78,41,93,2,14,110,5,152,217,25,51,59,185,213,230,156,251,40,34,211,214,252,93,
+  234,189,127,244,7,187,71,87,220,223,105,176,149,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+namespace Prompt {
+const nall::vector<uint8_t> Error = {  //size: 653
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,11,10,9,50,14,147,83,180,176,0,0,2,26,73,
+  68,65,84,56,203,157,147,189,79,147,81,20,198,127,247,109,11,229,163,190,160,196,26,77,160,104,155,2,182,11,196,148,
+  68,135,142,186,50,105,210,129,17,254,34,59,50,116,102,35,178,144,64,88,68,7,48,146,22,154,104,99,248,176,36,164,
+  45,77,91,40,246,61,199,225,125,75,16,137,26,79,114,115,147,155,243,252,158,228,57,247,24,110,84,14,82,192,60,144,
+  6,198,189,231,18,176,14,44,101,96,235,122,191,185,33,126,59,28,10,45,76,38,147,4,195,97,124,3,3,168,42,157,
+  179,51,206,203,101,138,133,2,213,102,51,155,129,197,223,0,57,120,55,157,76,190,28,153,153,225,114,123,27,231,244,20,
+  28,7,85,69,12,88,67,119,241,79,77,82,221,221,229,83,177,184,154,129,87,0,190,174,243,116,50,249,250,94,44,70,
+  123,115,19,109,52,64,4,85,65,69,65,4,105,182,232,28,124,163,63,22,195,182,172,232,139,106,245,193,50,172,152,28,
+  164,134,67,161,247,207,230,230,184,88,91,67,157,14,170,138,42,168,122,98,5,197,133,169,49,4,83,41,62,111,108,80,
+  107,181,102,45,96,126,50,145,224,114,103,7,117,28,16,80,1,21,113,15,234,129,20,84,193,113,184,200,231,121,60,22,
+  1,152,247,3,233,96,248,62,63,62,124,68,69,25,43,127,231,79,181,111,223,65,170,53,2,15,31,1,164,253,192,184,
+  213,63,136,138,27,216,223,74,68,81,237,96,122,2,0,227,22,112,149,246,191,0,240,250,84,5,0,63,80,234,212,207,
+  38,20,131,170,195,151,145,17,183,65,196,189,81,55,60,55,85,68,221,32,181,217,2,40,89,192,250,249,201,9,198,30,
+  186,34,255,34,238,58,138,39,86,193,26,28,164,93,171,1,172,91,192,210,254,222,30,129,120,220,37,203,117,145,55,58,
+  237,78,67,80,99,240,71,34,148,14,15,1,150,172,12,108,213,90,173,108,165,144,167,103,234,169,11,185,230,138,119,171,
+  40,24,67,79,52,70,227,224,128,122,187,157,205,192,150,15,96,25,86,158,87,42,179,182,207,23,29,72,36,112,26,13,
+  228,162,125,245,19,21,48,118,136,222,248,4,141,114,153,194,241,241,106,6,222,220,186,76,118,95,223,194,147,209,81,2,
+  182,141,233,237,69,69,112,154,77,58,245,58,95,143,142,186,206,139,183,110,227,255,172,243,79,194,71,102,30,185,88,118,
+  119,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Information = {  //size: 863
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,3,2,6,32,50,58,80,156,247,0,0,0,53,116,
+  69,88,116,67,111,109,109,101,110,116,0,40,99,41,32,50,48,48,52,32,74,97,107,117,98,32,83,116,101,105,110,101,
+  114,10,10,67,114,101,97,116,101,100,32,119,105,116,104,32,84,104,101,32,71,73,77,80,144,217,139,111,0,0,2,171,
+  73,68,65,84,56,203,149,146,75,72,84,113,20,198,191,255,220,59,227,189,58,215,71,38,89,106,102,82,62,8,69,34,
+  19,196,161,6,37,212,164,162,144,108,98,202,112,97,180,8,19,178,69,203,16,49,170,69,143,77,162,139,136,160,2,3,
+  3,37,200,16,73,116,38,74,74,205,138,52,149,76,157,113,116,156,185,119,174,247,245,111,229,160,53,17,126,171,115,224,
+  251,126,156,115,56,4,17,84,92,217,212,76,41,28,6,37,41,32,132,152,136,49,203,16,250,180,255,101,107,227,159,94,
+  118,125,83,84,222,148,193,48,164,239,98,109,101,90,185,189,0,113,2,15,85,55,224,241,5,83,123,122,63,92,33,38,
+  230,44,165,122,73,127,87,235,215,136,0,150,37,111,239,181,212,111,223,147,185,99,44,16,82,70,103,125,146,18,90,85,
+  89,69,211,163,109,37,249,89,233,233,201,123,91,110,61,118,1,136,95,203,48,107,69,73,213,213,219,245,231,143,150,21,
+  31,204,249,172,168,186,155,183,152,21,0,68,167,32,170,78,13,239,74,104,150,139,230,16,205,115,59,117,38,37,115,114,
+  252,109,39,0,152,194,36,134,61,87,110,47,64,32,164,142,177,44,67,45,102,198,32,132,80,80,24,154,110,40,148,194,
+  232,26,156,236,46,42,204,1,195,114,167,214,114,97,192,170,66,227,5,43,15,89,209,87,85,205,72,52,51,166,11,137,
+  177,156,115,87,178,224,200,223,189,213,1,37,144,58,229,17,253,60,207,33,32,169,92,109,93,131,105,195,13,8,33,68,
+  209,116,72,178,106,50,12,42,38,88,163,224,245,75,46,81,10,49,43,65,73,27,158,150,134,21,69,55,169,170,177,22,
+  225,1,136,97,64,12,207,74,94,95,32,70,148,85,203,138,164,250,51,146,99,49,250,195,247,94,84,116,58,183,36,41,
+  238,111,139,30,193,106,73,240,250,131,136,19,120,181,163,173,89,220,176,130,153,209,6,122,222,12,131,143,98,115,39,126,
+  45,139,0,240,211,39,234,31,39,188,254,222,225,153,133,37,81,54,87,236,79,171,24,114,141,193,202,97,226,175,27,108,
+  19,66,199,59,187,250,229,153,233,249,236,180,36,235,161,107,237,3,55,207,28,206,186,228,254,50,31,98,88,146,232,180,
+  103,85,251,23,151,114,220,131,35,170,192,250,74,195,171,175,255,131,99,167,27,171,100,42,60,47,43,45,178,20,30,200,
+  70,52,207,65,213,117,248,150,69,12,185,198,241,206,53,162,197,113,210,229,39,237,55,30,68,4,0,64,109,93,67,98,
+  136,110,113,5,100,164,59,78,36,49,132,0,44,11,116,117,207,5,25,205,191,175,163,237,206,20,254,167,154,26,187,213,
+  233,180,81,207,98,31,21,101,23,253,62,249,140,58,157,54,26,201,203,254,131,161,2,192,253,187,15,145,157,155,141,233,
+  169,105,108,90,78,167,141,206,204,190,50,2,210,16,253,52,250,194,216,236,4,0,80,85,125,242,122,141,32,196,31,89,
+  88,88,120,157,151,23,251,40,146,233,55,86,76,54,94,124,138,29,4,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+const nall::vector<uint8_t> Question = {  //size: 932
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
+  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,3,54,73,68,65,84,56,141,
+  109,147,91,108,84,101,20,133,191,243,255,231,90,81,26,192,134,78,161,244,54,237,128,78,169,83,130,5,77,83,46,70,
+  45,250,162,162,134,196,132,152,24,19,43,6,73,76,39,65,140,105,162,173,23,36,241,65,13,137,17,210,248,224,131,9,
+  160,71,35,130,142,213,160,5,105,25,90,219,130,99,167,98,157,161,45,80,123,153,105,167,118,206,239,67,75,3,193,157,
+  236,151,157,189,87,214,94,89,75,83,74,113,99,21,111,107,9,0,207,0,117,64,112,126,220,5,68,128,214,184,27,238,
+  187,113,95,187,14,80,188,173,69,0,141,139,114,204,87,119,62,186,206,190,39,224,19,149,229,249,0,156,191,152,164,243,
+  66,194,59,116,236,215,204,100,122,166,9,120,59,238,134,189,5,128,249,227,182,251,170,138,66,45,47,61,236,156,138,14,
+  112,162,61,70,79,255,48,0,107,74,242,120,160,198,207,198,181,171,8,191,255,245,212,79,157,3,29,64,109,220,13,123,
+  250,60,147,198,77,235,74,67,111,238,122,200,217,179,255,75,174,141,165,217,251,220,102,42,253,249,164,167,102,248,161,35,
+  78,171,219,201,241,95,98,188,187,231,17,231,149,3,110,245,143,29,241,70,160,153,162,250,230,64,112,251,123,169,191,135,
+  199,212,211,225,79,85,81,125,179,122,177,229,136,202,102,61,213,211,63,164,70,199,211,74,41,165,78,158,142,169,154,157,
+  31,169,134,183,190,80,137,145,113,21,220,190,63,93,84,223,28,144,185,254,173,47,191,240,228,134,77,163,227,105,237,208,
+  177,179,0,24,134,206,103,199,207,115,216,61,71,123,247,95,60,190,229,110,86,46,207,229,243,239,123,25,25,77,81,186,
+  98,9,37,43,150,202,83,209,63,39,4,80,87,85,225,211,78,180,199,0,176,76,157,75,151,199,72,94,77,97,91,6,
+  85,21,62,0,174,254,147,198,48,116,114,28,139,206,223,135,8,173,46,16,82,136,58,29,8,6,203,150,19,189,152,196,
+  48,36,166,105,96,89,6,150,105,16,10,20,176,123,199,70,148,82,28,60,218,129,99,91,8,161,145,184,50,201,234,226,
+  60,60,165,130,215,69,68,8,13,67,215,49,141,185,182,76,131,189,207,214,98,234,146,195,95,69,233,187,52,74,142,99,
+  34,132,192,177,76,52,52,52,64,0,93,93,177,203,132,2,5,24,134,156,99,97,232,44,190,221,102,32,57,198,153,222,
+  4,109,209,65,28,219,196,177,45,114,28,147,242,194,101,196,6,175,33,164,232,18,64,228,220,133,132,183,101,125,25,186,
+  46,209,117,137,97,232,104,66,240,115,247,32,167,123,147,72,41,177,109,99,1,164,210,159,71,247,31,67,94,54,235,69,
+  4,208,250,241,145,51,153,13,107,87,177,254,174,149,232,82,162,75,65,238,34,135,221,79,221,75,195,99,213,228,56,38,
+  150,101,98,219,38,107,138,239,164,204,151,203,129,214,72,70,41,213,42,226,110,184,111,34,149,105,218,247,193,55,83,175,
+  63,191,153,80,192,135,148,2,41,181,5,191,27,243,186,4,10,151,240,68,109,41,175,125,248,237,244,68,42,211,20,119,
+  195,125,55,89,121,107,141,191,250,141,134,7,237,104,108,152,104,108,152,196,149,73,108,123,238,231,202,210,101,148,228,47,
+  166,233,224,201,105,183,173,231,236,204,191,179,181,113,55,236,221,18,166,59,110,179,247,237,218,113,191,93,85,225,211,202,
+  11,151,162,105,26,241,196,40,191,245,143,120,239,124,242,93,102,124,114,186,41,235,121,55,135,233,255,226,44,165,168,83,
+  74,5,1,164,148,93,179,179,217,136,82,234,150,56,255,7,102,247,76,148,162,219,138,97,0,0,0,0,73,69,78,68,
+  174,66,96,130,
+};
+const nall::vector<uint8_t> Warning = {  //size: 603
+  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
+  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
+  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,214,2,16,14,39,45,183,238,146,119,0,0,1,232,73,
+  68,65,84,56,203,149,147,193,74,27,97,20,133,191,59,153,206,52,137,51,131,16,153,74,237,70,67,196,32,134,65,74,
+  154,48,4,130,72,99,131,116,23,186,16,34,93,116,231,48,27,241,9,92,27,116,101,192,66,192,69,73,86,5,173,175,
+  80,8,62,128,80,92,4,10,133,22,92,212,64,73,49,127,55,214,18,200,180,246,192,189,139,203,225,59,103,115,133,8,
+  133,240,200,128,183,0,3,120,221,128,47,227,124,122,20,192,132,163,121,120,14,112,1,71,64,117,156,79,139,72,247,82,
+  176,90,88,88,208,158,164,82,90,10,86,67,240,238,13,48,97,47,13,122,98,119,151,76,179,73,26,116,19,246,238,5,
+  8,161,56,5,197,108,185,44,122,62,143,158,207,147,45,151,101,10,138,33,20,249,151,118,160,123,2,195,94,167,163,60,
+  207,83,158,231,169,94,167,163,78,96,184,3,221,191,54,8,97,197,133,165,140,239,139,225,251,56,142,131,227,56,24,190,
+  79,198,247,197,133,165,16,86,34,1,38,236,103,193,72,4,1,162,105,88,150,133,101,89,136,166,145,8,2,178,96,152,
+  176,63,22,16,194,250,52,164,231,10,5,140,82,9,17,193,182,109,108,219,70,68,48,74,37,230,10,5,166,33,29,194,
+  250,8,32,4,49,160,177,120,155,174,233,58,34,242,167,129,8,154,174,147,8,2,22,193,48,160,17,130,192,237,10,225,
+  213,44,180,94,44,47,27,147,103,103,16,139,161,148,162,223,239,3,144,76,38,17,17,184,185,225,106,109,141,15,231,231,
+  131,75,168,55,224,157,132,16,123,12,87,79,193,154,63,62,230,97,181,138,82,10,165,20,181,90,13,128,118,187,141,136,
+  32,34,252,56,61,229,98,99,131,46,124,255,12,147,154,192,102,28,204,153,92,142,120,165,114,103,20,17,92,215,197,117,
+  221,145,91,188,82,97,38,151,35,14,166,192,166,108,195,215,151,144,154,61,56,96,162,94,191,75,255,61,192,8,64,68,
+  184,110,181,184,220,218,226,61,124,147,67,24,62,3,121,192,255,233,39,240,17,148,254,9,14,7,240,102,34,226,47,162,
+  116,13,195,30,52,127,1,151,222,125,8,28,180,225,19,0,0,0,0,73,69,78,68,174,66,96,130,
+};
+}
+}
diff --git a/hiro/resource/resource.hpp b/hiro/resource/resource.hpp
new file mode 100644
index 00000000..5597d3dd
--- /dev/null
+++ b/hiro/resource/resource.hpp
@@ -0,0 +1,102 @@
+namespace Icon {
+namespace Action {
+extern const nall::vector<uint8_t> Add;
+extern const nall::vector<uint8_t> Attach;
+extern const nall::vector<uint8_t> Bookmark;
+extern const nall::vector<uint8_t> FullScreen;
+extern const nall::vector<uint8_t> Mute;
+extern const nall::vector<uint8_t> New;
+extern const nall::vector<uint8_t> Open;
+extern const nall::vector<uint8_t> Properties;
+extern const nall::vector<uint8_t> Quit;
+extern const nall::vector<uint8_t> Refresh;
+extern const nall::vector<uint8_t> Remove;
+extern const nall::vector<uint8_t> Save;
+extern const nall::vector<uint8_t> Search;
+extern const nall::vector<uint8_t> Settings;
+extern const nall::vector<uint8_t> Stop;
+}
+namespace Application {
+extern const nall::vector<uint8_t> Browser;
+extern const nall::vector<uint8_t> Calculator;
+extern const nall::vector<uint8_t> Calendar;
+extern const nall::vector<uint8_t> Chat;
+extern const nall::vector<uint8_t> FileManager;
+extern const nall::vector<uint8_t> Mail;
+extern const nall::vector<uint8_t> Monitor;
+extern const nall::vector<uint8_t> Terminal;
+extern const nall::vector<uint8_t> TextEditor;
+}
+namespace Device {
+extern const nall::vector<uint8_t> Clock;
+extern const nall::vector<uint8_t> Display;
+extern const nall::vector<uint8_t> Joypad;
+extern const nall::vector<uint8_t> Keyboard;
+extern const nall::vector<uint8_t> Microphone;
+extern const nall::vector<uint8_t> Mouse;
+extern const nall::vector<uint8_t> Network;
+extern const nall::vector<uint8_t> Optical;
+extern const nall::vector<uint8_t> Printer;
+extern const nall::vector<uint8_t> Speaker;
+extern const nall::vector<uint8_t> Storage;
+}
+namespace Edit {
+extern const nall::vector<uint8_t> Clear;
+extern const nall::vector<uint8_t> Copy;
+extern const nall::vector<uint8_t> Cut;
+extern const nall::vector<uint8_t> Delete;
+extern const nall::vector<uint8_t> Find;
+extern const nall::vector<uint8_t> Paste;
+extern const nall::vector<uint8_t> Redo;
+extern const nall::vector<uint8_t> Replace;
+extern const nall::vector<uint8_t> Undo;
+}
+namespace Emblem {
+extern const nall::vector<uint8_t> Archive;
+extern const nall::vector<uint8_t> Audio;
+extern const nall::vector<uint8_t> Binary;
+extern const nall::vector<uint8_t> File;
+extern const nall::vector<uint8_t> Folder;
+extern const nall::vector<uint8_t> Font;
+extern const nall::vector<uint8_t> Image;
+extern const nall::vector<uint8_t> Markup;
+extern const nall::vector<uint8_t> Program;
+extern const nall::vector<uint8_t> Script;
+extern const nall::vector<uint8_t> Text;
+extern const nall::vector<uint8_t> Video;
+}
+namespace Go {
+extern const nall::vector<uint8_t> Down;
+extern const nall::vector<uint8_t> Home;
+extern const nall::vector<uint8_t> Left;
+extern const nall::vector<uint8_t> Right;
+extern const nall::vector<uint8_t> Up;
+}
+namespace Media {
+extern const nall::vector<uint8_t> Back;
+extern const nall::vector<uint8_t> Eject;
+extern const nall::vector<uint8_t> Flash;
+extern const nall::vector<uint8_t> Floppy;
+extern const nall::vector<uint8_t> Next;
+extern const nall::vector<uint8_t> Optical;
+extern const nall::vector<uint8_t> Pause;
+extern const nall::vector<uint8_t> Play;
+extern const nall::vector<uint8_t> Record;
+extern const nall::vector<uint8_t> Rewind;
+extern const nall::vector<uint8_t> Skip;
+extern const nall::vector<uint8_t> Stop;
+}
+namespace Place {
+extern const nall::vector<uint8_t> Bookmarks;
+extern const nall::vector<uint8_t> Desktop;
+extern const nall::vector<uint8_t> Home;
+extern const nall::vector<uint8_t> Server;
+extern const nall::vector<uint8_t> Share;
+}
+namespace Prompt {
+extern const nall::vector<uint8_t> Error;
+extern const nall::vector<uint8_t> Information;
+extern const nall::vector<uint8_t> Question;
+extern const nall::vector<uint8_t> Warning;
+}
+}
diff --git a/phoenix/windows/action/action.cpp b/hiro/windows/action/action.cpp
similarity index 100%
rename from phoenix/windows/action/action.cpp
rename to hiro/windows/action/action.cpp
diff --git a/phoenix/windows/action/check-item.cpp b/hiro/windows/action/check-item.cpp
similarity index 100%
rename from phoenix/windows/action/check-item.cpp
rename to hiro/windows/action/check-item.cpp
diff --git a/phoenix/windows/action/item.cpp b/hiro/windows/action/item.cpp
similarity index 100%
rename from phoenix/windows/action/item.cpp
rename to hiro/windows/action/item.cpp
diff --git a/phoenix/windows/action/menu.cpp b/hiro/windows/action/menu.cpp
similarity index 100%
rename from phoenix/windows/action/menu.cpp
rename to hiro/windows/action/menu.cpp
diff --git a/phoenix/windows/action/radio-item.cpp b/hiro/windows/action/radio-item.cpp
similarity index 100%
rename from phoenix/windows/action/radio-item.cpp
rename to hiro/windows/action/radio-item.cpp
diff --git a/phoenix/windows/action/separator.cpp b/hiro/windows/action/separator.cpp
similarity index 100%
rename from phoenix/windows/action/separator.cpp
rename to hiro/windows/action/separator.cpp
diff --git a/phoenix/windows/application.cpp b/hiro/windows/application.cpp
similarity index 100%
rename from phoenix/windows/application.cpp
rename to hiro/windows/application.cpp
diff --git a/phoenix/windows/browser-window.cpp b/hiro/windows/browser-window.cpp
similarity index 96%
rename from phoenix/windows/browser-window.cpp
rename to hiro/windows/browser-window.cpp
index 146c4c8e..93b2ba6b 100644
--- a/phoenix/windows/browser-window.cpp
+++ b/hiro/windows/browser-window.cpp
@@ -19,7 +19,7 @@ static string BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) {
   for(auto& filter : state.filters) {
     lstring part = filter.split("(");
     if(part.size() != 2) continue;
-    part[1].rtrim<1>(")");
+    part[1].rtrim(")");
     part[1].replace(" ", "");
     part[1].transform(",", ";");
     filters.append(filter, "\t", part[1], "\t");
diff --git a/phoenix/windows/desktop.cpp b/hiro/windows/desktop.cpp
similarity index 100%
rename from phoenix/windows/desktop.cpp
rename to hiro/windows/desktop.cpp
diff --git a/phoenix/windows/font.cpp b/hiro/windows/font.cpp
similarity index 94%
rename from phoenix/windows/font.cpp
rename to hiro/windows/font.cpp
index 94db0ecf..6cfadfb2 100644
--- a/phoenix/windows/font.cpp
+++ b/hiro/windows/font.cpp
@@ -26,9 +26,7 @@ Size pFont::size(string font, string text) {
 }
 
 HFONT pFont::create(string description) {
-  lstring part;
-  part.split(",", description);
-  for(auto& item : part) item.trim(" ");
+  lstring part = description.split<2>(",").strip();
 
   string family = "Sans";
   unsigned size = 8u;
diff --git a/phoenix/windows/header.hpp b/hiro/windows/header.hpp
similarity index 100%
rename from phoenix/windows/header.hpp
rename to hiro/windows/header.hpp
diff --git a/phoenix/windows/keyboard.cpp b/hiro/windows/keyboard.cpp
similarity index 100%
rename from phoenix/windows/keyboard.cpp
rename to hiro/windows/keyboard.cpp
diff --git a/phoenix/windows/message-window.cpp b/hiro/windows/message-window.cpp
similarity index 100%
rename from phoenix/windows/message-window.cpp
rename to hiro/windows/message-window.cpp
diff --git a/phoenix/windows/monitor.cpp b/hiro/windows/monitor.cpp
similarity index 100%
rename from phoenix/windows/monitor.cpp
rename to hiro/windows/monitor.cpp
diff --git a/phoenix/windows/mouse.cpp b/hiro/windows/mouse.cpp
similarity index 100%
rename from phoenix/windows/mouse.cpp
rename to hiro/windows/mouse.cpp
diff --git a/phoenix/windows/object.cpp b/hiro/windows/object.cpp
similarity index 100%
rename from phoenix/windows/object.cpp
rename to hiro/windows/object.cpp
diff --git a/phoenix/windows/phoenix.Manifest b/hiro/windows/phoenix.Manifest
similarity index 100%
rename from phoenix/windows/phoenix.Manifest
rename to hiro/windows/phoenix.Manifest
diff --git a/phoenix/windows/phoenix.rc b/hiro/windows/phoenix.rc
similarity index 100%
rename from phoenix/windows/phoenix.rc
rename to hiro/windows/phoenix.rc
diff --git a/phoenix/windows/platform.cpp b/hiro/windows/platform.cpp
similarity index 100%
rename from phoenix/windows/platform.cpp
rename to hiro/windows/platform.cpp
diff --git a/phoenix/windows/platform.hpp b/hiro/windows/platform.hpp
similarity index 98%
rename from phoenix/windows/platform.hpp
rename to hiro/windows/platform.hpp
index 06378f53..36366c6b 100644
--- a/phoenix/windows/platform.hpp
+++ b/hiro/windows/platform.hpp
@@ -273,6 +273,7 @@ struct pButton : public pWidget {
   HIMAGELIST himagelist;
 
   Size minimumSize();
+  void setBordered(bool bordered);
   void setImage(const image& image, Orientation orientation);
   void setText(string text);
 
@@ -360,6 +361,7 @@ struct pComboButton : public pWidget {
 struct pConsole : public pWidget {
   Console& console;
   LRESULT CALLBACK (*windowProc)(HWND, UINT, LPARAM, WPARAM);
+  HBRUSH backgroundBrush = nullptr;
 
   void print(string text);
   void reset();
@@ -392,6 +394,7 @@ struct pHexEdit : public pWidget {
   HexEdit& hexEdit;
   WindowProc windowProc = nullptr;
   HWND scrollBar = nullptr;
+  HBRUSH backgroundBrush = nullptr;
 
   void setBackgroundColor(Color color);
   void setColumns(unsigned columns);
@@ -456,6 +459,7 @@ struct pLabel : public pWidget {
 
 struct pLineEdit : public pWidget {
   LineEdit& lineEdit;
+  HBRUSH backgroundBrush = nullptr;
 
   Size minimumSize();
   void setBackgroundColor(Color color);
@@ -580,6 +584,7 @@ struct pTabFrame : public pWidget {
 
 struct pTextEdit : public pWidget {
   TextEdit& textEdit;
+  HBRUSH backgroundBrush = nullptr;
 
   void setBackgroundColor(Color color);
   void setCursorPosition(unsigned position);
diff --git a/phoenix/windows/settings.cpp b/hiro/windows/settings.cpp
similarity index 100%
rename from phoenix/windows/settings.cpp
rename to hiro/windows/settings.cpp
diff --git a/phoenix/windows/timer.cpp b/hiro/windows/timer.cpp
similarity index 100%
rename from phoenix/windows/timer.cpp
rename to hiro/windows/timer.cpp
diff --git a/phoenix/windows/utility.cpp b/hiro/windows/utility.cpp
similarity index 86%
rename from phoenix/windows/utility.cpp
rename to hiro/windows/utility.cpp
index 5902f0d5..ce692b59 100644
--- a/phoenix/windows/utility.cpp
+++ b/hiro/windows/utility.cpp
@@ -245,13 +245,41 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
 
   switch(msg) {
   case WM_CTLCOLORBTN:
+  case WM_CTLCOLOREDIT:
   case WM_CTLCOLORSTATIC: {
     Object* object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
     if(object == nullptr) break;
-    if(dynamic_cast<HexEdit*>(object) || dynamic_cast<LineEdit*>(object) || dynamic_cast<TextEdit*>(object)) {
-      //text edit controls, when disabled, use CTLCOLORSTATIC instead of CTLCOLOREDIT
-      //override this behavior: we do not want read-only edit controls to use the parent window background color
-      return windowProc(hwnd, WM_CTLCOLOREDIT, wparam, lparam);
+    //allow custom colors for various widgets
+    //note that this happens always: default colors are black text on a white background, unless overridden
+    //this intentionally overrides the default behavior of Windows to paint disabled controls with the window background color
+    if(dynamic_cast<Console*>(object)) {
+      Console& console = *(Console*)object;
+      Color& background = console.state.backgroundColor;
+      Color& foreground = console.state.foregroundColor;
+      SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
+      SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
+      return (LRESULT)console.p.backgroundBrush;
+    } else if(dynamic_cast<HexEdit*>(object)) {
+      HexEdit& hexEdit = *(HexEdit*)object;
+      Color& background = hexEdit.state.backgroundColor;
+      Color& foreground = hexEdit.state.foregroundColor;
+      SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
+      SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
+      return (LRESULT)hexEdit.p.backgroundBrush;
+    } else if(dynamic_cast<LineEdit*>(object)) {
+      LineEdit& lineEdit = *(LineEdit*)object;
+      Color& background = lineEdit.state.backgroundColor;
+      Color& foreground = lineEdit.state.foregroundColor;
+      SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
+      SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
+      return (LRESULT)lineEdit.p.backgroundBrush;
+    } else if(dynamic_cast<TextEdit*>(object)) {
+      TextEdit& textEdit = *(TextEdit*)object;
+      Color& background = textEdit.state.backgroundColor;
+      Color& foreground = textEdit.state.foregroundColor;
+      SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
+      SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
+      return (LRESULT)textEdit.p.backgroundBrush;
     } else if(!GetParentWidget((Sizable*)object) && window.p.brush) {
       SetBkColor((HDC)wparam, window.p.brushColor);
       return (INT_PTR)window.p.brush;
diff --git a/phoenix/windows/widget/button.cpp b/hiro/windows/widget/button.cpp
similarity index 95%
rename from phoenix/windows/widget/button.cpp
rename to hiro/windows/widget/button.cpp
index 0598c620..26a938f1 100644
--- a/phoenix/windows/widget/button.cpp
+++ b/hiro/windows/widget/button.cpp
@@ -32,7 +32,10 @@ Size pButton::minimumSize() {
     size.height += button.state.image.height;
   }
 
-  return {size.width + 20, size.height + 10};
+  return {size.width + (button.state.text ? 20 : 10), size.height + 10};
+}
+
+void pButton::setBordered(bool bordered) {
 }
 
 void pButton::setImage(const image& image, Orientation orientation) {
@@ -75,6 +78,7 @@ void pButton::constructor() {
   hwnd = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
   SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&button);
   setDefaultFont();
+  setBordered(button.state.bordered);
   setImage(button.state.image, button.state.orientation);
 //setText(button.state.text);  //called by setImage();
   synchronize();
diff --git a/phoenix/windows/widget/canvas.cpp b/hiro/windows/widget/canvas.cpp
similarity index 100%
rename from phoenix/windows/widget/canvas.cpp
rename to hiro/windows/widget/canvas.cpp
diff --git a/phoenix/windows/widget/check-button.cpp b/hiro/windows/widget/check-button.cpp
similarity index 100%
rename from phoenix/windows/widget/check-button.cpp
rename to hiro/windows/widget/check-button.cpp
diff --git a/phoenix/windows/widget/check-label.cpp b/hiro/windows/widget/check-label.cpp
similarity index 100%
rename from phoenix/windows/widget/check-label.cpp
rename to hiro/windows/widget/check-label.cpp
diff --git a/phoenix/windows/widget/combo-button.cpp b/hiro/windows/widget/combo-button.cpp
similarity index 100%
rename from phoenix/windows/widget/combo-button.cpp
rename to hiro/windows/widget/combo-button.cpp
diff --git a/phoenix/windows/widget/console.cpp b/hiro/windows/widget/console.cpp
similarity index 86%
rename from phoenix/windows/widget/console.cpp
rename to hiro/windows/widget/console.cpp
index 812fdfa3..64abd98b 100644
--- a/phoenix/windows/widget/console.cpp
+++ b/hiro/windows/widget/console.cpp
@@ -15,6 +15,8 @@ void pConsole::reset() {
 }
 
 void pConsole::setBackgroundColor(Color color) {
+  if(backgroundBrush) DeleteObject(backgroundBrush);
+  backgroundBrush = CreateSolidBrush(RGB(color.red, color.green, color.blue));
 }
 
 void pConsole::setForegroundColor(Color color) {
@@ -31,6 +33,7 @@ void pConsole::constructor() {
   );
   SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&console);
   setDefaultFont();
+  setBackgroundColor(console.state.backgroundColor);
 
   windowProc = (LRESULT CALLBACK (*)(HWND, UINT, LPARAM, WPARAM))GetWindowLongPtr(hwnd, GWLP_WNDPROC);
   SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)Console_windowProc);
diff --git a/phoenix/windows/widget/frame.cpp b/hiro/windows/widget/frame.cpp
similarity index 100%
rename from phoenix/windows/widget/frame.cpp
rename to hiro/windows/widget/frame.cpp
diff --git a/phoenix/windows/widget/hex-edit.cpp b/hiro/windows/widget/hex-edit.cpp
similarity index 97%
rename from phoenix/windows/widget/hex-edit.cpp
rename to hiro/windows/widget/hex-edit.cpp
index 3dace06c..b29f81b4 100644
--- a/phoenix/windows/widget/hex-edit.cpp
+++ b/hiro/windows/widget/hex-edit.cpp
@@ -50,6 +50,8 @@ static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, L
 }
 
 void pHexEdit::setBackgroundColor(Color color) {
+  if(backgroundBrush) DeleteObject(backgroundBrush);
+  backgroundBrush = CreateSolidBrush(RGB(color.red, color.green, color.blue));
 }
 
 void pHexEdit::setColumns(unsigned columns) {
@@ -131,6 +133,7 @@ void pHexEdit::constructor() {
   SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc);
 
   setDefaultFont();
+  setBackgroundColor(hexEdit.state.backgroundColor);
   setLength(hexEdit.state.length);
   setOffset(hexEdit.state.offset);
   update();
diff --git a/phoenix/windows/widget/horizontal-scroller.cpp b/hiro/windows/widget/horizontal-scroller.cpp
similarity index 100%
rename from phoenix/windows/widget/horizontal-scroller.cpp
rename to hiro/windows/widget/horizontal-scroller.cpp
diff --git a/phoenix/windows/widget/horizontal-slider.cpp b/hiro/windows/widget/horizontal-slider.cpp
similarity index 100%
rename from phoenix/windows/widget/horizontal-slider.cpp
rename to hiro/windows/widget/horizontal-slider.cpp
diff --git a/phoenix/windows/widget/label.cpp b/hiro/windows/widget/label.cpp
similarity index 100%
rename from phoenix/windows/widget/label.cpp
rename to hiro/windows/widget/label.cpp
diff --git a/phoenix/windows/widget/line-edit.cpp b/hiro/windows/widget/line-edit.cpp
similarity index 87%
rename from phoenix/windows/widget/line-edit.cpp
rename to hiro/windows/widget/line-edit.cpp
index 0b383132..c1e0be14 100644
--- a/phoenix/windows/widget/line-edit.cpp
+++ b/hiro/windows/widget/line-edit.cpp
@@ -6,6 +6,8 @@ Size pLineEdit::minimumSize() {
 }
 
 void pLineEdit::setBackgroundColor(Color color) {
+  if(backgroundBrush) DeleteObject(backgroundBrush);
+  backgroundBrush = CreateSolidBrush(RGB(color.red, color.green, color.blue));
 }
 
 void pLineEdit::setEditable(bool editable) {
@@ -37,6 +39,7 @@ void pLineEdit::constructor() {
   );
   SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&lineEdit);
   setDefaultFont();
+  setBackgroundColor(lineEdit.state.backgroundColor);
   setEditable(lineEdit.state.editable);
   setText(lineEdit.state.text);
   synchronize();
diff --git a/phoenix/windows/widget/list-view.cpp b/hiro/windows/widget/list-view.cpp
similarity index 91%
rename from phoenix/windows/widget/list-view.cpp
rename to hiro/windows/widget/list-view.cpp
index 465a9fe0..7813614b 100644
--- a/phoenix/windows/widget/list-view.cpp
+++ b/hiro/windows/widget/list-view.cpp
@@ -57,6 +57,7 @@ void pListView::reset() {
 }
 
 void pListView::setBackgroundColor(Color color) {
+  ListView_SetBkColor(hwnd, RGB(color.red, color.green, color.blue));
 }
 
 void pListView::setCheckable(bool checkable) {
@@ -152,6 +153,7 @@ void pListView::constructor() {
   SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&listView);
   setDefaultFont();
   setHeaderText(listView.state.headerText);
+  setBackgroundColor(listView.state.backgroundColor);
   setHeaderVisible(listView.state.headerVisible);
   setCheckable(listView.state.checkable);
   for(auto& text : listView.state.text) append(text);
@@ -259,17 +261,28 @@ LRESULT pListView::onCustomDraw(LPARAM lparam) {
   LPNMLVCUSTOMDRAW lvcd = (LPNMLVCUSTOMDRAW)lparam;
 
   switch(lvcd->nmcd.dwDrawStage) {
-  case CDDS_PREPAINT:
+
+  case CDDS_PREPAINT: {
     return CDRF_NOTIFYITEMDRAW;
-  case CDDS_ITEMPREPAINT:
-    if(listView.state.headerText.size() >= 2) {
-      //draw alternating row colors of there are two or more columns
-      if(lvcd->nmcd.dwItemSpec % 2) lvcd->clrTextBk = GetSysColor(COLOR_WINDOW) ^ 0x070707;
+  }
+
+  case CDDS_ITEMPREPAINT: {
+    Color& background = listView.state.backgroundColor;
+    Color& foreground = listView.state.foregroundColor;
+    lvcd->clrText = RGB(foreground.red, foreground.green, foreground.blue);
+    lvcd->clrTextBk = RGB(background.red, background.green, background.blue);
+    if(listView.state.headerText.size() >= 2 && lvcd->nmcd.dwItemSpec % 2) {
+      //draw alternating row colors if there are two or more columns
+      lvcd->clrTextBk = RGB(max(0, (signed)background.red - 17), max(0, (signed)background.green - 17), max(0, (signed)background.blue - 17));
     }
     return CDRF_DODEFAULT;
-  default:
+  }
+
+  default: {
     return CDRF_DODEFAULT;
   }
+
+  }
 }
 
 }
diff --git a/phoenix/windows/widget/progress-bar.cpp b/hiro/windows/widget/progress-bar.cpp
similarity index 100%
rename from phoenix/windows/widget/progress-bar.cpp
rename to hiro/windows/widget/progress-bar.cpp
diff --git a/phoenix/windows/widget/radio-button.cpp b/hiro/windows/widget/radio-button.cpp
similarity index 100%
rename from phoenix/windows/widget/radio-button.cpp
rename to hiro/windows/widget/radio-button.cpp
diff --git a/phoenix/windows/widget/radio-label.cpp b/hiro/windows/widget/radio-label.cpp
similarity index 100%
rename from phoenix/windows/widget/radio-label.cpp
rename to hiro/windows/widget/radio-label.cpp
diff --git a/phoenix/windows/widget/tab-frame.cpp b/hiro/windows/widget/tab-frame.cpp
similarity index 100%
rename from phoenix/windows/widget/tab-frame.cpp
rename to hiro/windows/widget/tab-frame.cpp
diff --git a/phoenix/windows/widget/text-edit.cpp b/hiro/windows/widget/text-edit.cpp
similarity index 91%
rename from phoenix/windows/widget/text-edit.cpp
rename to hiro/windows/widget/text-edit.cpp
index 509897fc..7ce02d7a 100644
--- a/phoenix/windows/widget/text-edit.cpp
+++ b/hiro/windows/widget/text-edit.cpp
@@ -1,6 +1,8 @@
 namespace phoenix {
 
 void pTextEdit::setBackgroundColor(Color color) {
+  if(backgroundBrush) DeleteObject(backgroundBrush);
+  backgroundBrush = CreateSolidBrush(RGB(color.red, color.green, color.blue));
 }
 
 void pTextEdit::setCursorPosition(unsigned position) {
@@ -49,6 +51,7 @@ void pTextEdit::constructor() {
   );
   SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&textEdit);
   setDefaultFont();
+  setBackgroundColor(textEdit.state.backgroundColor);
   setCursorPosition(textEdit.state.cursorPosition);
   setEditable(textEdit.state.editable);
   setText(textEdit.state.text);
diff --git a/phoenix/windows/widget/vertical-scroller.cpp b/hiro/windows/widget/vertical-scroller.cpp
similarity index 100%
rename from phoenix/windows/widget/vertical-scroller.cpp
rename to hiro/windows/widget/vertical-scroller.cpp
diff --git a/phoenix/windows/widget/vertical-slider.cpp b/hiro/windows/widget/vertical-slider.cpp
similarity index 100%
rename from phoenix/windows/widget/vertical-slider.cpp
rename to hiro/windows/widget/vertical-slider.cpp
diff --git a/phoenix/windows/widget/viewport.cpp b/hiro/windows/widget/viewport.cpp
similarity index 100%
rename from phoenix/windows/widget/viewport.cpp
rename to hiro/windows/widget/viewport.cpp
diff --git a/phoenix/windows/widget/widget.cpp b/hiro/windows/widget/widget.cpp
similarity index 100%
rename from phoenix/windows/widget/widget.cpp
rename to hiro/windows/widget/widget.cpp
diff --git a/phoenix/windows/window.cpp b/hiro/windows/window.cpp
similarity index 98%
rename from phoenix/windows/window.cpp
rename to hiro/windows/window.cpp
index fff305f0..79b64c4e 100644
--- a/phoenix/windows/window.cpp
+++ b/hiro/windows/window.cpp
@@ -192,7 +192,11 @@ void pWindow::setModal(bool modality) {
     updateModality();
     while(window.state.modal) {
       Application::processEvents();
-      usleep(20 * 1000);
+      if(Application::main) {
+        Application::main();
+      } else {
+        usleep(20 * 1000);
+      }
     }
     if(auto position = modal.find(this)) modal.remove(position());
     updateModality();
diff --git a/nall/Makefile b/nall/GNUmakefile
similarity index 90%
rename from nall/Makefile
rename to nall/GNUmakefile
index 9697bcd6..e51a20a8 100644
--- a/nall/Makefile
+++ b/nall/GNUmakefile
@@ -1,7 +1,3 @@
-# Makefile
-# author: byuu
-# license: public domain
-
 [A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
 [a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
 [0-9] = 0 1 2 3 4 5 6 7 8 9
@@ -36,8 +32,8 @@ endif
 
 cflags := -x c -std=c99
 objcflags := -x objective-c -std=c99
-cppflags := -x c++ -std=c++11
-objcppflags := -x objective-c++ -std=c++11
+cppflags := -x c++ -std=c++14
+objcppflags := -x objective-c++ -std=c++14
 flags :=
 link :=
 
@@ -48,20 +44,23 @@ ifeq ($(compiler),)
   else ifeq ($(platform),macosx)
     compiler := clang++
   else ifeq ($(platform),bsd)
-    compiler := g++47
+    compiler := g++49
   else
     compiler := g++
   endif
 endif
 
+# clang settings
+ifeq ($(findstring clang++,$(compiler)),clang++)
+  flags += -fwrapv
 # gcc settings
-ifeq ($(findstring g++,$(compiler)),g++)
+else ifeq ($(findstring g++,$(compiler)),g++)
   flags += -fwrapv
 endif
 
-# clang settings
-ifeq ($(findstring clang++,$(compiler)),clang++)
-  flags += -fwrapv -w
+# windows settings
+ifeq ($(platform),windows)
+  link := $(link) -lws2_32 -lole32
 endif
 
 # macosx settings
@@ -81,9 +80,8 @@ ifeq ($(arch),x86)
   link := -m32 $(link)
 endif
 
-ifeq ($(prefix),)
-  prefix := /usr/local
-endif
+# paths
+prefix := $(HOME)/.local
 
 # function rwildcard(directory, pattern)
 rwildcard = \
diff --git a/nall/algorithm.hpp b/nall/algorithm.hpp
index 5867c1cd..89fec57d 100644
--- a/nall/algorithm.hpp
+++ b/nall/algorithm.hpp
@@ -1,19 +1,29 @@
 #ifndef NALL_ALGORITHM_HPP
 #define NALL_ALGORITHM_HPP
 
+#include <nall/traits.hpp>
+
 #undef min
 #undef max
 
-namespace nall {
+namespace nall { namespace {
 
-template<typename T, typename U> T min(const T& t, const U& u) {
+template<typename T, typename U> auto min(const T& t, const U& u) -> T {
   return t < u ? t : u;
 }
 
-template<typename T, typename U> T max(const T& t, const U& u) {
+template<typename T, typename U, typename... P> auto min(const T& t, const U& u, P&&... p) -> T {
+  return t < u ? min(t, forward<P>(p)...) : min(u, forward<P>(p)...);
+}
+
+template<typename T, typename U> auto max(const T& t, const U& u) -> T {
   return t > u ? t : u;
 }
 
+template<typename T, typename U, typename... P> auto max(const T& t, const U& u, P&&... p) -> T {
+  return t > u ? max(t, forward<P>(p)...) : max(u, forward<P>(p)...);
 }
 
+}}
+
 #endif
diff --git a/nall/any.hpp b/nall/any.hpp
index 27a07ef2..8d6b638a 100644
--- a/nall/any.hpp
+++ b/nall/any.hpp
@@ -15,11 +15,7 @@ struct any {
   }
 
   template<typename T> any& operator=(const T& value) {
-    typedef typename type_if<
-      std::is_array<T>::value,
-      typename std::remove_extent<typename std::add_const<T>::type>::type*,
-      T
-    >::type auto_t;
+    using auto_t = type_if<is_array<T>, typename std::remove_extent<typename std::add_const<T>::type>::type*, T>;
 
     if(type() == typeid(auto_t)) {
       static_cast<holder<auto_t>*>(container)->value = (auto_t)value;
@@ -46,7 +42,7 @@ struct any {
 
   any() = default;
   any(const any& source) { operator=(source); }
-  any(any&& source) { operator=(std::move(source)); }
+  any(any&& source) { operator=(move(source)); }
   template<typename T> any(const T& value) { operator=(value); }
   ~any() { reset(); }
 
diff --git a/nall/beat/archive.hpp b/nall/beat/archive.hpp
index f8c6b6cf..f3fce618 100644
--- a/nall/beat/archive.hpp
+++ b/nall/beat/archive.hpp
@@ -10,7 +10,7 @@ struct beatArchive : beatBase {
     if(fp.open(beatname, file::mode::write) == false) return false;
     if(pathname.endsWith("/") == false) pathname.append("/");
 
-    checksum = ~0;
+    checksum.reset();
     writeString("BPA1");
     writeNumber(metadata.length());
     writeString(metadata);
@@ -19,7 +19,7 @@ struct beatArchive : beatBase {
     ls(list, pathname, pathname);
     for(auto &name : list) {
       if(name.endsWith("/")) {
-        name.rtrim<1>("/");
+        name.rtrim("/");
         writeNumber(0 | ((name.length() - 1) << 1));
         writeString(name);
       } else {
@@ -29,17 +29,17 @@ struct beatArchive : beatBase {
         writeString(name);
         unsigned size = stream.size();
         writeNumber(size);
-        uint32_t checksum = ~0;
+        Hash::CRC32 checksum;
         while(size--) {
           uint8_t data = stream.read();
           write(data);
-          checksum = crc32_adjust(checksum, data);
+          checksum.data(data);
         }
-        writeChecksum(~checksum);
+        writeChecksum(checksum.value());
       }
     }
 
-    writeChecksum(~checksum);
+    writeChecksum(checksum.value());
     fp.close();
     return true;
   }
@@ -48,7 +48,7 @@ struct beatArchive : beatBase {
     if(fp.open(beatname, file::mode::read) == false) return false;
     if(pathname.endsWith("/") == false) pathname.append("/");
 
-    checksum = ~0;
+    checksum.reset();
     if(readString(4) != "BPA1") return false;
     unsigned length = readNumber();
     while(length--) read();
@@ -65,17 +65,17 @@ struct beatArchive : beatBase {
         file stream;
         if(stream.open({pathname, name}, file::mode::write) == false) return false;
         unsigned size = readNumber();
-        uint32_t checksum = ~0;
+        Hash::CRC32 checksum;
         while(size--) {
           uint8_t data = read();
           stream.write(data);
-          checksum = crc32_adjust(checksum, data);
+          checksum.data(data);
         }
-        if(readChecksum(~checksum) == false) return false;
+        if(readChecksum(checksum.value()) == false) return false;
       }
     }
 
-    return readChecksum(~checksum);
+    return readChecksum(checksum.value());
   }
 };
 
diff --git a/nall/beat/base.hpp b/nall/beat/base.hpp
index d35b8e7c..7d021d52 100644
--- a/nall/beat/base.hpp
+++ b/nall/beat/base.hpp
@@ -6,24 +6,24 @@ namespace nall {
 struct beatBase {
 protected:
   file fp;
-  uint32_t checksum;
+  Hash::CRC32 checksum;
 
   void ls(lstring& list, const string& path, const string& basepath) {
     lstring paths = directory::folders(path);
     for(auto& pathname : paths) {
-      list.append(string{path, pathname}.ltrim<1>(basepath));
+      list.append(string{path, pathname}.ltrim(basepath));
       ls(list, {path, pathname}, basepath);
     }
 
     lstring files = directory::files(path);
     for(auto& filename : files) {
-      list.append(string{path, filename}.ltrim<1>(basepath));
+      list.append(string{path, filename}.ltrim(basepath));
     }
   }
 
   void write(uint8_t data) {
     fp.write(data);
-    checksum = crc32_adjust(checksum, data);
+    checksum.data(data);
   }
 
   void writeNumber(uint64_t data) {
@@ -50,7 +50,7 @@ protected:
 
   uint8_t read() {
     uint8_t data = fp.read();
-    checksum = crc32_adjust(checksum, data);
+    checksum.data(data);
     return data;
   }
 
@@ -68,12 +68,12 @@ protected:
 
   string readString(unsigned length) {
     string text;
-    text.reserve(length + 1);
-    for(unsigned n = 0; n < length; n++) {
-      text[n] = fp.read();
-      checksum = crc32_adjust(checksum, text[n]);
+    text.resize(length + 1);
+    char* p = text.pointer();
+    while(length--) {
+      *p = fp.read();
+      checksum.data(*p++);
     }
-    text[length] = 0;
     return text;
   }
 
diff --git a/nall/beat/delta.hpp b/nall/beat/delta.hpp
index 04145efb..487097fa 100644
--- a/nall/beat/delta.hpp
+++ b/nall/beat/delta.hpp
@@ -1,7 +1,6 @@
 #ifndef NALL_BEAT_DELTA_HPP
 #define NALL_BEAT_DELTA_HPP
 
-#include <nall/crc32.hpp>
 #include <nall/file.hpp>
 #include <nall/filemap.hpp>
 #include <nall/stdint.hpp>
@@ -63,12 +62,12 @@ bool bpsdelta::create(const string& filename, const string& metadata) {
   file modifyFile;
   if(modifyFile.open(filename, file::mode::write) == false) return false;
 
-  uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
+  Hash::CRC32 sourceChecksum, modifyChecksum;
   unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
 
   auto write = [&](uint8_t data) {
     modifyFile.write(data);
-    modifyChecksum = crc32_adjust(modifyChecksum, data);
+    modifyChecksum.data(data);
   };
 
   auto encode = [&](uint64_t data) {
@@ -103,7 +102,7 @@ bool bpsdelta::create(const string& filename, const string& metadata) {
   //source tree creation
   for(unsigned offset = 0; offset < sourceSize; offset++) {
     uint16_t symbol = sourceData[offset + 0];
-    sourceChecksum = crc32_adjust(sourceChecksum, symbol);
+    sourceChecksum.data(symbol);
     if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
     Node *node = new Node;
     node->offset = offset;
@@ -199,11 +198,10 @@ bool bpsdelta::create(const string& filename, const string& metadata) {
 
   targetReadFlush();
 
-  sourceChecksum = ~sourceChecksum;
-  for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
-  uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
+  for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum.value() >> n);
+  uint32_t targetChecksum = Hash::CRC32(targetData, targetSize).value();
   for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
-  uint32_t outputChecksum = ~modifyChecksum;
+  uint32_t outputChecksum = modifyChecksum.value();
   for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
 
   modifyFile.close();
diff --git a/nall/beat/linear.hpp b/nall/beat/linear.hpp
index 4ee44a57..ac13894c 100644
--- a/nall/beat/linear.hpp
+++ b/nall/beat/linear.hpp
@@ -1,7 +1,6 @@
 #ifndef NALL_BEAT_LINEAR_HPP
 #define NALL_BEAT_LINEAR_HPP
 
-#include <nall/crc32.hpp>
 #include <nall/file.hpp>
 #include <nall/filemap.hpp>
 #include <nall/stdint.hpp>
@@ -56,12 +55,12 @@ bool bpslinear::create(const string& filename, const string& metadata) {
   file modifyFile;
   if(modifyFile.open(filename, file::mode::write) == false) return false;
 
-  uint32_t modifyChecksum = ~0;
+  Hash::CRC32 modifyChecksum;
   unsigned targetRelativeOffset = 0, outputOffset = 0;
 
   auto write = [&](uint8_t data) {
     modifyFile.write(data);
-    modifyChecksum = crc32_adjust(modifyChecksum, data);
+    modifyChecksum.data(data);
   };
 
   auto encode = [&](uint64_t data) {
@@ -136,11 +135,11 @@ bool bpslinear::create(const string& filename, const string& metadata) {
 
   targetReadFlush();
 
-  uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
+  uint32_t sourceChecksum = Hash::CRC32(sourceData, sourceSize).value();
   for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
-  uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
+  uint32_t targetChecksum = Hash::CRC32(targetData, targetSize).value();
   for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
-  uint32_t outputChecksum = ~modifyChecksum;
+  uint32_t outputChecksum = modifyChecksum.value();
   for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
 
   modifyFile.close();
diff --git a/nall/beat/metadata.hpp b/nall/beat/metadata.hpp
index 2b31fdfd..f83771ca 100644
--- a/nall/beat/metadata.hpp
+++ b/nall/beat/metadata.hpp
@@ -1,7 +1,6 @@
 #ifndef NALL_BEAT_METADATA_HPP
 #define NALL_BEAT_METADATA_HPP
 
-#include <nall/crc32.hpp>
 #include <nall/file.hpp>
 #include <nall/filemap.hpp>
 #include <nall/stdint.hpp>
@@ -75,11 +74,11 @@ bool bpsmetadata::save(const string& filename, const string& metadata) {
     return data;
   };
 
-  uint32_t checksum = ~0;
+  Hash::CRC32 checksum;
 
   auto write = [&](uint8_t data) {
     targetFile.write(data);
-    checksum = crc32_adjust(checksum, data);
+    checksum.data(data);
   };
 
   auto encode = [&](uint64_t data) {
@@ -105,7 +104,7 @@ bool bpsmetadata::save(const string& filename, const string& metadata) {
   for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
   unsigned length = sourceFile.size() - sourceFile.offset() - 4;
   for(unsigned n = 0; n < length; n++) write(read());
-  uint32_t outputChecksum = ~checksum;
+  uint32_t outputChecksum = checksum.value();
   for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
 
   targetFile.close();
diff --git a/nall/beat/multi.hpp b/nall/beat/multi.hpp
index c99001c8..6d9428d6 100644
--- a/nall/beat/multi.hpp
+++ b/nall/beat/multi.hpp
@@ -23,7 +23,7 @@ struct bpsmulti {
   bool create(const string& patchName, const string& sourcePath, const string& targetPath, bool delta = false, const string& metadata = "") {
     if(fp.open()) fp.close();
     fp.open(patchName, file::mode::write);
-    checksum = ~0;
+    checksum.reset();
 
     writeString("BPM1");  //signature
     writeNumber(metadata.length());
@@ -35,7 +35,7 @@ struct bpsmulti {
 
     for(auto& targetName : targetList) {
       if(targetName.endsWith("/")) {
-        targetName.rtrim<1>("/");
+        targetName.rtrim("/");
         writeNumber(CreatePath | ((targetName.length() - 1) << 2));
         writeString(targetName);
       } else if(auto position = sourceList.find(targetName)) {  //if sourceName == targetName
@@ -44,19 +44,19 @@ struct bpsmulti {
         dp.open({targetPath, targetName}, file::mode::read);
 
         bool identical = sp.size() == dp.size();
-        uint32_t cksum = ~0;
+        Hash::CRC32 cksum;
 
         for(unsigned n = 0; n < sp.size(); n++) {
           uint8_t byte = sp.read();
           if(identical && byte != dp.read()) identical = false;
-          cksum = crc32_adjust(cksum, byte);
+          cksum.data(byte);
         }
 
         if(identical) {
           writeNumber(MirrorFile | ((targetName.length() - 1) << 2));
           writeString(targetName);
           writeNumber(OriginSource);
-          writeChecksum(~cksum);
+          writeChecksum(cksum.value());
         } else {
           writeNumber(ModifyFile | ((targetName.length() - 1) << 2));
           writeString(targetName);
@@ -84,12 +84,12 @@ struct bpsmulti {
         auto buffer = file::read({targetPath, targetName});
         writeNumber(buffer.size());
         for(auto &byte : buffer) write(byte);
-        writeChecksum(crc32_calculate(buffer.data(), buffer.size()));
+        writeChecksum(Hash::CRC32(buffer.data(), buffer.size()).value());
       }
     }
 
     //checksum
-    writeChecksum(~checksum);
+    writeChecksum(checksum.value());
     fp.close();
     return true;
   }
@@ -100,7 +100,7 @@ struct bpsmulti {
 
     if(fp.open()) fp.close();
     fp.open(patchName, file::mode::read);
-    checksum = ~0;
+    checksum.reset();
 
     if(readString(4) != "BPM1") return false;
     auto metadataLength = readNumber();
@@ -142,7 +142,7 @@ struct bpsmulti {
       }
     }
 
-    uint32_t cksum = ~checksum;
+    uint32_t cksum = checksum.value();
     if(read() != (uint8_t)(cksum >>  0)) return false;
     if(read() != (uint8_t)(cksum >>  8)) return false;
     if(read() != (uint8_t)(cksum >> 16)) return false;
@@ -154,25 +154,25 @@ struct bpsmulti {
 
 protected:
   file fp;
-  uint32_t checksum;
+  Hash::CRC32 checksum;
 
   //create() functions
   void ls(lstring& list, const string& path, const string& basepath) {
     lstring paths = directory::folders(path);
     for(auto& pathname : paths) {
-      list.append(string{path, pathname}.ltrim<1>(basepath));
+      list.append(string{path, pathname}.ltrim(basepath));
       ls(list, {path, pathname}, basepath);
     }
 
     lstring files = directory::files(path);
     for(auto& filename : files) {
-      list.append(string{path, filename}.ltrim<1>(basepath));
+      list.append(string{path, filename}.ltrim(basepath));
     }
   }
 
   void write(uint8_t data) {
     fp.write(data);
-    checksum = crc32_adjust(checksum, data);
+    checksum.data(data);
   }
 
   void writeNumber(uint64_t data) {
@@ -203,7 +203,7 @@ protected:
   //apply() functions
   uint8_t read() {
     uint8_t data = fp.read();
-    checksum = crc32_adjust(checksum, data);
+    checksum.data(data);
     return data;
   }
 
@@ -221,9 +221,9 @@ protected:
 
   string readString(unsigned length) {
     string text;
-    text.reserve(length + 1);
-    for(unsigned n = 0; n < length; n++) text[n] = read();
-    text[length] = 0;
+    text.resize(length + 1);
+    char* p = text.pointer();
+    while(length--) *p++ = read();
     return text;
   }
 
diff --git a/nall/beat/patch.hpp b/nall/beat/patch.hpp
index 59c6c5d1..081c4ff0 100644
--- a/nall/beat/patch.hpp
+++ b/nall/beat/patch.hpp
@@ -1,7 +1,6 @@
 #ifndef NALL_BEAT_PATCH_HPP
 #define NALL_BEAT_PATCH_HPP
 
-#include <nall/crc32.hpp>
 #include <nall/file.hpp>
 #include <nall/filemap.hpp>
 #include <nall/stdint.hpp>
@@ -129,12 +128,12 @@ unsigned bpspatch::size() const {
 bpspatch::result bpspatch::apply() {
   if(modifySize < 19) return result::patch_too_small;
 
-  uint32_t modifyChecksum = ~0, targetChecksum = ~0;
+  Hash::CRC32 modifyChecksum, targetChecksum;
   unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
 
   auto read = [&]() -> uint8_t {
     uint8_t data = modifyData[modifyOffset++];
-    modifyChecksum = crc32_adjust(modifyChecksum, data);
+    modifyChecksum.data(data);
     return data;
   };
 
@@ -152,7 +151,7 @@ bpspatch::result bpspatch::apply() {
 
   auto write = [&](uint8_t data) {
     targetData[outputOffset++] = data;
-    targetChecksum = crc32_adjust(targetChecksum, data);
+    targetChecksum.data(data);
   };
 
   if(read() != 'B') return result::patch_invalid_header;
@@ -201,14 +200,13 @@ bpspatch::result bpspatch::apply() {
   uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
   for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
   for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
-  uint32_t checksum = ~modifyChecksum;
+  uint32_t checksum = modifyChecksum.value();
   for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
 
-  uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
-  targetChecksum = ~targetChecksum;
+  uint32_t sourceChecksum = Hash::CRC32(sourceData, modifySourceSize).value();
 
   if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
-  if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
+  if(targetChecksum.value() != modifyTargetChecksum) return result::target_checksum_invalid;
   if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
 
   return result::success;
diff --git a/nall/config.hpp b/nall/config.hpp
index 4c69bce8..0a7d8f55 100644
--- a/nall/config.hpp
+++ b/nall/config.hpp
@@ -60,7 +60,7 @@ struct Node {
     for(auto& child : children) {
       auto leaf = path[child.name];
       if(!leaf.exists()) continue;
-      if(!child.empty()) child.set(leaf.data.trim<1>(" ", "\r"));
+      if(!child.empty()) child.set(leaf.text());
       child.load(leaf);
     }
   }
diff --git a/nall/crc16.hpp b/nall/crc16.hpp
deleted file mode 100644
index be79a502..00000000
--- a/nall/crc16.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef NALL_CRC16_HPP
-#define NALL_CRC16_HPP
-
-#include <nall/stdint.hpp>
-
-namespace nall {
-
-inline uint16_t crc16_adjust(uint16_t crc16, uint8_t data) {
-  for(unsigned n = 0; n < 8; n++) {
-    if((crc16 & 1) ^ (data & 1)) crc16 = (crc16 >> 1) ^ 0x8408;
-    else crc16 >>= 1;
-    data >>= 1;
-  }
-  return crc16;
-}
-
-inline uint16_t crc16_calculate(const uint8_t* data, unsigned length) {
-  uint16_t crc16 = ~0;
-  for(unsigned n = 0; n < length; n++) {
-    crc16 = crc16_adjust(crc16, data[n]);
-  }
-  return ~crc16;
-}
-
-}
-
-#endif
diff --git a/nall/decode/bmp.hpp b/nall/decode/bmp.hpp
new file mode 100644
index 00000000..04330639
--- /dev/null
+++ b/nall/decode/bmp.hpp
@@ -0,0 +1,8 @@
+#ifndef NALL_DECODE_BMP_HPP
+#define NALL_DECODE_BMP_HPP
+
+namespace nall {
+
+}
+
+#endif
diff --git a/nall/gzip.hpp b/nall/decode/gzip.hpp
similarity index 81%
rename from nall/gzip.hpp
rename to nall/decode/gzip.hpp
index 2d43e7ab..db42100c 100644
--- a/nall/gzip.hpp
+++ b/nall/decode/gzip.hpp
@@ -1,12 +1,12 @@
-#ifndef NALL_GZIP_HPP
-#define NALL_GZIP_HPP
+#ifndef NALL_DECODE_GZIP_HPP
+#define NALL_DECODE_GZIP_HPP
 
 #include <nall/file.hpp>
-#include <nall/inflate.hpp>
+#include <nall/decode/inflate.hpp>
 
-namespace nall {
+namespace nall { namespace Decode {
 
-struct gzip {
+struct GZIP {
   string filename;
   uint8_t* data = nullptr;
   unsigned size = 0;
@@ -14,18 +14,18 @@ struct gzip {
   inline bool decompress(const string& filename);
   inline bool decompress(const uint8_t* data, unsigned size);
 
-  inline gzip();
-  inline ~gzip();
+  inline GZIP();
+  inline ~GZIP();
 };
 
-bool gzip::decompress(const string& filename) {
+bool GZIP::decompress(const string& filename) {
   if(auto memory = file::read(filename)) {
     return decompress(memory.data(), memory.size());
   }
   return false;
 }
 
-bool gzip::decompress(const uint8_t* data, unsigned size) {
+bool GZIP::decompress(const uint8_t* data, unsigned size) {
   if(size < 18) return false;
   if(data[0] != 0x1f) return false;
   if(data[1] != 0x8b) return false;
@@ -73,13 +73,13 @@ bool gzip::decompress(const uint8_t* data, unsigned size) {
   return inflate(this->data, this->size, data + p, size - p - 8);
 }
 
-gzip::gzip() {
+GZIP::GZIP() {
 }
 
-gzip::~gzip() {
+GZIP::~GZIP() {
   if(data) delete[] data;
 }
 
-}
+}}
 
 #endif
diff --git a/nall/inflate.hpp b/nall/decode/inflate.hpp
similarity index 98%
rename from nall/inflate.hpp
rename to nall/decode/inflate.hpp
index d096d173..00fb2305 100644
--- a/nall/inflate.hpp
+++ b/nall/decode/inflate.hpp
@@ -1,9 +1,9 @@
-#ifndef NALL_INFLATE_HPP
-#define NALL_INFLATE_HPP
+#ifndef NALL_DECODE_INFLATE_HPP
+#define NALL_DECODE_INFLATE_HPP
 
 #include <setjmp.h>
 
-namespace nall {
+namespace nall { namespace Decode {
 
 namespace puff {
   inline int puff(
@@ -344,6 +344,6 @@ inline int puff(
 
 }
 
-}
+}}
 
 #endif
diff --git a/nall/png.hpp b/nall/decode/png.hpp
similarity index 93%
rename from nall/png.hpp
rename to nall/decode/png.hpp
index ccdf64eb..3991f823 100644
--- a/nall/png.hpp
+++ b/nall/decode/png.hpp
@@ -1,15 +1,12 @@
-#ifndef NALL_PNG_HPP
-#define NALL_PNG_HPP
+#ifndef NALL_DECODE_PNG_HPP
+#define NALL_DECODE_PNG_HPP
 
-//PNG image decoder
-//author: byuu
-
-#include <nall/inflate.hpp>
 #include <nall/string.hpp>
+#include <nall/decode/inflate.hpp>
 
-namespace nall {
+namespace nall { namespace Decode {
 
-struct png {
+struct PNG {
   struct Info {
     unsigned width;
     unsigned height;
@@ -39,8 +36,8 @@ struct png {
   inline unsigned readbits(const uint8_t*& data);
   unsigned bitpos = 0;
 
-  inline png();
-  inline ~png();
+  inline PNG();
+  inline ~PNG();
 
 protected:
   enum class FourCC : unsigned {
@@ -57,14 +54,14 @@ protected:
   inline unsigned read(const uint8_t* data, unsigned length);
 };
 
-bool png::decode(const string& filename) {
+bool PNG::decode(const string& filename) {
   if(auto memory = file::read(filename)) {
     return decode(memory.data(), memory.size());
   }
   return false;
 }
 
-bool png::decode(const uint8_t* sourceData, unsigned sourceSize) {
+bool PNG::decode(const uint8_t* sourceData, unsigned sourceSize) {
   if(sourceSize < 8) return false;
   if(read(sourceData + 0, 4) != 0x89504e47) return false;
   if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false;
@@ -170,7 +167,7 @@ bool png::decode(const uint8_t* sourceData, unsigned sourceSize) {
   return true;
 }
 
-unsigned png::interlace(unsigned pass, unsigned index) {
+unsigned PNG::interlace(unsigned pass, unsigned index) {
   static const unsigned data[7][4] = {
     //x-distance, y-distance, x-origin, y-origin
     {8, 8, 0, 0},
@@ -184,7 +181,7 @@ unsigned png::interlace(unsigned pass, unsigned index) {
   return data[pass][index];
 }
 
-unsigned png::inflateSize() {
+unsigned PNG::inflateSize() {
   if(info.interlaceMethod == 0) {
     return info.width * info.height * info.bytesPerPixel + info.height;
   }
@@ -201,7 +198,7 @@ unsigned png::inflateSize() {
   return size;
 }
 
-bool png::deinterlace(const uint8_t*& inputData, unsigned pass) {
+bool PNG::deinterlace(const uint8_t*& inputData, unsigned pass) {
   unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
   unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
   unsigned width  = (info.width  + (xd - xo - 1)) / xd;
@@ -227,7 +224,7 @@ bool png::deinterlace(const uint8_t*& inputData, unsigned pass) {
   return result;
 }
 
-bool png::filter(uint8_t* outputData, const uint8_t* inputData, unsigned width, unsigned height) {
+bool PNG::filter(uint8_t* outputData, const uint8_t* inputData, unsigned width, unsigned height) {
   uint8_t* wr = outputData;
   const uint8_t* rd = inputData;
   int bpp = info.bytesPerPixel, pitch = width * bpp;
@@ -290,13 +287,13 @@ bool png::filter(uint8_t* outputData, const uint8_t* inputData, unsigned width,
   return true;
 }
 
-unsigned png::read(const uint8_t* data, unsigned length) {
+unsigned PNG::read(const uint8_t* data, unsigned length) {
   unsigned result = 0;
   while(length--) result = (result << 8) | (*data++);
   return result;
 }
 
-unsigned png::readbits(const uint8_t*& data) {
+unsigned PNG::readbits(const uint8_t*& data) {
   unsigned result = 0;
   switch(info.bitDepth) {
   case 1:
@@ -325,13 +322,13 @@ unsigned png::readbits(const uint8_t*& data) {
   return result;
 }
 
-png::png() {
+PNG::PNG() {
 }
 
-png::~png() {
+PNG::~PNG() {
   if(data) delete[] data;
 }
 
-}
+}}
 
 #endif
diff --git a/nall/unzip.hpp b/nall/decode/zip.hpp
similarity index 94%
rename from nall/unzip.hpp
rename to nall/decode/zip.hpp
index 269a15d2..419a99d3 100644
--- a/nall/unzip.hpp
+++ b/nall/decode/zip.hpp
@@ -1,14 +1,14 @@
-#ifndef NALL_UNZIP_HPP
-#define NALL_UNZIP_HPP
+#ifndef NALL_DECODE_ZIP_HPP
+#define NALL_DECODE_ZIP_HPP
 
 #include <nall/filemap.hpp>
-#include <nall/inflate.hpp>
 #include <nall/string.hpp>
 #include <nall/vector.hpp>
+#include <nall/decode/inflate.hpp>
 
-namespace nall {
+namespace nall { namespace Decode {
 
-struct unzip {
+struct ZIP {
   struct File {
     string name;
     const uint8_t* data;
@@ -102,7 +102,7 @@ struct unzip {
     if(fm.open()) fm.close();
   }
 
-  ~unzip() {
+  ~ZIP() {
     close();
   }
 
@@ -121,6 +121,6 @@ public:
   vector<File> file;
 };
 
-}
+}}
 
 #endif
diff --git a/nall/directory.hpp b/nall/directory.hpp
index 45a062c7..f2983df6 100644
--- a/nall/directory.hpp
+++ b/nall/directory.hpp
@@ -4,6 +4,7 @@
 #include <nall/file.hpp>
 #include <nall/intrinsics.hpp>
 #include <nall/sort.hpp>
+#include <nall/storage.hpp>
 #include <nall/string.hpp>
 #include <nall/vector.hpp>
 
@@ -17,7 +18,7 @@
 
 namespace nall {
 
-struct directory {
+struct directory : storage {
   static bool create(const string& pathname, unsigned permissions = 0755);  //recursive
   static bool remove(const string& pathname);  //recursive
   static bool exists(const string& pathname);
@@ -73,7 +74,7 @@ private:
 #if defined(PLATFORM_WINDOWS)
   inline bool directory::create(const string& pathname, unsigned permissions) {
     string path;
-    lstring list = string{pathname}.transform("\\", "/").rtrim<1>("/").split("/");
+    lstring list = string{pathname}.transform("\\", "/").rtrim("/").split("/");
     bool result = true;
     for(auto& part : list) {
       path.append(part, "/");
@@ -93,7 +94,7 @@ private:
 
   inline bool directory::exists(const string& pathname) {
     string name = pathname;
-    name.trim<1>("\"");
+    name.trim("\"", "\"");
     DWORD result = GetFileAttributes(utf16_t(name));
     if(result == INVALID_FILE_ATTRIBUTES) return false;
     return (result & FILE_ATTRIBUTE_DIRECTORY);
@@ -103,7 +104,7 @@ private:
     lstring list;
     string path = pathname;
     path.transform("/", "\\");
-    if(!strend(path, "\\")) path.append("\\");
+    if(!path.endsWith("\\")) path.append("\\");
     path.append("*");
     HANDLE handle;
     WIN32_FIND_DATA data;
@@ -133,7 +134,7 @@ private:
     lstring list;
     string path = pathname;
     path.transform("/", "\\");
-    if(!strend(path, "\\")) path.append("\\");
+    if(!path.endsWith("\\")) path.append("\\");
     path.append("*");
     HANDLE handle;
     WIN32_FIND_DATA data;
@@ -154,12 +155,24 @@ private:
     return list;
   }
 #else
+  inline bool directory_is_folder(DIR* dp, struct dirent* ep) {
+    if(ep->d_type == DT_DIR) return true;
+    if(ep->d_type == DT_LNK || ep->d_type == DT_UNKNOWN) {
+      //symbolic links must be resolved to determine type
+      struct stat sp = {0};
+      fstatat(dirfd(dp), ep->d_name, &sp, 0);
+      return S_ISDIR(sp.st_mode);
+    }
+    return false;
+  }
+
   inline bool directory::create(const string& pathname, unsigned permissions) {
     string path;
-    lstring list = string{pathname}.rtrim<1>("/").split("/");
+    lstring list = string{pathname}.rtrim("/").split("/");
     bool result = true;
     for(auto& part : list) {
       path.append(part, "/");
+      if(directory::exists(path)) continue;
       result &= (mkdir(path, permissions) == 0);
     }
     return result;
@@ -175,7 +188,7 @@ private:
   }
 
   inline bool directory::exists(const string& pathname) {
-    DIR *dp = opendir(pathname);
+    DIR* dp = opendir(pathname);
     if(!dp) return false;
     closedir(dp);
     return true;
@@ -190,15 +203,9 @@ private:
       while(ep = readdir(dp)) {
         if(!strcmp(ep->d_name, ".")) continue;
         if(!strcmp(ep->d_name, "..")) continue;
-        bool is_directory = ep->d_type & DT_DIR;
-        if(ep->d_type & DT_UNKNOWN) {
-          struct stat sp = {0};
-          stat(string{pathname, ep->d_name}, &sp);
-          is_directory = S_ISDIR(sp.st_mode);
-        }
-        if(is_directory) {
-          if(strmatch(ep->d_name, pattern)) list.append(ep->d_name);
-        }
+        if(!directory_is_folder(dp, ep)) continue;
+        string name{ep->d_name};
+        if(name.match(pattern)) list.append(std::move(name));
       }
       closedir(dp);
     }
@@ -215,9 +222,9 @@ private:
       while(ep = readdir(dp)) {
         if(!strcmp(ep->d_name, ".")) continue;
         if(!strcmp(ep->d_name, "..")) continue;
-        if((ep->d_type & DT_DIR) == 0) {
-          if(strmatch(ep->d_name, pattern)) list.append(ep->d_name);
-        }
+        if(directory_is_folder(dp, ep)) continue;
+        string name{ep->d_name};
+        if(name.match(pattern)) list.append(std::move(name));
       }
       closedir(dp);
     }
diff --git a/nall/dl.hpp b/nall/dl.hpp
index d0d0151e..baf7e70e 100644
--- a/nall/dl.hpp
+++ b/nall/dl.hpp
@@ -38,8 +38,10 @@ private:
 #if defined(PLATFORM_XORG)
 inline bool library::open(const string& name, const string& path) {
   if(handle) close();
-  handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endsWith("/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY);
+  if(path) handle = (uintptr_t)dlopen(string(path, "lib", name, ".so"), RTLD_LAZY);
+  if(!handle) handle = (uintptr_t)dlopen(string(userpath(), ".local/lib/lib", name, ".so"), RTLD_LAZY);
   if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY);
+  if(!handle) handle = (uintptr_t)dlopen(string("lib", name, ".so"), RTLD_LAZY);
   return handle;
 }
 
@@ -62,8 +64,10 @@ inline void library::close() {
 #elif defined(PLATFORM_MACOSX)
 inline bool library::open(const string& name, const string& path) {
   if(handle) close();
-  handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endsWith("/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY);
+  if(path) handle = (uintptr_t)dlopen(string(path, "lib", name, ".dylib"), RTLD_LAZY);
+  if(!handle) handle = (uintptr_t)dlopen(string(userpath(), ".local/lib/lib", name, ".dylib"), RTLD_LAZY);
   if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY);
+  if(!handle) handle = (uintptr_t)dlopen(string("lib", name, ".dylib"), RTLD_LAZY);
   return handle;
 }
 
@@ -86,8 +90,14 @@ inline void library::close() {
 #elif defined(PLATFORM_WINDOWS)
 inline bool library::open(const string& name, const string& path) {
   if(handle) close();
-  string filepath(path, !path.empty() && !path.endsWith("/") && !path.endsWith("\\") ? "/" : "", name, ".dll");
-  handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
+  if(path) {
+    string filepath = {path, name, ".dll"};
+    handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
+  }
+  if(!handle) {
+    string filepath = {name, ".dll"};
+    handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
+  }
   return handle;
 }
 
diff --git a/nall/file.hpp b/nall/file.hpp
index 2cd28a13..cdebda4f 100644
--- a/nall/file.hpp
+++ b/nall/file.hpp
@@ -3,26 +3,18 @@
 
 #include <nall/platform.hpp>
 #include <nall/stdint.hpp>
+#include <nall/storage.hpp>
 #include <nall/string.hpp>
 #include <nall/utility.hpp>
 #include <nall/varint.hpp>
-#include <nall/windows/utf8.hpp>
+#include <nall/hash/sha256.hpp>
 #include <nall/stream/memory.hpp>
 
 namespace nall {
 
-inline FILE* fopen_utf8(const string& filename, const string& mode) {
-  #if !defined(_WIN32)
-  return fopen(filename, mode);
-  #else
-  return _wfopen(utf16_t(filename), utf16_t(mode));
-  #endif
-}
-
-struct file : varint {
+struct file : storage, varint {
   enum class mode : unsigned { read, write, modify, append, readwrite = modify, writeread = append };
   enum class index : unsigned { absolute, relative };
-  enum class time : unsigned { create, modify, access };
 
   static bool copy(const string& sourcename, const string& targetname) {
     file rd, wr;
@@ -32,29 +24,24 @@ struct file : varint {
     return true;
   }
 
+  //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) {
-    auto result = rename(sourcename, targetname);
-    if(result == 0) return true;
-    if(errno == EXDEV) {
-      //cannot move files between file systems; copy file instead of failing
-      if(file::copy(sourcename, targetname)) {
-        file::remove(sourcename);
-        return true;
-      }
+    if(rename(sourcename, targetname)) return true;
+    if(!writable(sourcename)) return false;
+    if(copy(sourcename, targetname)) {
+      remove(sourcename);
+      return true;
     }
     return false;
   }
 
-  static bool remove(const string& filename) {
-    return unlink(filename) == 0;
-  }
-
   static bool truncate(const string& filename, unsigned size) {
     #if !defined(_WIN32)
     return truncate(filename, size) == 0;
     #else
     bool result = false;
-    FILE* fp = fopen(filename, "rb+");
+    FILE* fp = _wfopen(utf16_t(filename), L"rb+");
     if(fp) {
       result = _chsize(fileno(fp), size) == 0;
       fclose(fp);
@@ -63,6 +50,29 @@ struct file : varint {
     #endif
   }
 
+  //specialization of storage::exists(); returns false for folders
+  static bool exists(const string& filename) {
+    #if !defined(_WIN32)
+    struct stat data;
+    if(stat(filename, &data) != 0) return false;
+    #else
+    struct __stat64 data;
+    if(_wstat64(utf16_t(filename), &data) != 0) return false;
+    #endif
+    return !(data.st_mode & S_IFDIR);
+  }
+
+  static uintmax_t size(const string& filename) {
+    #if !defined(_WIN32)
+    struct stat data;
+    stat(filename, &data);
+    #else
+    struct __stat64 data;
+    _wstat64(utf16_t(filename), &data);
+    #endif
+    return S_ISREG(data.st_mode) ? data.st_size : 0u;
+  }
+
   static vector<uint8_t> read(const string& filename) {
     vector<uint8_t> memory;
     file fp;
@@ -115,7 +125,7 @@ struct file : varint {
 
   static string sha256(const string& filename) {
     auto buffer = read(filename);
-    return nall::sha256(buffer.data(), buffer.size());
+    return Hash::SHA256(buffer.data(), buffer.size()).digest();
   }
 
   uint8_t read() {
@@ -231,44 +241,6 @@ struct file : varint {
     return file_offset >= file_size;
   }
 
-  static bool exists(const string& filename) {
-    #if !defined(_WIN32)
-    struct stat data;
-    if(stat(filename, &data) != 0) return false;
-    #else
-    struct __stat64 data;
-    if(_wstat64(utf16_t(filename), &data) != 0) return false;
-    #endif
-    //return true if this is a file, and false if this is a directory
-    return !(data.st_mode & S_IFDIR);
-  }
-
-  static uintmax_t size(const string& filename) {
-    #if !defined(_WIN32)
-    struct stat data;
-    stat(filename, &data);
-    #else
-    struct __stat64 data;
-    _wstat64(utf16_t(filename), &data);
-    #endif
-    return S_ISREG(data.st_mode) ? data.st_size : 0u;
-  }
-
-  static time_t timestamp(const string& filename, file::time mode = file::time::create) {
-    #if !defined(_WIN32)
-    struct stat data;
-    stat(filename, &data);
-    #else
-    struct __stat64 data;
-    _wstat64(utf16_t(filename), &data);
-    #endif
-    switch(mode) { default:
-    case file::time::create: return data.st_ctime;
-    case file::time::modify: return data.st_mtime;
-    case file::time::access: return data.st_atime;
-    }
-  }
-
   bool open() const {
     return fp;
   }
@@ -309,8 +281,9 @@ struct file : varint {
     fp = nullptr;
   }
 
-  file() {
-  }
+  file& operator=(const file&) = delete;
+  file(const file&) = delete;
+  file() = default;
 
   file(const string& filename, mode mode_) {
     open(filename, mode_);
@@ -320,9 +293,6 @@ struct file : varint {
     close();
   }
 
-  file& operator=(const file&) = delete;
-  file(const file&) = delete;
-
 private:
   enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 };
   char buffer[buffer_size] = {0};
diff --git a/nall/function.hpp b/nall/function.hpp
index 77d2f41a..fe7382f1 100644
--- a/nall/function.hpp
+++ b/nall/function.hpp
@@ -1,6 +1,8 @@
 #ifndef NALL_FUNCTION_HPP
 #define NALL_FUNCTION_HPP
 
+#include <nall/traits.hpp>
+
 namespace nall {
 
 template<typename T> class function;
@@ -16,7 +18,7 @@ template<typename R, typename... P> class function<R (P...)> {
 
   struct global : container {
     R (*function)(P...);
-    R operator()(P... p) const { return function(std::forward<P>(p)...); }
+    R operator()(P... p) const { return function(forward<P>(p)...); }
     container* copy() const { return new global(function); }
     global(R (*function)(P...)) : function(function) {}
   };
@@ -24,21 +26,28 @@ template<typename R, typename... P> class function<R (P...)> {
   template<typename C> struct member : container {
     R (C::*function)(P...);
     C* object;
-    R operator()(P... p) const { return (object->*function)(std::forward<P>(p)...); }
+    R operator()(P... p) const { return (object->*function)(forward<P>(p)...); }
     container* copy() const { return new member(function, object); }
     member(R (C::*function)(P...), C* object) : function(function), object(object) {}
   };
 
   template<typename L> struct lambda : container {
     mutable L object;
-    R operator()(P... p) const { return object(std::forward<P>(p)...); }
+    R operator()(P... p) const { return object(forward<P>(p)...); }
     container* copy() const { return new lambda(object); }
     lambda(const L& object) : object(object) {}
   };
 
+  //value = true if R L::operator()(P...) exists
+  template<typename L> struct is_compatible {
+    template<typename T> static const typename is_same<R, decltype(declval<T>().operator()(declval<P>()...))>::type exists(T*);
+    template<typename T> static const false_type exists(...);
+    static constexpr bool value = decltype(exists<L>(0))::value;
+  };
+
 public:
   explicit operator bool() const { return callback; }
-  R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
+  R operator()(P... p) const { return (*callback)(forward<P>(p)...); }
   void reset() { if(callback) { delete callback; callback = nullptr; } }
 
   function& operator=(const function& source) {
@@ -50,12 +59,12 @@ public:
   }
 
   function() = default;
-  function(const function &source) { operator=(source); }
+  function(const function& source) { operator=(source); }
   function(void* function) { if(function) callback = new global((R (*)(P...))function); }
   function(R (*function)(P...)) { callback = new global(function); }
   template<typename C> function(R (C::*function)(P...), C* object) { callback = new member<C>(function, object); }
   template<typename C> function(R (C::*function)(P...) const, C* object) { callback = new member<C>((R (C::*)(P...))function, object); }
-  template<typename L> function(const L& object) { callback = new lambda<L>(object); }
+  template<typename L, typename = enable_if<is_compatible<L>>> function(const L& object) { callback = new lambda<L>(object); }
   ~function() { if(callback) delete callback; }
 };
 
diff --git a/nall/hash/crc16.hpp b/nall/hash/crc16.hpp
new file mode 100644
index 00000000..12c582f4
--- /dev/null
+++ b/nall/hash/crc16.hpp
@@ -0,0 +1,43 @@
+#ifndef NALL_HASH_CRC16_HPP
+#define NALL_HASH_CRC16_HPP
+
+#include <nall/range.hpp>
+
+namespace nall {
+struct string;
+namespace Hash {
+
+struct CRC16 {
+  CRC16() { reset(); }
+  CRC16(const void* values, unsigned size) : CRC16() { data(values, size); }
+
+  auto reset() -> void {
+    checksum = ~0;
+  }
+
+  auto data(uint8_t value) -> void {
+    for(auto n : range(8)) {
+      if((checksum & 1) ^ (value & 1)) checksum = (checksum >> 1) ^ 0x8408;
+      else checksum >>= 1;
+      value >>= 1;
+    }
+  }
+
+  auto data(const void* values, unsigned size) -> void {
+    auto p = (const uint8_t*)values;
+    while(size--) data(*p++);
+  }
+
+  auto value() -> uint16_t {
+    return ~checksum;
+  }
+
+  inline auto digest() -> string;
+
+private:
+  uint16_t checksum;
+};
+
+}}
+
+#endif
diff --git a/nall/crc32.hpp b/nall/hash/crc32.hpp
similarity index 80%
rename from nall/crc32.hpp
rename to nall/hash/crc32.hpp
index 75700101..3ecdd64f 100644
--- a/nall/crc32.hpp
+++ b/nall/hash/crc32.hpp
@@ -1,11 +1,13 @@
-#ifndef NALL_CRC32_HPP
-#define NALL_CRC32_HPP
+#ifndef NALL_HASH_CRC32_HPP
+#define NALL_HASH_CRC32_HPP
 
-#include <nall/stdint.hpp>
+#include <nall/range.hpp>
 
 namespace nall {
+struct string;
+namespace Hash {
 
-const uint32_t crc32_table[256] = {
+const uint32_t _crc32_table[256] = {
   0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
   0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
   0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
@@ -48,21 +50,36 @@ const uint32_t crc32_table[256] = {
   0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
   0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
   0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
 };
 
-inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) {
-  return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff];
-}
+struct CRC32 {
+  CRC32() { reset(); }
+  CRC32(const void* values, unsigned size) : CRC32() { data(values, size); }
 
-inline uint32_t crc32_calculate(const uint8_t* data, unsigned length) {
-  uint32_t crc32 = ~0;
-  for(unsigned i = 0; i < length; i++) {
-    crc32 = crc32_adjust(crc32, data[i]);
+  auto reset() -> void {
+    checksum = ~0;
   }
-  return ~crc32;
-}
 
-}
+  auto data(uint8_t value) -> void {
+    checksum = ((checksum >> 8) & 0xffffff) ^ _crc32_table[(checksum ^ value) & 0xff];
+  }
+
+  auto data(const void* values, unsigned size) -> void {
+    auto p = (const uint8_t*)values;
+    while(size--) data(*p++);
+  }
+
+  auto value() const -> uint32_t {
+    return ~checksum;
+  }
+
+  inline auto digest() -> string;
+
+private:
+  uint32_t checksum;
+};
+
+}}
 
 #endif
diff --git a/nall/hash/sha256.hpp b/nall/hash/sha256.hpp
new file mode 100644
index 00000000..27105407
--- /dev/null
+++ b/nall/hash/sha256.hpp
@@ -0,0 +1,111 @@
+#ifndef NALL_HASH_SHA256_HPP
+#define NALL_HASH_SHA256_HPP
+
+#include <nall/range.hpp>
+
+namespace nall {
+struct string;
+namespace Hash {
+
+struct SHA256 {
+  SHA256() { reset(); }
+  SHA256(const void* values, unsigned size) : SHA256() { data(values, size); }
+
+  auto reset() -> void {
+    for(auto n : input) n = 0;
+    for(auto n : w) n = 0;
+    for(auto n : range(8)) h[n] = square(n);
+    queued = length = 0;
+  }
+
+  auto data(uint8_t value) -> void {
+    byte(value);
+    length++;
+  }
+
+  auto data(const void* values, unsigned size) -> void {
+    length += size;
+    auto p = (const uint8_t*)values;
+    while(size--) byte(*p++);
+  }
+
+  auto value() const -> vector<uint8_t> {
+    SHA256 self(*this);
+    self.finish();
+    vector<uint8_t> result;
+    for(auto n : range(32)) result.append(self.h[n >> 2] >> ((3 - (n & 3)) << 3));
+    return result;
+  }
+
+  inline auto digest() const -> nall::string;
+
+private:
+  auto byte(uint8_t value) -> void {
+    auto shift = (3 - (queued & 3)) * 8;
+    input[queued >> 2] &= ~(0xff << shift);
+    input[queued >> 2] |= (value << shift);
+    if(++queued == 64) block(), queued = 0;
+  }
+
+  auto block() -> void {
+    for(auto n : range(16)) w[n] = input[n];
+    for(auto n : range(16, 64)) {
+      uint32_t a = ror(w[n - 15],  7) ^ ror(w[n - 15], 18) ^ (w[n - 15] >>  3);
+      uint32_t b = ror(w[n -  2], 17) ^ ror(w[n -  2], 19) ^ (w[n -  2] >> 10);
+      w[n] = w[n - 16] + w[n - 7] + a + b;
+    }
+    uint32_t t[8];
+    for(auto n : range(8)) t[n] = h[n];
+    for(auto n : range(64)) {
+      uint32_t a = ror(t[0], 2) ^ ror(t[0], 13) ^ ror(t[0], 22);
+      uint32_t b = ror(t[4], 6) ^ ror(t[4], 11) ^ ror(t[4], 25);
+      uint32_t c = (t[0] & t[1]) ^ (t[0] & t[2]) ^ (t[1] & t[2]);
+      uint32_t d = (t[4] & t[5]) ^ (~t[4] & t[6]);
+      uint32_t e = t[7] + w[n] + cube(n) + b + d;
+      t[7] = t[6]; t[6] = t[5]; t[5] = t[4]; t[4] = t[3] + e;
+      t[3] = t[2]; t[2] = t[1]; t[1] = t[0]; t[0] = a + c + e;
+    }
+    for(auto n : range(8)) h[n] += t[n];
+  }
+
+  auto finish() -> void {
+    byte(0x80);
+    while(queued != 56) byte(0x00);
+    for(auto n : range(8)) byte((length << 3) >> ((7 - n) << 3));
+  }
+
+  auto ror(uint32_t x, uint32_t n) -> uint32_t {
+    return (x >> n) | (x << 32 - n);
+  }
+
+  auto square(unsigned n) -> uint32_t {
+    static const uint32_t value[8] = {
+      0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
+    };
+    return value[n];
+  }
+
+  auto cube(unsigned n) -> uint32_t {
+    static const uint32_t value[64] = {
+      0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+      0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+      0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+      0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+      0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+      0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+      0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+      0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
+    };
+    return value[n];
+  }
+
+  uint32_t input[16];
+  uint32_t queued;
+  uint32_t w[64];
+  uint32_t h[8];
+  uint64_t length;
+};
+
+}}
+
+#endif
diff --git a/nall/http.hpp b/nall/http.hpp
deleted file mode 100644
index 55421867..00000000
--- a/nall/http.hpp
+++ /dev/null
@@ -1,176 +0,0 @@
-#ifndef NALL_HTTP_HPP
-#define NALL_HTTP_HPP
-
-#if !defined(_WIN32)
-  #include <sys/types.h>
-  #include <sys/socket.h>
-  #include <netinet/in.h>
-  #include <netdb.h>
-#else
-  #include <winsock2.h>
-  #include <ws2tcpip.h>
-  #include <windows.h>
-#endif
-
-#include <nall/platform.hpp>
-#include <nall/string.hpp>
-
-namespace nall {
-
-struct http {
-  string hostname;
-  addrinfo* serverinfo;
-  int serversocket;
-  string header;
-
-  inline void download(const string& path, uint8_t*& data, unsigned& size) {
-    data = nullptr;
-    size = 0;
-
-    send({
-      "GET ", path, " HTTP/1.1\r\n"
-      "Host: ", hostname, "\r\n"
-      "Connection: close\r\n"
-      "\r\n"
-    });
-
-    header = downloadHeader();
-    downloadContent(data, size);
-  }
-
-  inline bool connect(string host, unsigned port) {
-    hostname = host;
-
-    addrinfo hints;
-    memset(&hints, 0, sizeof(addrinfo));
-    hints.ai_family = AF_UNSPEC;
-    hints.ai_socktype = SOCK_STREAM;
-    hints.ai_flags = AI_PASSIVE;
-
-    int status = getaddrinfo(hostname, string(port), &hints, &serverinfo);
-    if(status != 0) return false;
-
-    serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol);
-    if(serversocket == -1) return false;
-
-    int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen);
-    if(result == -1) return false;
-
-    return true;
-  }
-
-  inline bool send(const string& data) {
-    return send((const uint8_t*)(const char*)data, data.length());
-  }
-
-  inline bool send(const uint8_t* data, unsigned size) {
-    while(size) {
-      int length = ::send(serversocket, (const char*)data, size, 0);
-      if(length == -1) return false;
-      data += length;
-      size -= length;
-    }
-    return true;
-  }
-
-  inline string downloadHeader() {
-    string output;
-    do {
-      char buffer[2];
-      int length = recv(serversocket, buffer, 1, 0);
-      if(length <= 0) return output;
-      buffer[1] = 0;
-      output.append(buffer);
-    } while(output.endsWith("\r\n\r\n") == false);
-    return output;
-  }
-
-  inline string downloadChunkLength() {
-    string output;
-    do {
-      char buffer[2];
-      int length = recv(serversocket, buffer, 1, 0);
-      if(length <= 0) return output;
-      buffer[1] = 0;
-      output.append(buffer);
-    } while(output.endsWith("\r\n") == false);
-    return output;
-  }
-
-  inline void downloadContent(uint8_t*& data, unsigned& size) {
-    unsigned capacity = 0;
-
-    if(header.ifind("\r\nTransfer-Encoding: chunked\r\n")) {
-      while(true) {
-        unsigned length = hex(downloadChunkLength());
-        if(length == 0) break;
-        capacity += length;
-        data = (uint8_t*)realloc(data, capacity);
-
-        char buffer[length];
-        while(length) {
-          int packetlength = recv(serversocket, buffer, length, 0);
-          if(packetlength <= 0) break;
-          memcpy(data + size, buffer, packetlength);
-          size += packetlength;
-          length -= packetlength;
-        }
-      }
-    } else if(auto position = header.ifind("\r\nContent-Length: ")) {
-      unsigned length = decimal((const char*)header + position() + 18);
-      while(length) {
-        char buffer[256];
-        int packetlength = recv(serversocket, buffer, min(256, length), 0);
-        if(packetlength <= 0) break;
-        capacity += packetlength;
-        data = (uint8_t*)realloc(data, capacity);
-        memcpy(data + size, buffer, packetlength);
-        size += packetlength;
-        length -= packetlength;
-      }
-    } else {
-      while(true) {
-        char buffer[256];
-        int packetlength = recv(serversocket, buffer, 256, 0);
-        if(packetlength <= 0) break;
-        capacity += packetlength;
-        data = (uint8_t*)realloc(data, capacity);
-        memcpy(data + size, buffer, packetlength);
-        size += packetlength;
-      }
-    }
-
-    data = (uint8_t*)realloc(data, capacity + 1);
-    data[capacity] = 0;
-  }
-
-  inline void disconnect() {
-    close(serversocket);
-    freeaddrinfo(serverinfo);
-    serverinfo = nullptr;
-    serversocket = -1;
-  }
-
-  #ifdef _WIN32
-  inline int close(int sock) {
-    return closesocket(sock);
-  }
-
-  inline http() {
-    int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
-      WSADATA wsaData;
-      if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
-        WSACleanup();
-        return;
-      }
-    } else {
-      close(sock);
-    }
-  }
-  #endif
-};
-
-}
-
-#endif
diff --git a/nall/http/client.hpp b/nall/http/client.hpp
new file mode 100644
index 00000000..9c578640
--- /dev/null
+++ b/nall/http/client.hpp
@@ -0,0 +1,59 @@
+#ifndef NALL_HTTP_CLIENT_HPP
+#define NALL_HTTP_CLIENT_HPP
+
+#include <nall/http/role.hpp>
+
+namespace nall {
+
+struct httpClient : httpRole {
+  inline auto open(const string& hostname, unsigned port = 80) -> bool;
+  inline auto upload(const httpRequest& request) -> bool;
+  inline auto download(const httpRequest& request) -> httpResponse;
+  inline auto close() -> void;
+  ~httpClient() { close(); }
+
+private:
+  signed fd = -1;
+  addrinfo* info = nullptr;
+};
+
+auto httpClient::open(const string& hostname, unsigned port) -> bool {
+  addrinfo hint = {0};
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_flags = AI_ADDRCONFIG;
+
+  if(getaddrinfo(hostname, string{port}, &hint, &info) != 0) return close(), false;
+
+  fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
+  if(fd < 0) return close(), false;
+
+  if(connect(fd, info->ai_addr, info->ai_addrlen) < 0) return close(), false;
+  return true;
+}
+
+auto httpClient::upload(const httpRequest& request) -> bool {
+  return httpRole::upload(fd, request);
+}
+
+auto httpClient::download(const httpRequest& request) -> httpResponse {
+  httpResponse response(request);
+  httpRole::download(fd, response);
+  return response;
+}
+
+auto httpClient::close() -> void {
+  if(fd) {
+    ::close(fd);
+    fd = -1;
+  }
+
+  if(info) {
+    freeaddrinfo(info);
+    info = nullptr;
+  }
+}
+
+}
+
+#endif
diff --git a/nall/http/message.hpp b/nall/http/message.hpp
new file mode 100644
index 00000000..569a789e
--- /dev/null
+++ b/nall/http/message.hpp
@@ -0,0 +1,77 @@
+#ifndef NALL_HTTP_MESSAGE_HPP
+#define NALL_HTTP_MESSAGE_HPP
+
+//httpMessage: base class for httpRequest and httpResponse
+//provides shared functionality
+
+namespace nall {
+
+struct httpVariable {
+  string name;
+  string value;
+};
+
+struct httpVariables : vector<httpVariable> {
+  auto append(const string& name, const string& value) -> void;
+  auto get(const string& name) const -> string;
+  auto remove(const string& name) -> void;
+  auto set(const string& name, const string& value) -> void;
+};
+
+auto httpVariables::append(const string& name, const string& value) -> void {
+  vector::append({name, value});
+}
+
+auto httpVariables::get(const string& name) const -> string {
+  for(auto& variable : *this) {
+    if(variable.name.iequals(name)) return variable.value;
+  }
+  return "";
+}
+
+auto httpVariables::remove(const string& name) -> void {
+  while(true) {
+    unsigned n = 0;
+    bool found = false;
+    for(auto& variable : *this) {
+      if(!variable.name.iequals(name)) { n++; continue; }
+      vector::remove(n);
+      found = true;
+      break;
+    }
+    if(found == false) break;
+  }
+}
+
+auto httpVariables::set(const string& name, const string& value) -> void {
+  for(auto& variable : *this) {
+    if(!variable.name.iequals(name)) continue;
+    variable.name = name;
+    variable.value = value;
+    return;
+  }
+  vector::append({name, value});
+}
+
+struct httpMessage {
+  using type = httpMessage;
+
+  virtual auto head(const function<bool (const uint8_t* data, unsigned size)>& callback) const -> bool = 0;
+  virtual auto setHead() -> bool = 0;
+
+  virtual auto body(const function<bool (const uint8_t* data, unsigned size)>& callback) const -> bool = 0;
+  virtual auto setBody() -> bool = 0;
+
+  virtual auto header(const string& name) const -> string { return _header.get(name); }
+  virtual auto appendHeader(const string& name, const string& value = "") -> type& { return _header.append(name, value), *this; }
+  virtual auto removeHeader(const string& name) -> type& { return _header.remove(name), *this; }
+  virtual auto setHeader(const string& name, const string& value = "") -> type& { return _header.set(name, value), *this; }
+
+  string _head;
+  string _body;
+  httpVariables _header;
+};
+
+}
+
+#endif
diff --git a/nall/http/request.hpp b/nall/http/request.hpp
new file mode 100644
index 00000000..934db529
--- /dev/null
+++ b/nall/http/request.hpp
@@ -0,0 +1,146 @@
+#ifndef NALL_HTTP_REQUEST_HPP
+#define NALL_HTTP_REQUEST_HPP
+
+#include <nall/http/message.hpp>
+
+namespace nall {
+
+struct httpRequest : httpMessage {
+  using type = httpRequest;
+
+  enum class RequestType : unsigned { None, Head, Get, Post };
+
+  explicit operator bool() const { return requestType() != RequestType::None; }
+
+  inline auto head(const function<bool (const uint8_t* data, unsigned size)>& callback) const -> bool;
+  inline auto setHead() -> bool;
+
+  inline auto body(const function<bool (const uint8_t* data, unsigned size)>& callback) const -> bool;
+  inline auto setBody() -> bool;
+
+  auto ip() const -> string {
+    return {(uint8_t)(_ip >> 24), ".", (uint8_t)(_ip >> 16), ".", (uint8_t)(_ip >> 8), ".", (uint8_t)(_ip >> 0)};
+  }
+
+  auto requestType() const -> RequestType { return _requestType; }
+  auto setRequestType(RequestType value) -> void { _requestType = value; }
+
+  auto path() const -> string { return _path; }
+  auto setPath(const string& value) -> void { _path = value; }
+
+  auto appendHeader(const string& name, const string& value = "") -> type& { return httpMessage::appendHeader(name, value), *this; }
+  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 post(const string& name) -> string { return _post.get(name); }
+  auto setPost(const string& name, const string& value = "") -> void { return _post.set(name, value); }
+
+//private:
+  uint32_t _ip = 0;
+  RequestType _requestType = RequestType::None;
+  string _path;
+  httpVariables _cookie;
+  httpVariables _get;
+  httpVariables _post;
+};
+
+auto httpRequest::head(const function<bool (const uint8_t*, unsigned)>& callback) const -> bool {
+  if(!callback) return false;
+  string output;
+
+  string request = path();
+  if(_get.size()) {
+    request.append("?");
+    for(auto& get : _get) {
+      request.append(get.name, "=", get.value, "&");
+    }
+    request.rtrim("&");
+  }
+
+  switch(requestType()) {
+  case RequestType::Head: output.append("HEAD ", request, " HTTP/1.1\r\n"); break;
+  case RequestType::Get : output.append("GET ",  request, " HTTP/1.1\r\n"); break;
+  case RequestType::Post: output.append("POST ", request, " HTTP/1.1\r\n"); break;
+  default: return false;
+  }
+
+  for(auto& header : _header) {
+    output.append(header.name, ": ", header.value, "\r\n");
+  }
+  output.append("\r\n");
+
+  return callback(output.binary(), output.size());
+}
+
+auto httpRequest::setHead() -> bool {
+  lstring headers = _head.split("\n");
+  string request = headers.takeFirst().rtrim("\r");
+  string requestHost;
+
+       if(irtrim(request, " HTTP/1.0"));
+  else if(irtrim(request, " HTTP/1.1"));
+  else return false;
+
+       if(iltrim(request, "HEAD ")) setRequestType(RequestType::Head);
+  else if(iltrim(request, "GET " )) setRequestType(RequestType::Get );
+  else if(iltrim(request, "POST ")) setRequestType(RequestType::Post);
+  else return false;
+
+  //decode absolute URIs
+  request.strip().iltrim("http://");
+  if(!request.beginsWith("/")) {
+    lstring components = request.split<1>("/");
+    requestHost = components(0);
+    request = {"/", components(1)};
+  }
+
+  lstring components = request.split<1>("?");
+  setPath(components(0));
+
+  if(auto queryString = components(1)) {
+    for(auto& block : queryString.split("&")) {
+      lstring variable = block.split<1>("=");
+      if(variable(0)) setGet(variable(0), variable(1));
+    }
+  }
+
+  for(auto& header : headers) {
+    if(header.beginsWith(" ") || header.beginsWith("\t")) continue;
+    auto part = header.split<1>(":").strip();
+    if(!part[0] || part.size() != 2) continue;
+    appendHeader(part[0], part[1]);
+  }
+
+  if(requestHost) setHeader("Host", requestHost);  //request URI overrides host header
+  return true;
+}
+
+auto httpRequest::body(const function<bool (const uint8_t*, unsigned)>& callback) const -> bool {
+  if(!callback) return false;
+
+  if(_body) {
+    return callback(_body.binary(), _body.size());
+  }
+
+  return true;
+}
+
+auto httpRequest::setBody() -> bool {
+  if(requestType() == RequestType::Post) {
+    if(header("Content-Type").iequals("application/x-www-form-urlencoded")) {
+      for(auto& block : _body.split("\n")) {
+        lstring variable = block.rtrim("\r").split<1>("=");
+        if(variable(0)) setPost(variable(0), variable(1));
+      }
+    }
+  }
+
+  return true;
+}
+
+}
+
+#endif
diff --git a/nall/http/response.hpp b/nall/http/response.hpp
new file mode 100644
index 00000000..7bca9201
--- /dev/null
+++ b/nall/http/response.hpp
@@ -0,0 +1,253 @@
+#ifndef NALL_HTTP_RESPONSE_HPP
+#define NALL_HTTP_RESPONSE_HPP
+
+#include <nall/http/message.hpp>
+
+namespace nall {
+
+struct httpResponse : httpMessage {
+  using type = httpResponse;
+
+  httpResponse() = default;
+  httpResponse(const httpRequest& request) { setRequest(request); }
+
+  explicit operator bool() const { return responseType() != 0; }
+  auto operator()(unsigned responseType) -> type& { return setResponseType(responseType); }
+
+  inline auto head(const function<bool (const uint8_t* data, unsigned size)>& callback) const -> bool;
+  inline auto setHead() -> bool;
+
+  inline auto body(const function<bool (const uint8_t* data, unsigned size)>& callback) const -> bool;
+  inline auto setBody() -> bool;
+
+  auto request() const -> const httpRequest* { return _request; }
+  auto setRequest(const httpRequest& value) -> type& { _request = &value; return *this; }
+
+  auto responseType() const -> unsigned { return _responseType; }
+  auto setResponseType(unsigned value) -> type& { _responseType = value; return *this; }
+
+  auto appendHeader(const string& name, const string& value = "") -> type& { return httpMessage::appendHeader(name, value), *this; }
+  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 hasData() const -> bool { return (bool)_data; }
+  auto data() const -> const vector<uint8_t>& { return _data; }
+  inline auto setData(const vector<uint8_t>& value) -> type&;
+
+  auto hasFile() const -> bool { return (bool)_file; }
+  auto file() const -> const string& { return _file; }
+  inline auto setFile(const string& value) -> type&;
+
+  auto hasText() const -> bool { return (bool)_text; }
+  auto text() const -> const string& { return _text; }
+  inline auto setText(const string& value) -> type&;
+
+  inline auto hasBody() const -> bool;
+  inline auto findContentLength() const -> unsigned;
+  inline auto findContentType() const -> string;
+  inline auto findContentType(const string& suffix) const -> string;
+  inline auto findResponseType() const -> string;
+  inline auto setFileETag() -> void;
+
+  const httpRequest* _request = nullptr;
+  unsigned _responseType = 0;
+  vector<uint8_t> _data;
+  string _file;
+  string _text;
+};
+
+auto httpResponse::head(const function<bool (const uint8_t*, unsigned)>& callback) const -> bool {
+  if(!callback) return false;
+  string output;
+
+  if(auto request = this->request()) {
+    if(auto eTag = header("ETag")) {
+      if(eTag == request->header("If-None-Match")) {
+        output.append("HTTP/1.1 304 Not Modified\r\n");
+        output.append("Connection: close\r\n");
+        output.append("\r\n");
+        return callback(output.binary(), output.size());
+      }
+    }
+  }
+
+  output.append("HTTP/1.1 ", findResponseType(), "\r\n");
+  for(auto& header : _header) {
+    output.append(header.name, ": ", header.value, "\r\n");
+  }
+  if(hasBody()) {
+    if(!header("Content-Length") && !header("Transfer-Encoding").iequals("chunked")) {
+      output.append("Content-Length: ", findContentLength(), "\r\n");
+    }
+    if(!header("Content-Type")) {
+      output.append("Content-Type: ", findContentType(), "\r\n");
+    }
+  }
+  if(!header("Connection")) {
+    output.append("Connection: close\r\n");
+  }
+  output.append("\r\n");
+
+  return callback(output.binary(), output.size());
+}
+
+auto httpResponse::setHead() -> bool {
+  lstring headers = _head.split("\n");
+  string response = headers.takeFirst().rtrim("\r");
+
+       if(iltrim(response, "HTTP/1.0 "));
+  else if(iltrim(response, "HTTP/1.1 "));
+  else return false;
+
+  setResponseType(decimal(response));
+
+  for(auto& header : headers) {
+    if(header.beginsWith(" ") || header.beginsWith("\t")) continue;
+    lstring variable = header.split<1>(":").strip();
+    if(variable.size() != 2) continue;
+    appendHeader(variable[0], variable[1]);
+  }
+
+  return true;
+}
+
+auto httpResponse::body(const function<bool (const uint8_t*, unsigned)>& callback) const -> bool {
+  if(!callback) return false;
+  if(!hasBody()) return true;
+  bool chunked = header("Transfer-Encoding") == "chunked";
+
+  if(chunked) {
+    string prefix = {hex(findContentLength()), "\r\n"};
+    if(!callback(prefix.binary(), prefix.size())) return false;
+  }
+
+  if(_body) {
+    if(!callback(_body.binary(), _body.size())) return false;
+  } else if(hasData()) {
+    if(!callback(data().data(), data().size())) return false;
+  } else if(hasFile()) {
+    filemap map(file(), filemap::mode::read);
+    if(!callback(map.data(), map.size())) return false;
+  } else if(hasText()) {
+    if(!callback(text().binary(), text().size())) return false;
+  } else {
+    string response = findResponseType();
+    if(!callback(response.binary(), response.size())) return false;
+  }
+
+  if(chunked) {
+    string suffix = {"\r\n0\r\n\r\n"};
+    if(!callback(suffix.binary(), suffix.size())) return false;
+  }
+
+  return true;
+}
+
+auto httpResponse::setBody() -> bool {
+  return true;
+}
+
+auto httpResponse::hasBody() const -> bool {
+  if(auto request = this->request()) {
+    if(request->requestType() == httpRequest::RequestType::Head) return false;
+  }
+  if(responseType() == 301) return false;
+  if(responseType() == 302) return false;
+  if(responseType() == 303) return false;
+  if(responseType() == 304) return false;
+  if(responseType() == 307) return false;
+  return true;
+}
+
+auto httpResponse::findContentLength() const -> unsigned {
+  if(auto contentLength = header("Content-Length")) return decimal(contentLength);
+  if(_body) return _body.size();
+  if(hasData()) return data().size();
+  if(hasFile()) return file::size(file());
+  if(hasText()) return text().size();
+  return findResponseType().size();
+}
+
+auto httpResponse::findContentType() const -> string {
+  if(auto contentType = header("Content-Type")) return contentType;
+  if(hasData()) return "application/octet-stream";
+  if(hasFile()) return findContentType(file().suffixname());
+  return "text/html; charset=utf-8";
+}
+
+auto httpResponse::findContentType(const string& s) const -> string {
+  if(s == ".7z"  ) return "application/x-7z-compressed";
+  if(s == ".avi" ) return "video/avi";
+  if(s == ".bml" ) return "text/plain; charset=utf-8";
+  if(s == ".bz2" ) return "application/x-bzip2";
+  if(s == ".css" ) return "text/css; charset=utf-8";
+  if(s == ".gif" ) return "image/gif";
+  if(s == ".gz"  ) return "application/gzip";
+  if(s == ".htm" ) return "text/html; charset=utf-8";
+  if(s == ".html") return "text/html; charset=utf-8";
+  if(s == ".jpg" ) return "image/jpeg";
+  if(s == ".jpeg") return "image/jpeg";
+  if(s == ".js"  ) return "application/javascript";
+  if(s == ".mka" ) return "audio/x-matroska";
+  if(s == ".mkv" ) return "video/x-matroska";
+  if(s == ".mp3" ) return "audio/mpeg";
+  if(s == ".mp4" ) return "video/mp4";
+  if(s == ".mpeg") return "video/mpeg";
+  if(s == ".mpg" ) return "video/mpeg";
+  if(s == ".ogg" ) return "audio/ogg";
+  if(s == ".pdf" ) return "application/pdf";
+  if(s == ".png" ) return "image/png";
+  if(s == ".rar" ) return "application/x-rar-compressed";
+  if(s == ".svg" ) return "image/svg+xml";
+  if(s == ".tar" ) return "application/x-tar";
+  if(s == ".txt" ) return "text/plain; charset=utf-8";
+  if(s == ".wav" ) return "audio/vnd.wave";
+  if(s == ".webm") return "video/webm";
+  if(s == ".xml" ) return "text/xml; charset=utf-8";
+  if(s == ".xz"  ) return "application/x-xz";
+  if(s == ".zip" ) return "application/zip";
+  return "application/octet-stream";  //binary
+}
+
+auto httpResponse::findResponseType() const -> string {
+  switch(responseType()) {
+  case 200: return "200 OK";
+  case 301: return "301 Moved Permanently";
+  case 302: return "302 Found";
+  case 303: return "303 See Other";
+  case 304: return "304 Not Modified";
+  case 307: return "307 Temporary Redirect";
+  case 400: return "400 Bad Request";
+  case 403: return "403 Forbidden";
+  case 404: return "404 Not Found";
+  case 500: return "500 Internal Server Error";
+  case 501: return "501 Not Implemented";
+  case 503: return "503 Service Unavailable";
+  }
+  return "501 Not Implemented";
+}
+
+auto httpResponse::setData(const vector<uint8_t>& value) -> type& {
+  _data = value;
+  setHeader("Content-Length", value.size());
+  return *this;
+}
+
+auto httpResponse::setFile(const string& value) -> type& {
+  _file = value;
+  string eTag = {"\"", string::datetime(file::timestamp(value, file::time::modify)), "\""};
+  setHeader("Content-Length", file::size(value));
+  setHeader("Cache-Control", "public");
+  setHeader("ETag", eTag);
+  return *this;
+}
+
+auto httpResponse::setText(const string& value) -> type& {
+  _text = value;
+  setHeader("Content-Length", value.size());
+  return *this;
+}
+
+}
+
+#endif
diff --git a/nall/http/role.hpp b/nall/http/role.hpp
new file mode 100644
index 00000000..7a1970e2
--- /dev/null
+++ b/nall/http/role.hpp
@@ -0,0 +1,161 @@
+#ifndef NALL_HTTP_ROLE_HPP
+#define NALL_HTTP_ROLE_HPP
+
+//httpRole: base class for httpClient and httpServer
+//provides shared functionality
+
+#include <nall/http/request.hpp>
+#include <nall/http/response.hpp>
+
+namespace nall {
+
+struct httpRole {
+  struct Settings {
+    signed connectionLimit =   1024;  //server
+    signed headSizeLimit   =  16384;  //client, server
+    signed bodySizeLimit   =  65536;  //client, server
+    signed chunkSize       =  32768;  //client, server
+    signed threadStackSize = 131072;  //server
+    signed timeoutReceive  =  15000;  //server
+    signed timeoutSend     =  15000;  //server
+  } settings;
+
+  inline auto configure(const string& parameters) -> bool;
+  inline auto download(signed fd, httpMessage& message) -> bool;
+  inline auto upload(signed fd, const httpMessage& message) -> bool;
+};
+
+auto httpRole::configure(const string& parameters) -> bool {
+  auto document = Markup::Document(parameters);
+  for(auto& parameter : document) {
+    string& name = parameter.name;
+    signed value = parameter.integer();
+
+    if(0);
+    else if(name == "connectionLimit") settings.connectionLimit = value;
+    else if(name == "headSizeLimit") settings.headSizeLimit = value;
+    else if(name == "bodySizeLimit") settings.bodySizeLimit = value;
+    else if(name == "chunkSize") settings.chunkSize = value;
+    else if(name == "threadStackSize") settings.threadStackSize = value;
+    else if(name == "timeoutReceive") settings.timeoutReceive = value;
+    else if(name == "timeoutSend") settings.timeoutSend = value;
+  }
+  return true;
+}
+
+auto httpRole::download(signed fd, httpMessage& message) -> bool {
+  auto& head = message._head;
+  auto& body = message._body;
+  string chunk;
+  uint8_t packet[settings.chunkSize], *p = nullptr;
+
+  head.reset(), head.reserve(4095);
+  body.reset(), body.reserve(4095);
+
+  bool headReceived = false;
+  bool chunked = false;
+  bool chunkReceived = false;
+  bool chunkFooterReceived = true;
+  signed length = 0;
+  signed chunkLength = 0;
+  signed contentLength = 0;
+
+  while(true) {
+    if(auto limit = settings.headSizeLimit) if(head.size() >= limit) return false;
+    if(auto limit = settings.bodySizeLimit) if(body.size() >= limit) return false;
+
+    if(headReceived && !chunked && body.size() >= contentLength) {
+      body.resize(contentLength);
+      break;
+    }
+
+    if(length == 0) {
+      length = recv(fd, packet, settings.chunkSize, MSG_NOSIGNAL);
+      if(length <= 0) return false;
+      p = packet;
+    }
+
+    if(!headReceived) {
+      head.append((char)*p++);
+      --length;
+
+      if(head.endsWith("\r\n\r\n") || head.endsWith("\n\n")) {
+        headReceived = true;
+        if(!message.setHead()) return false;
+        chunked = message.header("Transfer-Encoding").iequals("chunked");
+        contentLength = decimal(message.header("Content-Length"));
+      }
+
+      continue;
+    }
+
+    if(chunked && !chunkReceived) {
+      char n = *p++;
+      --length;
+
+      if(!chunkFooterReceived) {
+        if(n == '\n') chunkFooterReceived = true;
+        continue;
+      }
+
+      chunk.append(n);
+
+      if(chunk.endsWith("\r\n") || chunk.endsWith("\n")) {
+        chunkReceived = true;
+        chunkLength = hex(chunk);
+        if(chunkLength == 0) break;
+        chunk.reset();
+      }
+
+      continue;
+    }
+
+    if(!chunked) {
+      body.resize(body.size() + length);
+      memory::copy(body.pointer() + body.size() - length, p, length);
+
+      p += length;
+      length = 0;
+    } else {
+      signed transferLength = min(length, chunkLength);
+      body.resize(body.size() + transferLength);
+      memory::copy(body.pointer() + body.size() - transferLength, p, transferLength);
+
+      p += transferLength;
+      length -= transferLength;
+      chunkLength -= transferLength;
+
+      if(chunkLength == 0) {
+        chunkReceived = false;
+        chunkFooterReceived = false;
+      }
+    }
+  }
+
+  if(!message.setBody()) return false;
+  return true;
+}
+
+auto httpRole::upload(signed fd, const httpMessage& message) -> bool {
+  auto transfer = [&](const uint8_t* data, unsigned size) -> bool {
+    while(size) {
+      signed length = send(fd, data, min(size, settings.chunkSize), MSG_NOSIGNAL);
+      if(length < 0) return false;
+      data += length;
+      size -= length;
+    }
+    return true;
+  };
+
+  if(message.head([&](const uint8_t* data, unsigned size) -> bool { return transfer(data, size); })) {
+    if(message.body([&](const uint8_t* data, unsigned size) -> bool { return transfer(data, size); })) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}
+
+#endif
diff --git a/nall/http/server.hpp b/nall/http/server.hpp
new file mode 100644
index 00000000..41a57e07
--- /dev/null
+++ b/nall/http/server.hpp
@@ -0,0 +1,136 @@
+#ifndef NALL_HTTP_SERVER_HPP
+#define NALL_HTTP_SERVER_HPP
+
+#include <poll.h>
+#include <atomic>
+
+#include <nall/service.hpp>
+#include <nall/http/role.hpp>
+
+namespace nall {
+
+struct httpServer : httpRole, service {
+  inline auto open(unsigned port = 8080, const string& serviceName = "", const string& command = "") -> bool;
+  inline auto main(const function<httpResponse (httpRequest&)>& function = {}) -> void;
+  inline auto scan() -> string;
+  inline auto close() -> void;
+  ~httpServer() { close(); }
+
+private:
+  signed fd = -1;
+  function<httpResponse (httpRequest&)> callback;
+  struct sockaddr_in addrin = {0};
+  std::atomic<signed> connections{0};
+};
+
+auto httpServer::open(unsigned port, const string& serviceName, const string& command) -> bool {
+  if(serviceName) {
+    if(!service::command(serviceName, command)) return false;
+  }
+
+  fd = socket(AF_INET, SOCK_STREAM, 0);
+  if(fd < 0) return false;
+
+  {
+  #if defined(SO_RCVTIMEO)
+  if(settings.timeoutReceive) {
+    struct timeval rcvtimeo;
+    rcvtimeo.tv_sec  = settings.timeoutReceive / 1000;
+    rcvtimeo.tv_usec = settings.timeoutReceive % 1000 * 1000;
+    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval));
+  }
+  #endif
+
+  #if defined(SO_SNDTIMEO)
+  if(settings.timeoutSend) {
+    struct timeval sndtimeo;
+    sndtimeo.tv_sec  = settings.timeoutSend / 1000;
+    sndtimeo.tv_usec = settings.timeoutSend % 1000 * 1000;
+    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval));
+  }
+  #endif
+
+  #if defined(SO_NOSIGPIPE)  //BSD, OSX
+  signed nosigpipe = 1;
+  setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(signed));
+  #endif
+
+  #if defined(SO_REUSEADDR)  //BSD, Linux, OSX
+  signed reuseaddr = 1;
+  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(signed));
+  #endif
+
+  #if defined(SO_REUSEPORT)  //BSD, OSX
+  signed reuseport = 1;
+  setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(signed));
+  #endif
+  }
+
+  addrin.sin_family = AF_INET;
+  addrin.sin_addr.s_addr = htonl(INADDR_ANY);
+  addrin.sin_port = htons(port);
+
+  signed result = bind(fd, (struct sockaddr*)&addrin, sizeof(addrin));
+  if(result < 0) return close(), false;
+
+  result = listen(fd, SOMAXCONN);  //system-wide limit (per port)
+  if(result < 0) return close(), false;
+
+  return true;
+}
+
+auto httpServer::main(const function<httpResponse (httpRequest&)>& function) -> void {
+  callback = function;
+}
+
+auto httpServer::scan() -> string {
+  if(auto command = service::receive()) return command;
+
+  if(connections >= settings.connectionLimit) return "busy";
+
+  struct pollfd query = {0};
+  query.fd = fd;
+  query.events = POLLIN;
+  poll(&query, 1, 0);
+
+  if(query.fd == fd && query.revents & POLLIN) {
+    ++connections;
+
+    thread::create([&](uintptr_t) {
+      thread::detach();
+
+      signed clientfd = -1;
+      struct sockaddr_in settings = {0};
+      socklen_t socklen = sizeof(sockaddr_in);
+
+      clientfd = accept(fd, (struct sockaddr*)&settings, &socklen);
+      if(clientfd < 0) return;
+
+      httpRequest request;
+      request._ip = ntohl(settings.sin_addr.s_addr);
+
+      if(download(clientfd, request) && callback) {
+        auto response = callback(request);
+        upload(clientfd, response);
+      } else {
+        upload(clientfd, httpResponse());  //"501 Not Implemented"
+      }
+
+      ::close(clientfd);
+      --connections;
+    }, 0, settings.threadStackSize);
+  }
+
+  return "ok";
+}
+
+auto httpServer::close() -> void {
+  if(fd) {
+    ::close(fd);
+    fd = -1;
+  }
+}
+
+}
+
+#endif
diff --git a/nall/image.hpp b/nall/image.hpp
index 5f39fce5..b1c865ea 100644
--- a/nall/image.hpp
+++ b/nall/image.hpp
@@ -6,9 +6,8 @@
 #include <nall/bmp.hpp>
 #include <nall/filemap.hpp>
 #include <nall/interpolation.hpp>
-#include <nall/png.hpp>
 #include <nall/stdint.hpp>
-
+#include <nall/decode/png.hpp>
 #include <nall/image/base.hpp>
 #include <nall/image/static.hpp>
 #include <nall/image/core.hpp>
diff --git a/nall/image/base.hpp b/nall/image/base.hpp
index 5697ec8d..a530671a 100644
--- a/nall/image/base.hpp
+++ b/nall/image/base.hpp
@@ -47,6 +47,7 @@ struct image {
   static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
 
   //core.hpp
+  inline explicit operator bool() const;
   inline bool operator==(const image& source);
   inline bool operator!=(const image& source);
 
@@ -56,6 +57,7 @@ struct image {
   inline image(image&& source);
   inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
   inline image(const string& filename);
+  inline image(const vector<uint8_t>& buffer);
   inline image(const uint8_t* data, unsigned size);
   inline image();
   inline ~image();
diff --git a/nall/image/core.hpp b/nall/image/core.hpp
index 5ee04042..c0f0a63c 100644
--- a/nall/image/core.hpp
+++ b/nall/image/core.hpp
@@ -3,6 +3,10 @@
 
 namespace nall {
 
+image::operator bool() const {
+  return !empty();
+}
+
 bool image::operator==(const image& source) {
   if(width != source.width) return false;
   if(height != source.height) return false;
@@ -88,6 +92,10 @@ image::image(const string& filename) {
   load(filename);
 }
 
+image::image(const vector<uint8_t>& buffer) {
+  loadPNG(buffer.data(), buffer.size());
+}
+
 image::image(const uint8_t* data, unsigned size) {
   loadPNG(data, size);
 }
diff --git a/nall/image/load.hpp b/nall/image/load.hpp
index 6ac17d80..9c5b6f4e 100644
--- a/nall/image/load.hpp
+++ b/nall/image/load.hpp
@@ -35,7 +35,7 @@ bool image::loadPNG(const string& filename) {
 }
 
 bool image::loadPNG(const uint8_t* pngData, unsigned pngSize) {
-  png source;
+  Decode::PNG source;
   if(source.decode(pngData, pngSize) == false) return false;
 
   allocate(source.info.width, source.info.height);
diff --git a/nall/intrinsics.hpp b/nall/intrinsics.hpp
index da41094b..eadf33e5 100644
--- a/nall/intrinsics.hpp
+++ b/nall/intrinsics.hpp
@@ -15,16 +15,22 @@ struct Intrinsics {
   static inline Endian endian();
 };
 
+}
+
 /* Compiler detection */
 
+namespace nall {
+
 #if defined(__clang__)
   #define COMPILER_CLANG
   Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Clang; }
 
+  #pragma clang diagnostic ignored "-Wunknown-pragmas"
   #pragma clang diagnostic ignored "-Wempty-body"
   #pragma clang diagnostic ignored "-Wparentheses"
   #pragma clang diagnostic ignored "-Wreturn-type"
   #pragma clang diagnostic ignored "-Wswitch"
+  #pragma clang diagnostic ignored "-Wswitch-bool"
   #pragma clang diagnostic ignored "-Wtautological-compare"
 #elif defined(__GNUC__)
   #define COMPILER_GCC
@@ -40,8 +46,12 @@ struct Intrinsics {
   Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; }
 #endif
 
+}
+
 /* Platform detection */
 
+namespace nall {
+
 #if defined(_WIN32)
   #define PLATFORM_WINDOWS
   Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; }
@@ -62,6 +72,8 @@ struct Intrinsics {
   Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Unknown; }
 #endif
 
+}
+
 /* Architecture Detection */
 
 #if defined(PLATFORM_MACOSX)
@@ -72,6 +84,8 @@ struct Intrinsics {
   #include <sys/endian.h>
 #endif
 
+namespace nall {
+
 #if defined(__i386__) || defined(_M_IX86)
   #define ARCH_X86
   Intrinsics::Architecture Intrinsics::architecture() { return Intrinsics::Architecture::x86; }
@@ -84,8 +98,12 @@ struct Intrinsics {
   Intrinsics::Architecture Intrinsics::architecture() { return Intrinsics::Architecture::Unknown; }
 #endif
 
+}
+
 /* Endian detection */
 
+namespace nall {
+
 #if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
   #define ENDIAN_LSB
   Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::LSB; }
diff --git a/nall/invoke.hpp b/nall/invoke.hpp
index 078f7f5b..a697fbe8 100644
--- a/nall/invoke.hpp
+++ b/nall/invoke.hpp
@@ -36,7 +36,7 @@ template<typename... Args> inline void invoke(const string& name, Args&&... args
     const char** argp = argv;
     lstring argl(std::forward<Args>(args)...);
     *argp++ = (const char*)name;
-    for(auto &arg : argl) *argp++ = (const char*)arg;
+    for(auto& arg : argl) *argp++ = (const char*)arg;
     *argp++ = nullptr;
 
     if(execvp(name, (char* const*)argv) < 0) {
diff --git a/nall/main.hpp b/nall/main.hpp
new file mode 100644
index 00000000..98a433ab
--- /dev/null
+++ b/nall/main.hpp
@@ -0,0 +1,29 @@
+#ifndef NALL_MAIN_HPP
+#define NALL_MAIN_HPP
+
+#include <nall/platform.hpp>
+#include <nall/string.hpp>
+
+namespace nall {
+  auto main(lstring arguments) -> void;
+
+  auto main(int argc, char** argv) -> int {
+    #if defined(PLATFORM_WINDOWS)
+    CoInitialize(0);
+    WSAData wsaData = {0};
+    WSAStartup(MAKEWORD(2, 2), &wsaData);
+    utf8_args(argc, argv);
+    #endif
+
+    lstring arguments;
+    for(auto n : range(argc)) arguments.append(argv[n]);
+
+    return main(move(arguments)), EXIT_SUCCESS;
+  }
+}
+
+auto main(int argc, char** argv) -> int {
+  return nall::main(argc, argv);
+}
+
+#endif
diff --git a/nall/maybe.hpp b/nall/maybe.hpp
index 4f806dac..425f98e0 100644
--- a/nall/maybe.hpp
+++ b/nall/maybe.hpp
@@ -6,7 +6,7 @@ namespace nall {
 struct nothing_t {};
 static nothing_t nothing;
 
-template<typename T>
+template<typename T, typename = void>
 class maybe {
   T* value = nullptr;
 
@@ -15,28 +15,32 @@ public:
   maybe(nothing_t) {}
   maybe(const T& source) { operator=(source); }
   maybe(const maybe& source) { operator=(source); }
-  maybe(maybe&& source) { operator=(std::move(source)); }
+  maybe(maybe&& source) { operator=(move(source)); }
   ~maybe() { reset(); }
 
-  maybe& operator=(nothing_t) { reset(); return *this; }
-  maybe& operator=(const T& source) { reset(); value = new T(source); return *this; }
-  maybe& operator=(const maybe& source) { reset(); if(source) value = new T(source()); return *this; }
-  maybe& operator=(maybe&& source) { reset(); value = source.value; source.value = nullptr; return *this; }
+  auto operator=(nothing_t) -> maybe& { reset(); return *this; }
+  auto operator=(const T& source) -> maybe& { reset(); value = new T(source); return *this; }
+  auto operator=(const maybe& source) -> maybe& { reset(); if(source) value = new T(source()); return *this; }
+  auto operator=(maybe&& source) -> maybe& { reset(); value = source.value; source.value = nullptr; return *this; }
 
-  bool operator==(const maybe& source) const {
+  auto operator==(const maybe& source) const -> bool {
     if(value && source.value) return *value == *source.value;
     return !value && !source.value;
   }
-  bool operator!=(const maybe& source) const { return !operator==(source); }
+  auto operator!=(const maybe& source) const -> bool { return !operator==(source); }
 
   explicit operator bool() const { return value; }
-  T& operator()() { assert(value); return *value; }
-  const T& operator()() const { assert(value); return *value; }
-  const T& operator()(const T& invalid) const { if(value) return *value; return invalid; }
+  auto operator->() -> T* { assert(value); return value; }
+  auto operator->() const -> T* { assert(value); return value; }
+  auto operator*() -> T& { assert(value); return *value; }
+  auto operator*() const -> T& { assert(value); return *value; }
+  auto operator()() -> T& { assert(value); return *value; }
+  auto operator()() const -> T& { assert(value); return *value; }
+  auto operator()(const T& invalid) const -> T& { if(value) return *value; return invalid; }
 
-  bool empty() const { return value == nullptr; }
-  void reset() { if(value) { delete value; value = nullptr; } }
-  void swap(maybe& source) { std::swap(value, source.value); }
+  auto empty() const -> bool { return value == nullptr; }
+  auto reset() -> void { if(value) { delete value; value = nullptr; } }
+  auto swap(maybe& source) -> void { std::swap(value, source.value); }
 };
 
 template<typename T>
@@ -48,27 +52,64 @@ public:
   maybe(nothing_t) {}
   maybe(const T& source) { operator=(source); }
   maybe(const maybe& source) { operator=(source); }
-  maybe(maybe&& source) { operator=(std::move(source)); }
+  maybe(maybe&& source) { operator=(move(source)); }
 
-  maybe& operator=(nothing_t) { value = nullptr; return *this; }
-  maybe& operator=(const T& source) { value = (T*)&source; return *this; }
-  maybe& operator=(const maybe& source) { value = source.value; return *this; }
-  maybe& operator=(maybe&& source) { value = source.value; source.value = nullptr; return *this; }
+  auto operator=(nothing_t) -> maybe& { value = nullptr; return *this; }
+  auto operator=(const T& source) -> maybe& { value = (T*)&source; return *this; }
+  auto operator=(const maybe& source) -> maybe& { value = source.value; return *this; }
+  auto operator=(maybe&& source) -> maybe& { value = source.value; source.value = nullptr; return *this; }
 
-  bool operator==(const maybe& source) const {
+  auto operator==(const maybe& source) const -> bool {
     if(value && source.value) return *value == *source.value;
     return !value && !source.value;
   }
-  bool operator!=(const maybe& source) const { return !operator==(source); }
+  auto operator!=(const maybe& source) const -> bool { return !operator==(source); }
 
   explicit operator bool() const { return value; }
-  T& operator()() { assert(value); return *value; }
-  const T& operator()() const { assert(value); return *value; }
-  const T& operator()(const T& invalid) const { if(value) return *value; return invalid; }
+  auto operator->() -> T* { assert(value); return value; }
+  auto operator->() const -> T* { assert(value); return *value; }
+  auto operator*() -> T& { assert(value); return *value; }
+  auto operator*() const -> T& { assert(value); return *value; }
+  auto operator()() -> T& { assert(value); return *value; }
+  auto operator()() const -> T& { assert(value); return *value; }
+  auto operator()(const T& invalid) const -> T& { if(value) return *value; return invalid; }
 
-  bool empty() const { return value == nullptr; }
-  void reset() { value = nullptr; }
-  void swap(maybe& source) { std::swap(value, source.value); }
+  auto empty() const -> bool { return value == nullptr; }
+  auto reset() -> void { value = nullptr; }
+  auto swap(maybe& source) -> void { std::swap(value, source.value); }
+};
+
+template<typename T>
+class maybe<T, enable_if<is_integral<T>>> {
+  T value = 0;
+  bool valid = false;
+
+public:
+  maybe() {}
+  maybe(nothing_t) {}
+  maybe(const T& source) { operator=(source); }
+  maybe(const maybe& source) { operator=(source); }
+  maybe(maybe&& source) { operator=(move(source)); }
+
+  auto operator=(nothing_t) -> maybe& { valid = false; return *this; }
+  auto operator=(const T& source) -> maybe& { valid = true; value = source; return *this; }
+  auto operator=(const maybe& source) -> maybe& { valid = source.valid; value = source.value; return *this; }
+  auto operator=(maybe&& source) -> maybe& { valid = source.valid; value = source.value; source.valid = false; return *this; }
+
+  auto operator==(const maybe& source) const -> bool {
+    if(valid && source.valid) return value == source.value;
+    return !valid && !source.valid;
+  }
+  auto operator!=(const maybe& source) const -> bool { return !operator==(source); }
+
+  explicit operator bool() const { return valid; }
+  auto operator*() const -> T { assert(valid); return value; }
+  auto operator()() const -> T { assert(valid); return value; }
+  auto operator()(const T& invalid) const -> T { if(valid) return value; return invalid; }
+
+  auto empty() const -> bool { return !valid; }
+  auto reset() -> void { valid = false; }
+  auto swap(maybe& source) -> void { std::swap(valid, source.valid); std::swap(value, source.value); }
 };
 
 }
diff --git a/nall/memory.hpp b/nall/memory.hpp
new file mode 100644
index 00000000..17dc6180
--- /dev/null
+++ b/nall/memory.hpp
@@ -0,0 +1,8 @@
+#ifndef NALL_MEMORY_HPP
+#define NALL_MEMORY_HPP
+
+#define NALL_MEMORY_INTERNAL_HPP
+#include <nall/memory/memory.hpp>
+#undef NALL_MEMORY_INTERNAL_HPP
+
+#endif
diff --git a/nall/memory/memory.hpp b/nall/memory/memory.hpp
new file mode 100644
index 00000000..3df31894
--- /dev/null
+++ b/nall/memory/memory.hpp
@@ -0,0 +1,131 @@
+#ifdef NALL_MEMORY_INTERNAL_HPP
+
+#include <nall/algorithm.hpp>
+
+namespace nall {
+
+namespace memory {
+  inline auto allocate(unsigned size) -> void*;
+  inline auto allocate(unsigned size, uint8_t data) -> void*;
+
+  inline auto resize(void* target, unsigned size) -> void*;
+
+  inline auto free(void* target) -> void;
+
+  inline auto compare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed;
+  inline auto compare(const void* target, const void* source, unsigned size) -> signed;
+
+  inline auto icompare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed;
+  inline auto icompare(const void* target, const void* source, unsigned size) -> signed;
+
+  inline auto copy(void* target, unsigned capacity, const void* source, unsigned size) -> void*;
+  inline auto copy(void* target, const void* source, unsigned size) -> void*;
+
+  inline auto move(void* target, unsigned capacity, const void* source, unsigned size) -> void*;
+  inline auto move(void* target, const void* source, unsigned size) -> void*;
+
+  inline auto fill(void* target, unsigned capacity, uint8_t data = 0x00) -> void*;
+}
+
+}
+
+#include <nall/memory/pool.hpp>
+
+namespace nall {
+
+//implementation notes:
+//memcmp, memcpy, memmove have terrible performance on small block sizes (FreeBSD 10.0-amd64)
+//as this library is used extensively by nall/string, and most strings tend to be small,
+//this library hand-codes these functions instead. surprisingly, it's a substantial speedup
+
+auto memory::allocate(unsigned size) -> void* {
+  return malloc(size);
+}
+
+auto memory::allocate(unsigned size, uint8_t data) -> void* {
+  auto result = malloc(size);
+  if(result) fill(result, size, data);
+  return result;
+}
+
+auto memory::resize(void* target, unsigned size) -> void* {
+  return realloc(target, size);
+}
+
+auto memory::free(void* target) -> void {
+  ::free(target);
+}
+
+auto memory::compare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed {
+  auto t = (int8_t*)target;
+  auto s = (int8_t*)source;
+  auto l = min(capacity, size);
+  while(l--) {
+    auto x = *t++;
+    auto y = *s++;
+    if(x != y) return x - y;
+  }
+  return 0;
+}
+
+auto memory::compare(const void* target, const void* source, unsigned size) -> signed {
+  return compare(target, size, source, size);
+}
+
+auto memory::icompare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed {
+  auto t = (int8_t*)target;
+  auto s = (int8_t*)source;
+  auto l = min(capacity, size);
+  while(l--) {
+    auto x = *t++;
+    auto y = *s++;
+    if(x - 'A' < 26) x += 32;
+    if(y - 'A' < 26) y += 32;
+    if(x != y) return x - y;
+  }
+  return 0;
+}
+
+auto memory::icompare(const void* target, const void* source, unsigned size) -> signed {
+  return icompare(target, size, source, size);
+}
+
+auto memory::copy(void* target, unsigned capacity, const void* source, unsigned size) -> void* {
+  auto t = (uint8_t*)target;
+  auto s = (uint8_t*)source;
+  auto l = min(capacity, size);
+  while(l--) *t++ = *s++;
+  return target;
+}
+
+auto memory::copy(void* target, const void* source, unsigned size) -> void* {
+  return copy(target, size, source, size);
+}
+
+auto memory::move(void* target, unsigned capacity, const void* source, unsigned size) -> void* {
+  auto t = (uint8_t*)target;
+  auto s = (uint8_t*)source;
+  auto l = min(capacity, size);
+  if(t < s) {
+    while(l--) *t++ = *s++;
+  } else {
+    t += l;
+    s += l;
+    while(l--) *--t = *--s;
+  }
+  return target;
+}
+
+auto memory::move(void* target, const void* source, unsigned size) -> void* {
+  return move(target, size, source, size);
+}
+
+auto memory::fill(void* target, unsigned capacity, uint8_t data) -> void* {
+  auto t = (uint8_t*)target;
+  while(capacity--) *t++ = data;
+  return target;
+}
+
+}
+
+#endif
diff --git a/nall/memory/pool.hpp b/nall/memory/pool.hpp
new file mode 100644
index 00000000..8d162886
--- /dev/null
+++ b/nall/memory/pool.hpp
@@ -0,0 +1,64 @@
+#ifdef NALL_MEMORY_INTERNAL_HPP
+
+namespace nall {
+namespace memory {
+
+template<unsigned Capacity, unsigned Size>
+struct pool_spsc {
+  signed* list = nullptr;
+  uint8_t* data = nullptr;
+  unsigned slot = 0;
+
+  pool_spsc() {
+    list = (signed*)memory::allocate(Capacity * sizeof(signed));
+    data = (uint8_t*)memory::allocate(Capacity * Size);
+    for(unsigned n = 0; n < Capacity; n++) list[n] = n;
+  }
+
+  ~pool_spsc() {
+    memory::free(list);
+    memory::free(data);
+  }
+
+  auto allocate(unsigned size) -> void* {
+    if(size == 0) return nullptr;
+    if(size > Size) return memory::allocate(size);
+    signed offset = list[slot];
+    if(offset < 0) return memory::allocate(size);
+    list[slot] = -1;
+    slot = (slot + 1) % Capacity;
+    return (void*)(data + offset * Size);
+  }
+
+  auto allocate(unsigned size, uint8_t data) -> void* {
+    auto result = allocate(size);
+    memset(result, data, size);
+    return result;
+  }
+
+  auto resize(void* target, unsigned size) -> void* {
+    if(target == nullptr) return allocate(size);
+    signed offset = ((uint8_t*)target - data) / Size;
+    if(offset < 0 || offset >= Capacity) return memory::resize(target, size);
+    if(size <= Size) return target;
+    slot = (slot - 1) % Capacity;
+    list[slot] = offset;
+    return memory::allocate(size);
+  }
+
+  auto free(void* target) -> void {
+    if(target == nullptr) return;
+    signed offset = ((uint8_t*)target - data) / Size;
+    if(offset < 0 || offset >= Capacity) return memory::free(target);
+    slot = (slot - 1) % Capacity;
+    list[slot] = offset;
+  }
+
+  pool_spsc(const pool_spsc&) = delete;
+  pool_spsc& operator=(const pool_spsc&) = delete;
+};
+
+}
+}
+
+#endif
diff --git a/nall/method.hpp b/nall/method.hpp
new file mode 100644
index 00000000..c79eab0a
--- /dev/null
+++ b/nall/method.hpp
@@ -0,0 +1,59 @@
+#ifndef NALL_METHOD_HPP
+#define NALL_METHOD_HPP
+
+//provides extension-method and chaining-method support to classes
+//extension: class(function, params...);
+//chaining:  class[function](params...)[function](params...);
+
+//usage:
+//struct object : method<object> {
+//  using method::operator[];  //if object::operator[] defined
+//  using method::operator();  //if object::operator() defined
+//};
+
+//note: extension-methods would be obsolete if C++17 introduces unified function call syntax
+//currently proposed as N4165 and N4174
+
+namespace nall {
+
+template<typename T> struct method {
+  template<typename F> struct chain {
+    chain(T& self, const F& f) : self(self), f(f) {}
+    template<typename... P> auto operator()(P&&... p) -> T& {
+      return f(self, std::forward<P>(p)...), self;
+    }
+  private:
+    T& self;
+    const F& f;
+  };
+
+  template<typename F> struct const_chain {
+    const_chain(const T& self, const F& f) : self(self), f(f) {}
+    template<typename... P> auto operator()(P&&... p) const -> const T& {
+      return f(self, std::forward<P>(p)...), self;
+    }
+  private:
+    const T& self;
+    const F& f;
+  };
+
+  template<typename F, typename = enable_if<is_function<F>>>
+  auto operator[](const F& f) -> chain<F> { return chain<F>((T&)*this, f); }
+
+  template<typename F, typename = enable_if<is_function<F>>>
+  auto operator[](const F& f) const -> const_chain<F> { return const_chain<F>((const T&)*this, f); }
+
+  template<typename F, typename... P, typename = enable_if<is_function<F>>>
+  auto operator()(const F& f, P&&... p) -> decltype(f((T&)*this, std::forward<P>(p)...)) {
+    return f((T&)*this, std::forward<P>(p)...);
+  }
+
+  template<typename F, typename... P, typename = enable_if<is_function<F>>>
+  auto operator()(const F& f, P&&... p) const -> decltype(f((const T&)*this, std::forward<P>(p)...)) {
+    return f((const T&)*this, std::forward<P>(p)...);
+  }
+};
+
+}
+
+#endif
diff --git a/nall/mosaic/context.hpp b/nall/mosaic/context.hpp
index ee6b5d53..97719ffa 100644
--- a/nall/mosaic/context.hpp
+++ b/nall/mosaic/context.hpp
@@ -62,12 +62,11 @@ struct context {
 
     lstring list = expression.split(",");
     for(auto& item : list) {
-      item.trim();
+      item.strip();
       if(item.match("f(?*) ?*")) {
-        item.ltrim<1>("f(");
+        item.ltrim("f(");
         lstring part = item.split<1>(") ");
-        lstring args = part[0].split<3>(";");
-        for(auto &item : args) item.trim();
+        lstring args = part[0].split<3>(";").strip();
 
         unsigned length = eval(args(0, "0"));
         unsigned offset = eval(args(1, "0"));
@@ -86,14 +85,14 @@ struct context {
         }
       } else if(item.match("base64*")) {
         unsigned offset = 0;
-        item.ltrim<1>("base64");
+        item.ltrim("base64");
         if(item.match("(?*) *")) {
-          item.ltrim<1>("(");
+          item.ltrim("(");
           lstring part = item.split<1>(") ");
           offset = eval(part[0]);
           item = part(1, "");
         }
-        item.trim();
+        item.strip();
         for(auto& c : item) {
           if(c >= 'A' && c <= 'Z') buffer.append(offset + c - 'A' +  0);
           if(c >= 'a' && c <= 'z') buffer.append(offset + c - 'a' + 26);
@@ -102,8 +101,8 @@ struct context {
           if(c == '_') buffer.append(offset + 63);
         }
       } else if(item.match("file *")) {
-        item.ltrim<1>("file ");
-        item.trim();
+        item.ltrim("file ");
+        item.strip();
         //...
       } else if(item.empty() == false) {
         buffer.append(eval(item));
@@ -116,10 +115,8 @@ struct context {
 
     lstring lines = data.split("\n");
     for(auto& line : lines) {
-      lstring part = line.split<1>(":");
+      lstring part = line.split<1>(":").strip();
       if(part.size() != 2) continue;
-      part[0].trim();
-      part[1].trim();
 
       if(part[0] == "offset") offset = eval(part[1]);
       if(part[0] == "width") width = eval(part[1]);
diff --git a/nall/nall.hpp b/nall/nall.hpp
index 3898aa67..6e4264c4 100644
--- a/nall/nall.hpp
+++ b/nall/nall.hpp
@@ -1,3 +1,11 @@
+/* nall
+ * author: byuu
+ * license: ISC
+ *
+ * nall is a header library that provides both fundamental and useful classes
+ * its goals are portability, consistency, minimalism and reusability
+ */
+
 #ifndef NALL_HPP
 #define NALL_HPP
 
@@ -14,8 +22,6 @@
 #include <nall/bitvector.hpp>
 #include <nall/bmp.hpp>
 #include <nall/config.hpp>
-#include <nall/crc16.hpp>
-#include <nall/crc32.hpp>
 #include <nall/directory.hpp>
 #include <nall/dl.hpp>
 #include <nall/endian.hpp>
@@ -23,35 +29,41 @@
 #include <nall/filemap.hpp>
 #include <nall/function.hpp>
 #include <nall/group.hpp>
-#include <nall/gzip.hpp>
 #include <nall/hashset.hpp>
 #include <nall/hid.hpp>
-#include <nall/http.hpp>
 #include <nall/image.hpp>
-#include <nall/inflate.hpp>
 #include <nall/interpolation.hpp>
 #include <nall/intrinsics.hpp>
 #include <nall/invoke.hpp>
 #include <nall/map.hpp>
 #include <nall/matrix.hpp>
 #include <nall/maybe.hpp>
-#include <nall/png.hpp>
+#include <nall/memory.hpp>
 #include <nall/property.hpp>
 #include <nall/random.hpp>
+#include <nall/range.hpp>
 #include <nall/serializer.hpp>
 #include <nall/set.hpp>
-#include <nall/sha256.hpp>
+#include <nall/shared-pointer.hpp>
 #include <nall/sort.hpp>
 #include <nall/stdint.hpp>
+#include <nall/storage.hpp>
 #include <nall/stream.hpp>
 #include <nall/string.hpp>
 #include <nall/thread.hpp>
 #include <nall/traits.hpp>
-#include <nall/unzip.hpp>
 #include <nall/utility.hpp>
 #include <nall/varint.hpp>
 #include <nall/vector.hpp>
 #include <nall/zip.hpp>
+#include <nall/decode/bmp.hpp>
+#include <nall/decode/gzip.hpp>
+#include <nall/decode/inflate.hpp>
+#include <nall/decode/png.hpp>
+#include <nall/decode/zip.hpp>
+#include <nall/hash/crc16.hpp>
+#include <nall/hash/crc32.hpp>
+#include <nall/hash/sha256.hpp>
 
 #if defined(PLATFORM_WINDOWS)
   #include <nall/windows/registry.hpp>
diff --git a/nall/platform.hpp b/nall/platform.hpp
index d6d85da8..2cc2c0c0 100644
--- a/nall/platform.hpp
+++ b/nall/platform.hpp
@@ -9,13 +9,17 @@ namespace Math {
 }
 
 #if defined(PLATFORM_WINDOWS)
-  //minimum version needed for _wstat64, etc
+  //minimum version needed for _wstat64, AI_ADDRCONFIG, etc
+  #undef  _WIN32_WINNT
+  #define _WIN32_WINNT 0x0601
   #undef  __MSVCRT_VERSION__
-  #define __MSVCRT_VERSION__ 0x0601
+  #define __MSVCRT_VERSION__ _WIN32_WINNT
   #include <nall/windows/utf8.hpp>
 #endif
 
+#include <atomic>
 #include <limits>
+#include <mutex>
 #include <utility>
 
 #include <assert.h>
@@ -27,6 +31,7 @@ namespace Math {
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <fcntl.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -36,13 +41,16 @@ namespace Math {
   #include <direct.h>
   #include <shlobj.h>
   #include <wchar.h>
-  #undef interface
-  #define dllexport __declspec(dllexport)
+  #include <winsock2.h>
+  #include <ws2tcpip.h>
 #else
   #include <dlfcn.h>
   #include <unistd.h>
   #include <pwd.h>
-  #define dllexport
+  #include <grp.h>
+  #include <sys/socket.h>
+  #include <netinet/in.h>
+  #include <netdb.h>
 #endif
 
 #if defined(COMPILER_CL)
@@ -50,26 +58,35 @@ namespace Math {
 #endif
 
 #if defined(PLATFORM_WINDOWS)
-  __declspec(dllimport) int _fileno(FILE*);
+  #undef  interface
+  #define dllexport __declspec(dllexport)
+  #define MSG_NOSIGNAL 0
+  __declspec(dllimport) auto _fileno(FILE*) -> int;
 
-  inline int access(const char* path, int amode) { return _waccess(nall::utf16_t(path), amode); }
-  inline int fileno(FILE* stream) { return _fileno(stream); }
-  inline char* getcwd(char* buf, size_t size) { wchar_t wpath[PATH_MAX] = L""; if(!_wgetcwd(wpath, size)) return nullptr; strcpy(buf, nall::utf8_t(wpath)); return buf; }
-  inline int putenv(char* string) { return _wputenv(nall::utf16_t(string)); }
-  inline char* realpath(const char* file_name, char* resolved_name) { wchar_t wfile_name[PATH_MAX] = L""; if(!_wfullpath(wfile_name, nall::utf16_t(file_name), PATH_MAX)) return nullptr; strcpy(resolved_name, nall::utf8_t(wfile_name)); return resolved_name; }
-  inline int rename(const char* oldname, const char* newname) { return _wrename(nall::utf16_t(oldname), nall::utf16_t(newname)); }
-  inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 1000); }
+  inline auto access(const char* path, int amode) -> int { return _waccess(nall::utf16_t(path), amode); }
+  inline auto fileno(FILE* stream) -> int { return _fileno(stream); }
+  inline auto getcwd(char* buf, size_t size) -> char* { wchar_t wpath[PATH_MAX] = L""; if(!_wgetcwd(wpath, size)) return nullptr; strcpy(buf, nall::utf8_t(wpath)); return buf; }
+  inline auto mkdir(const char* path, int mode) -> int { return _wmkdir(nall::utf16_t(path)); }
+  inline auto putenv(const char* value) -> int { return _wputenv(nall::utf16_t(value)); }
+  inline auto realpath(const char* file_name, char* resolved_name) -> char* { wchar_t wfile_name[PATH_MAX] = L""; if(!_wfullpath(wfile_name, nall::utf16_t(file_name), PATH_MAX)) return nullptr; strcpy(resolved_name, nall::utf8_t(wfile_name)); return resolved_name; }
+  inline auto rename(const char* oldname, const char* newname) -> int { return _wrename(nall::utf16_t(oldname), nall::utf16_t(newname)); }
+  inline auto usleep(unsigned milliseconds) -> void { Sleep(milliseconds / 1000); }
+#else
+  #define dllexport
 #endif
 
 #if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
   #define neverinline   __attribute__((noinline))
   #define alwaysinline  inline __attribute__((always_inline))
+  #define deprecated    __attribute__((deprecated))
 #elif defined(COMPILER_CL)
   #define neverinline   __declspec(noinline)
   #define alwaysinline  inline __forceinline
+  #define deprecated    __declspec(deprecated)
 #else
   #define neverinline
   #define alwaysinline  inline
+  #define deprecated
 #endif
 
 #if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
@@ -78,4 +95,12 @@ namespace Math {
   #define unreachable throw
 #endif
 
+#if defined(COMPILER_GCC) && __GNUC__ == 4 && __GNUC_MINOR__ <= 7
+  //GCC 4.7.x has a bug (#54849) when specifying override with a trailing return type:
+  //auto function() -> return_type override;  //this is the syntax that the C++11 standard requires
+  //auto function() override -> return_type;  //this is the syntax that GCC 4.7.x requires
+  //in order to compile code correctly with both compilers, we disable the override keyword for GCC
+  #define override
+#endif
+
 #endif
diff --git a/nall/range.hpp b/nall/range.hpp
new file mode 100644
index 00000000..54efbb25
--- /dev/null
+++ b/nall/range.hpp
@@ -0,0 +1,48 @@
+#ifndef NALL_RANGE_HPP
+#define NALL_RANGE_HPP
+
+namespace nall {
+
+struct range_t {
+  struct iterator {
+    signed operator*() const { return position; }
+    bool operator!=(const iterator& source) const { return step > 0 ? position < source.position : position > source.position; }
+    iterator& operator++() { position += step; return *this; }
+    iterator(signed position, signed step = 0) : position(position), step(step) {}
+
+  private:
+    signed position;
+    const signed step;
+  };
+
+  const iterator begin() const { return iterator(origin, stride); }
+  const iterator end() const { return iterator(target); }
+
+  signed origin;
+  signed target;
+  signed stride;
+};
+
+inline range_t range(signed size) {
+  return range_t{0, size, 1};
+}
+
+inline range_t range(signed offset, signed size) {
+  return range_t{offset, size, 1};
+}
+
+inline range_t range(signed offset, signed size, signed step) {
+  return range_t{offset, size, step};
+}
+
+inline range_t range_reverse(signed size) {
+  return range_t{size - 1, -1, -1};
+}
+
+template<typename T> inline range_t range(const vector<T>& container) {
+  return range_t{0, (signed)container.size(), 1};
+}
+
+}
+
+#endif
diff --git a/nall/service.hpp b/nall/service.hpp
new file mode 100644
index 00000000..19b9f072
--- /dev/null
+++ b/nall/service.hpp
@@ -0,0 +1,120 @@
+#ifndef NALL_SERVICE_HPP
+#define NALL_SERVICE_HPP
+
+//service model template built on top of shared-memory
+
+#include <signal.h>
+#include <nall/shared-memory.hpp>
+
+namespace nall {
+
+struct service {
+  inline explicit operator bool() const;
+  inline auto command(const string& name, const string& command) -> bool;
+  inline auto receive() -> string;
+  inline auto name() const -> string;
+  inline auto stop() const -> bool;
+
+private:
+  shared_memory shared;
+  string _name;
+  bool _stop = false;
+};
+
+service::operator bool() const {
+  return (bool)shared;
+}
+
+//returns true on new service process creation (false is not necessarily an error)
+auto service::command(const string& name, const string& command) -> bool {
+  if(!name) return false;
+  if(!command) return print("[{0}] usage: {service} command\n"
+    "commands:\n"
+    "  status  : query whether service is running\n"
+    "  start   : start service if it is not running\n"
+    "  stop    : stop service if it is running\n"
+    "  remove  : remove semaphore lock if service crashed\n"
+    "  {value} : send custom command to service\n"
+    "", format{name}), false;
+
+  if(shared.open(name, 4096)) {
+    if(command == "start") {
+      print("[{0}] already started\n", format{name});
+    } else if(command == "status") {
+      print("[{0}] running\n", format{name});
+    }
+    if(auto data = shared.acquire()) {
+      if(command == "stop") print("[{0}] stopped\n", format{name});
+      memory::copy(data, command.data(), min(command.size(), 4096));
+      shared.release();
+    }
+    if(command == "remove") {
+      shared.remove();
+      print("[{0}] removed\n", format{name});
+    }
+    return false;
+  }
+
+  if(command == "start") {
+    if(shared.create(name, 4096)) {
+      print("[{0}] started\n", format{name});
+      auto pid = fork();
+      if(pid == 0) {
+        signal(SIGHUP, SIG_IGN);
+        signal(SIGPIPE, SIG_IGN);
+        _name = name;
+        return true;
+      }
+      shared.close();
+    } else {
+      print("[{0}] start failed ({1})\n", format{name, strerror(errno)});
+    }
+    return false;
+  }
+
+  if(command == "status") {
+    print("[{0}] stopped\n", format{name});
+    return false;
+  }
+
+  return false;
+}
+
+auto service::receive() -> string {
+  string command;
+  if(shared) {
+    if(auto data = shared.acquire()) {
+      if(*data) {
+        command.resize(4095);
+        memory::copy(command.pointer(), data, 4095);
+        memory::fill(data, 4096);
+      }
+      shared.release();
+      if(command == "remove") {
+        _stop = true;
+        return "";
+      } else if(command == "start") {
+        return "";
+      } else if(command == "status") {
+        return "";
+      } else if(command == "stop") {
+        _stop = true;
+        shared.remove();
+        return "";
+      }
+    }
+  }
+  return command;
+}
+
+auto service::name() const -> string {
+  return _name;
+}
+
+auto service::stop() const -> bool {
+  return _stop;
+}
+
+}
+
+#endif
diff --git a/nall/sha256.hpp b/nall/sha256.hpp
deleted file mode 100644
index 2be2d7b2..00000000
--- a/nall/sha256.hpp
+++ /dev/null
@@ -1,147 +0,0 @@
-#ifndef NALL_SHA256_HPP
-#define NALL_SHA256_HPP
-
-//author: vladitx
-
-#include <nall/stdint.hpp>
-
-namespace nall {
-
-#define PTR(t, a) ((t*)(a))
-
-#define SWAP32(x) ((uint32_t)(           \
-  (((uint32_t)(x) & 0x000000ff) << 24) | \
-  (((uint32_t)(x) & 0x0000ff00) <<  8) | \
-  (((uint32_t)(x) & 0x00ff0000) >>  8) | \
-  (((uint32_t)(x) & 0xff000000) >> 24)   \
-))
-
-#define ST32(a, d) *PTR(uint32_t, a) = (d)
-#define ST32BE(a, d) ST32(a, SWAP32(d))
-
-#define LD32(a) *PTR(uint32_t, a)
-#define LD32BE(a) SWAP32(LD32(a))
-
-#define LSL32(x, n) ((uint32_t)(x) << (n))
-#define LSR32(x, n) ((uint32_t)(x) >> (n))
-#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n)))
-
-//first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
-static const uint32_t T_H[8] = {
-  0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
-};
-
-//first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311
-static const uint32_t T_K[64] = {
-  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
-  0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
-  0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
-  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
-  0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
-  0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
-  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
-  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
-};
-
-struct sha256_ctx {
-  uint8_t in[64];
-  unsigned inlen;
-
-  uint32_t w[64];
-  uint32_t h[8];
-  uint64_t len;
-};
-
-inline void sha256_init(sha256_ctx* p) {
-  memset(p, 0, sizeof(sha256_ctx));
-  memcpy(p->h, T_H, sizeof(T_H));
-}
-
-static void sha256_block(sha256_ctx* p) {
-  unsigned i;
-  uint32_t s0, s1;
-  uint32_t a, b, c, d, e, f, g, h;
-  uint32_t t1, t2, maj, ch;
-
-  for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4);
-
-  for(i = 16; i < 64; i++) {
-    s0 = ROR32(p->w[i - 15],  7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15],  3);
-    s1 = ROR32(p->w[i -  2], 17) ^ ROR32(p->w[i -  2], 19) ^ LSR32(p->w[i -  2], 10);
-    p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1;
-  }
-
-  a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3];
-  e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7];
-
-  for(i = 0; i < 64; i++) {
-    s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22);
-    maj = (a & b) ^ (a & c) ^ (b & c);
-    t2 = s0 + maj;
-    s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25);
-    ch = (e & f) ^ (~e & g);
-    t1 = h + s1 + ch + T_K[i] + p->w[i];
-
-    h = g; g = f; f = e; e = d + t1;
-    d = c; c = b; b = a; a = t1 + t2;
-  }
-
-  p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d;
-  p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h;
-
-  //next block
-  p->inlen = 0;
-}
-
-inline void sha256_chunk(sha256_ctx* p, const uint8_t* s, unsigned len) {
-  unsigned l;
-  p->len += len;
-
-  while(len) {
-    l = 64 - p->inlen;
-    l = (len < l) ? len : l;
-
-    memcpy(p->in + p->inlen, s, l);
-    s += l;
-    p->inlen += l;
-    len -= l;
-
-    if(p->inlen == 64) sha256_block(p);
-  }
-}
-
-inline void sha256_final(sha256_ctx* p) {
-  uint64_t len;
-  p->in[p->inlen++] = 0x80;
-
-  if(p->inlen > 56) {
-    memset(p->in + p->inlen, 0, 64 - p->inlen);
-    sha256_block(p);
-  }
-
-  memset(p->in + p->inlen, 0, 56 - p->inlen);
-
-  len = p->len << 3;
-  ST32BE(p->in + 56, len >> 32);
-  ST32BE(p->in + 60, len);
-  sha256_block(p);
-}
-
-inline void sha256_hash(sha256_ctx* p, uint8_t* s) {
-  uint32_t *t = (uint32_t*)s;
-  for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
-}
-
-#undef PTR
-#undef SWAP32
-#undef ST32
-#undef ST32BE
-#undef LD32
-#undef LD32BE
-#undef LSL32
-#undef LSR32
-#undef ROR32
-
-}
-
-#endif
diff --git a/nall/shared-memory.hpp b/nall/shared-memory.hpp
new file mode 100644
index 00000000..ad237613
--- /dev/null
+++ b/nall/shared-memory.hpp
@@ -0,0 +1,157 @@
+#ifndef NALL_SHARED_MEMORY_HPP
+#define NALL_SHARED_MEMORY_HPP
+
+#include <nall/memory.hpp>
+#include <nall/string.hpp>
+
+#include <semaphore.h>
+#include <sys/mman.h>
+
+namespace nall {
+
+struct shared_memory {
+  shared_memory() = default;
+  shared_memory(const shared_memory&) = delete;
+  shared_memory& operator=(const shared_memory&) = delete;
+
+  ~shared_memory() {
+    reset();
+  }
+
+  explicit operator bool() const {
+    return _mode != mode::inactive;
+  }
+
+  auto empty() const -> bool {
+    return _mode == mode::inactive;
+  }
+
+  auto size() const -> unsigned {
+    return _size;
+  }
+
+  auto acquired() const -> bool {
+    return _acquired;
+  }
+
+  auto acquire() -> uint8_t* {
+    if(!acquired()) {
+      sem_wait(_semaphore);
+      _acquired = true;
+    }
+    return _data;
+  }
+
+  auto release() -> void {
+    if(acquired()) {
+      sem_post(_semaphore);
+      _acquired = false;
+    }
+  }
+
+  auto reset() -> void {
+    release();
+    if(_mode == mode::server) return remove();
+    if(_mode == mode::client) return close();
+  }
+
+  auto create(const string& name, unsigned size) -> bool {
+    reset();
+
+    _name = {"/nall::", string{name}.transform("/", ":")};
+    _size = size;
+
+    //O_CREAT | O_EXCL seems to throw ENOENT even when semaphore does not exist ...
+    _semaphore = sem_open(_name, O_CREAT, 0644, 1);
+    if(_semaphore == SEM_FAILED) return remove(), false;
+
+    _descriptor = shm_open(_name, O_CREAT | O_TRUNC | O_RDWR, 0644);
+    if(_descriptor < 0) return remove(), false;
+
+    if(ftruncate(_descriptor, _size) != 0) return remove(), false;
+
+    _data = (uint8_t*)mmap(nullptr, _size, PROT_READ | PROT_WRITE, MAP_SHARED, _descriptor, 0);
+    if(_data == MAP_FAILED) return remove(), false;
+
+    memory::fill(_data, _size);
+
+    _mode = mode::server;
+    return true;
+  }
+
+  auto remove() -> void {
+    if(_data) {
+      munmap(_data, _size);
+      _data = nullptr;
+    }
+
+    if(_descriptor) {
+      ::close(_descriptor);
+      shm_unlink(_name);
+      _descriptor = -1;
+    }
+
+    if(_semaphore) {
+      sem_close(_semaphore);
+      sem_unlink(_name);
+      _semaphore = nullptr;
+    }
+
+    _mode = mode::inactive;
+    _name = "";
+    _size = 0;
+  }
+
+  auto open(const string& name, unsigned size) -> bool {
+    reset();
+
+    _name = {"/nall::", string{name}.transform("/", ":")};
+    _size = size;
+
+    _semaphore = sem_open(_name, 0, 0644);
+    if(_semaphore == SEM_FAILED) return close(), false;
+
+    _descriptor = shm_open(_name, O_RDWR, 0644);
+    if(_descriptor < 0) return close(), false;
+
+    _data = (uint8_t*)mmap(nullptr, _size, PROT_READ | PROT_WRITE, MAP_SHARED, _descriptor, 0);
+    if(_data == MAP_FAILED) return close(), false;
+
+    _mode = mode::client;
+    return true;
+  }
+
+  auto close() -> void {
+    if(_data) {
+      munmap(_data, _size);
+      _data = nullptr;
+    }
+
+    if(_descriptor) {
+      ::close(_descriptor);
+      _descriptor = -1;
+    }
+
+    if(_semaphore) {
+      sem_close(_semaphore);
+      _semaphore = nullptr;
+    }
+
+    _mode = mode::inactive;
+    _name = "";
+    _size = 0;
+  }
+
+private:
+  enum class mode : unsigned { server, client, inactive } _mode = mode::inactive;
+  string _name;
+  sem_t* _semaphore = nullptr;
+  signed _descriptor = -1;
+  uint8_t* _data = nullptr;
+  unsigned _size = 0;
+  bool _acquired = false;
+};
+
+}
+
+#endif
diff --git a/nall/shared-pointer.hpp b/nall/shared-pointer.hpp
new file mode 100644
index 00000000..f3e6271a
--- /dev/null
+++ b/nall/shared-pointer.hpp
@@ -0,0 +1,268 @@
+#ifndef NALL_SHARED_POINTER_HPP
+#define NALL_SHARED_POINTER_HPP
+
+#include <nall/function.hpp>
+#include <nall/maybe.hpp>
+#include <nall/traits.hpp>
+#include <nall/vector.hpp>
+
+namespace nall {
+
+template<typename T> struct shared_pointer;
+
+struct shared_pointer_manager {
+  void* pointer = nullptr;
+  function<void (void*)> deleter;
+  unsigned strong = 0;
+  unsigned weak = 0;
+
+  shared_pointer_manager(void* pointer) : pointer(pointer) {
+  }
+};
+
+template<typename T> struct shared_pointer;
+template<typename T> struct shared_pointer_weak;
+
+template<typename T>
+struct shared_pointer {
+  using type = T;
+  shared_pointer_manager* manager = nullptr;
+
+  template<typename U>
+  struct is_compatible {
+    static constexpr bool value = is_base_of<T, U>::value || is_base_of<U, T>::value;
+  };
+
+  shared_pointer() {
+  }
+
+  shared_pointer(T* source) {
+    operator=(source);
+  }
+
+  shared_pointer(T* source, const function<void (T*)>& deleter) {
+    operator=(source);
+    manager->deleter = [=](void* p) { deleter((T*)p); };
+  }
+
+  shared_pointer(const shared_pointer& source) {
+    operator=(source);
+  }
+
+  shared_pointer(shared_pointer&& source) {
+    operator=(move(source));
+  }
+
+  template<typename U, typename = enable_if<is_compatible<U>>>
+  shared_pointer(const shared_pointer<U>& source) {
+    operator=<U>(source);
+  }
+
+  template<typename U, typename = enable_if<is_compatible<U>>>
+  shared_pointer(shared_pointer<U>&& source) {
+    operator=<U>(move(source));
+  }
+
+  template<typename U, typename = enable_if<is_compatible<U>>>
+  shared_pointer(const shared_pointer_weak<U>& source) {
+    operator=<U>(source);
+  }
+
+  template<typename U, typename = enable_if<is_compatible<U>>>
+  shared_pointer(const shared_pointer<U>& source, T* pointer) {
+    if((bool)source && (T*)source.manager->pointer == pointer) {
+      manager = source.manager;
+      manager->strong++;
+    }
+  }
+
+  ~shared_pointer() {
+    reset();
+  }
+
+  shared_pointer& operator=(T* source) {
+    reset();
+    if(source) {
+      manager = new shared_pointer_manager((void*)source);
+      manager->strong++;
+    }
+    return *this;
+  }
+
+  shared_pointer& operator=(const shared_pointer& source) {
+    if(this != &source) {
+      reset();
+      if((bool)source) {
+        manager = source.manager;
+        manager->strong++;
+      }
+    }
+    return *this;
+  }
+
+  shared_pointer& operator=(shared_pointer&& source) {
+    if(this != &source) {
+      reset();
+      manager = source.manager;
+      source.manager = nullptr;
+    }
+    return *this;
+  }
+
+  template<typename U, typename = enable_if<is_compatible<U>>>
+  shared_pointer& operator=(const shared_pointer<U>& source) {
+    if((uintptr_t)this != (uintptr_t)&source) {
+      reset();
+      if((bool)source) {
+        manager = source.manager;
+        manager->strong++;
+      }
+    }
+    return *this;
+  }
+
+  template<typename U, typename = enable_if<is_compatible<U>>>
+  shared_pointer& operator=(shared_pointer&& source) {
+    if((uintptr_t)this != (uintptr_t)&source) {
+      reset();
+      manager = source.manager;
+      source.manager = nullptr;
+    }
+    return *this;
+  }
+
+  template<typename U, typename = enable_if<is_compatible<U>>>
+  shared_pointer& operator=(const shared_pointer_weak<U>& source) {
+    reset();
+    if((bool)source) {
+      manager = source.manager;
+      manager->strong++;
+    }
+    return *this;
+  }
+
+  T* data() {
+    if(manager) return (T*)manager->pointer;
+    return nullptr;
+  }
+
+  const T* data() const {
+    if(manager) return (T*)manager->pointer;
+    return nullptr;
+  }
+
+  T* operator->() { return data(); }
+  const T* operator->() const { return data(); }
+
+  T& operator*() { return *data(); }
+  const T& operator*() const { return *data(); }
+
+  T& operator()() { return *data(); }
+  const T& operator()() const { return *data(); }
+
+  template<typename U>
+  bool operator==(const shared_pointer<U>& source) const {
+    return manager == source.manager;
+  }
+
+  template<typename U>
+  bool operator!=(const shared_pointer<U>& source) const {
+    return manager != source.manager;
+  }
+
+  explicit operator bool() const {
+    return !empty();
+  }
+
+  bool empty() const {
+    return !manager || !manager->strong;
+  }
+
+  bool unique() const {
+    return manager && manager->strong == 1;
+  }
+
+  void reset() {
+    if(manager && manager->strong) {
+      //pointer may contain weak references; if strong==0 it may destroy manager
+      //as such, we must destroy strong before decrementing it to zero
+      if(manager->strong == 1) {
+        if(manager->deleter) {
+          manager->deleter(manager->pointer);
+        } else {
+          delete (T*)manager->pointer;
+        }
+        manager->pointer = nullptr;
+      }
+      if(--manager->strong == 0) {
+        if(manager->weak == 0) {
+          delete manager;
+        }
+      }
+    }
+    manager = nullptr;
+  }
+
+  template<typename U>
+  shared_pointer<U> cast() {
+    if(auto pointer = dynamic_cast<U*>(data())) {
+      return {*this, pointer};
+    }
+    return {};
+  }
+};
+
+template<typename T>
+struct shared_pointer_weak {
+  using type = T;
+  shared_pointer_manager* manager = nullptr;
+
+  shared_pointer_weak() {
+  }
+
+  shared_pointer_weak(const shared_pointer<T>& source) {
+    operator=(source);
+  }
+
+  shared_pointer_weak& operator=(const shared_pointer<T>& source) {
+    reset();
+    if(manager = source.manager) manager->weak++;
+    return *this;
+  }
+
+  ~shared_pointer_weak() {
+    reset();
+  }
+
+  explicit operator bool() const {
+    return !empty();
+  }
+
+  bool empty() const {
+    return !manager || !manager->strong;
+  }
+
+  shared_pointer<T> acquire() const {
+    return shared_pointer<T>(*this);
+  }
+
+  void reset() {
+    if(manager && --manager->weak == 0) {
+      if(manager->strong == 0) {
+        delete manager;
+      }
+    }
+    manager = nullptr;
+  }
+};
+
+template<typename T>
+struct shared_pointer_new : shared_pointer<T> {
+  template<typename... P>
+  shared_pointer_new(P&&... p) : shared_pointer<T>(new T(forward<P>(p)...)) {
+  }
+};
+
+}
+
+#endif
diff --git a/nall/smtp.hpp b/nall/smtp.hpp
index ce1a02ad..54197e9a 100644
--- a/nall/smtp.hpp
+++ b/nall/smtp.hpp
@@ -273,7 +273,7 @@ string SMTP::contacts(const vector<Information::Contact>& contacts) {
   for(auto& contact : contacts) {
     result.append(this->contact(contact), "; ");
   }
-  result.rtrim<1>("; ");
+  result.rtrim("; ");
   return result;
 }
 
diff --git a/nall/storage.hpp b/nall/storage.hpp
new file mode 100644
index 00000000..3d9b7bb7
--- /dev/null
+++ b/nall/storage.hpp
@@ -0,0 +1,95 @@
+#ifndef NALL_STORAGE_HPP
+#define NALL_STORAGE_HPP
+
+//generic abstraction layer for common storage operations against both files and directories
+//these functions are not recursive; use directory::create() and directory::remove() for recursion
+
+#include <nall/platform.hpp>
+#include <nall/string.hpp>
+
+namespace nall {
+
+struct storage {
+  enum class time : unsigned { access, modify };
+
+  static auto exists(const string& name) -> bool;
+  static auto readable(const string& name) -> bool;
+  static auto writable(const string& name) -> bool;
+  static auto executable(const string& name) -> bool;
+  static auto uid(const string& name) -> unsigned;
+  static auto gid(const string& name) -> unsigned;
+  static auto mode(const string& name) -> unsigned;
+  static auto timestamp(const string& name, storage::time mode = storage::time::modify) -> time_t;
+
+  static auto create(const string& name, unsigned permissions = 0755) -> bool;
+  static auto rename(const string& name, const string& targetname) -> bool;
+  static auto remove(const string& name) -> bool;
+};
+
+inline auto storage::exists(const string& name) -> bool {
+  return access(name, F_OK) == 0;
+}
+
+inline auto storage::readable(const string& name) -> bool {
+  return access(name, R_OK) == 0;
+}
+
+inline auto storage::writable(const string& name) -> bool {
+  return access(name, W_OK) == 0;
+}
+
+inline auto storage::executable(const string& name) -> bool {
+  return access(name, X_OK) == 0;
+}
+
+inline auto storage::uid(const string& name) -> unsigned {
+  struct stat data = {0};
+  stat(name, &data);
+  return data.st_uid;
+}
+
+inline auto storage::gid(const string& name) -> unsigned {
+  struct stat data = {0};
+  stat(name, &data);
+  return data.st_gid;
+}
+
+inline auto storage::mode(const string& name) -> unsigned {
+  struct stat data = {0};
+  stat(name, &data);
+  return data.st_mode;
+}
+
+inline auto storage::timestamp(const string& name, storage::time mode) -> time_t {
+  struct stat data = {0};
+  stat(name, &data);
+  switch(mode) {
+  case storage::time::access: return data.st_atime;
+  case storage::time::modify: return data.st_mtime;
+  }
+  throw;
+}
+
+//returns true if 'name' already exists
+inline auto storage::create(const string& name, unsigned permissions) -> bool {
+  if(storage::exists(name)) return true;
+  if(name.endsWith("/")) return mkdir(name, permissions) == 0;
+  int fd = open(name, O_CREAT | O_EXCL, permissions);
+  if(fd < 0) return false;
+  return close(fd), true;
+}
+
+//returns false if 'name' and 'targetname' are on different file systems (requires copy)
+inline auto storage::rename(const string& name, const string& targetname) -> bool {
+  return ::rename(name, targetname) == 0;
+}
+
+//returns false if 'name' is a directory that is not empty
+inline auto storage::remove(const string& name) -> bool {
+  if(name.endsWith("/")) return rmdir(name) == 0;
+  return unlink(name) == 0;
+}
+
+}
+
+#endif
diff --git a/nall/stream.hpp b/nall/stream.hpp
index 586ccda7..b43ae41e 100644
--- a/nall/stream.hpp
+++ b/nall/stream.hpp
@@ -6,18 +6,16 @@
 
 #include <nall/file.hpp>
 #include <nall/filemap.hpp>
-#include <nall/gzip.hpp>
-#include <nall/http.hpp>
 #include <nall/stdint.hpp>
 #include <nall/string.hpp>
-#include <nall/zip.hpp>
+#include <nall/decode/gzip.hpp>
+#include <nall/decode/zip.hpp>
 
 #define NALL_STREAM_INTERNAL_HPP
 #include <nall/stream/stream.hpp>
 #include <nall/stream/memory.hpp>
 #include <nall/stream/mmap.hpp>
 #include <nall/stream/file.hpp>
-#include <nall/stream/http.hpp>
 #include <nall/stream/gzip.hpp>
 #include <nall/stream/zip.hpp>
 #include <nall/stream/auto.hpp>
diff --git a/nall/stream/auto.hpp b/nall/stream/auto.hpp
index 0e4d8705..a410a975 100644
--- a/nall/stream/auto.hpp
+++ b/nall/stream/auto.hpp
@@ -6,7 +6,6 @@ namespace nall {
 #define autostream(...) (*makestream(__VA_ARGS__))
 
 inline std::unique_ptr<stream> makestream(const string& path) {
-  if(path.ibeginsWith("http://")) return std::unique_ptr<stream>(new httpstream(path, 80));
   if(path.iendsWith(".gz")) return std::unique_ptr<stream>(new gzipstream(filestream{path}));
   if(path.iendsWith(".zip")) return std::unique_ptr<stream>(new zipstream(filestream{path}));
   return std::unique_ptr<stream>(new mmapstream(path));
diff --git a/nall/stream/gzip.hpp b/nall/stream/gzip.hpp
index 4af7a24d..9bccbebe 100644
--- a/nall/stream/gzip.hpp
+++ b/nall/stream/gzip.hpp
@@ -1,7 +1,7 @@
 #ifndef NALL_STREAM_GZIP_HPP
 #define NALL_STREAM_GZIP_HPP
 
-#include <nall/gzip.hpp>
+#include <nall/decode/gzip.hpp>
 
 namespace nall {
 
@@ -14,7 +14,7 @@ struct gzipstream : memorystream {
     uint8_t *data = new uint8_t[size];
     stream.read(data, size);
 
-    gzip archive;
+    Decode::GZIP archive;
     bool result = archive.decompress(data, size);
     delete[] data;
     if(result == false) return;
diff --git a/nall/stream/http.hpp b/nall/stream/http.hpp
deleted file mode 100644
index fb38a770..00000000
--- a/nall/stream/http.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef NALL_STREAM_HTTP_HPP
-#define NALL_STREAM_HTTP_HPP
-
-#include <nall/http.hpp>
-
-namespace nall {
-
-struct httpstream : stream {
-  using stream::read;
-  using stream::write;
-
-  bool seekable() const { return true; }
-  bool readable() const { return true; }
-  bool writable() const { return true; }
-  bool randomaccess() const { return true; }
-
-  unsigned size() const { return psize; }
-  unsigned offset() const { return poffset; }
-  void seek(unsigned offset) const { poffset = offset; }
-
-  uint8_t read() const { return pdata[poffset++]; }
-  void write(uint8_t data) const { pdata[poffset++] = data; }
-
-  uint8_t read(unsigned offset) const { return pdata[offset]; }
-  void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
-
-  httpstream(const string& url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
-    string uri = url;
-    uri.ltrim<1>("http://");
-    lstring part = uri.split<1>("/");
-    part[1] = { "/", part[1] };
-
-    http connection;
-    if(connection.connect(part[0], port) == false) return;
-    connection.download(part[1], pdata, psize);
-  }
-
-  ~httpstream() {
-    if(pdata) delete[] pdata;
-  }
-
-private:
-  mutable uint8_t* pdata;
-  mutable unsigned psize, poffset;
-};
-
-}
-
-#endif
diff --git a/nall/stream/stream.hpp b/nall/stream/stream.hpp
index 6e78b18a..2b10bf17 100644
--- a/nall/stream/stream.hpp
+++ b/nall/stream/stream.hpp
@@ -51,7 +51,6 @@ struct stream {
   string text() const {
     string buffer;
     buffer.resize(size() + 1);
-    buffer[size()] = 0;
     seek(0);
     read((uint8_t*)buffer.data(), size());
     return buffer;
diff --git a/nall/stream/zip.hpp b/nall/stream/zip.hpp
index dfb4767b..c7730897 100644
--- a/nall/stream/zip.hpp
+++ b/nall/stream/zip.hpp
@@ -1,7 +1,7 @@
 #ifndef NALL_STREAM_ZIP_HPP
 #define NALL_STREAM_ZIP_HPP
 
-#include <nall/unzip.hpp>
+#include <nall/decode/zip.hpp>
 
 namespace nall {
 
@@ -14,7 +14,7 @@ struct zipstream : memorystream {
     uint8_t* data = new uint8_t[size];
     stream.read(data, size);
 
-    unzip archive;
+    Decode::ZIP archive;
     if(archive.open(data, size) == false) return;
     delete[] data;
 
diff --git a/nall/string.hpp b/nall/string.hpp
index adf86b31..32f15f4c 100644
--- a/nall/string.hpp
+++ b/nall/string.hpp
@@ -12,34 +12,37 @@
 
 #include <nall/platform.hpp>
 #include <nall/atoi.hpp>
-#include <nall/crc32.hpp>
 #include <nall/function.hpp>
 #include <nall/intrinsics.hpp>
-#include <nall/sha256.hpp>
+#include <nall/memory.hpp>
+#include <nall/method.hpp>
 #include <nall/stdint.hpp>
 #include <nall/utility.hpp>
 #include <nall/varint.hpp>
 #include <nall/vector.hpp>
-
-#include <nall/windows/utf8.hpp>
+#include <nall/hash/crc16.hpp>
+#include <nall/hash/crc32.hpp>
+#include <nall/hash/sha256.hpp>
 
 #define NALL_STRING_INTERNAL_HPP
-#include <nall/string/char.hpp>
 #include <nall/string/base.hpp>
 #include <nall/string/ref.hpp>
 #include <nall/string/cast.hpp>
+#include <nall/string/compare.hpp>
+#include <nall/string/convert.hpp>
 #include <nall/string/core.hpp>
 #include <nall/string/datetime.hpp>
-#include <nall/string/file.hpp>
-#include <nall/string/filename.hpp>
+#include <nall/string/find.hpp>
 #include <nall/string/format.hpp>
+#include <nall/string/hash.hpp>
 #include <nall/string/list.hpp>
+#include <nall/string/match.hpp>
+#include <nall/string/path.hpp>
 #include <nall/string/platform.hpp>
 #include <nall/string/replace.hpp>
 #include <nall/string/split.hpp>
+#include <nall/string/trim.hpp>
 #include <nall/string/utility.hpp>
-#include <nall/string/variadic.hpp>
-#include <nall/string/wrapper.hpp>
 #include <nall/string/eval/node.hpp>
 #include <nall/string/eval/literal.hpp>
 #include <nall/string/eval/parser.hpp>
@@ -48,6 +51,8 @@
 #include <nall/string/markup/bml.hpp>
 #include <nall/string/markup/xml.hpp>
 #include <nall/string/markup/document.hpp>
+#include <nall/string/transform/cml.hpp>
+#include <nall/string/transform/dml.hpp>
 #undef NALL_STRING_INTERNAL_HPP
 
 #endif
diff --git a/nall/string/allocator/adaptive.hpp b/nall/string/allocator/adaptive.hpp
new file mode 100644
index 00000000..0a9f75fe
--- /dev/null
+++ b/nall/string/allocator/adaptive.hpp
@@ -0,0 +1,123 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+/*****
+  adaptive allocator
+  sizeof(string) == SSO + 8
+
+  aggressively tries to avoid heap allocations
+  small strings are stored on the stack
+  large strings are shared via copy-on-write
+
+  SSO alone is very slow on large strings due to copying
+  SSO alone is very slightly faster than this allocator on small strings
+
+  COW alone is very slow on small strings due to heap allocations
+  COW alone is very slightly faster than this allocator on large strings
+
+  adaptive is thus very fast for all string sizes
+*****/
+
+namespace nall {
+
+string::string() : _data(nullptr), _capacity(SSO - 1), _size(0) {
+}
+
+auto string::pointer() -> char* {
+  if(_capacity < SSO) return _text;
+  if(*_refs > 1) _copy();
+  return _data;
+}
+
+auto string::data() const -> const char* {
+  if(_capacity < SSO) return _text;
+  return _data;
+}
+
+auto string::reset() -> type& {
+  if(_capacity >= SSO && !--*_refs) memory::free(_data);
+  _data = nullptr;
+  _capacity = SSO - 1;
+  _size = 0;
+  return *this;
+}
+
+auto string::reserve(unsigned capacity) -> type& {
+  if(capacity <= _capacity) return *this;
+  capacity = bit::round(capacity + 1) - 1;
+  if(_capacity < SSO) {
+    _capacity = capacity;
+    _allocate();
+  } else if(*_refs > 1) {
+    _capacity = capacity;
+    _copy();
+  } else {
+    _capacity = capacity;
+    _resize();
+  }
+  return *this;
+}
+
+auto string::resize(unsigned size) -> type& {
+  reserve(size);
+  pointer()[_size = size] = 0;
+  return *this;
+}
+
+auto string::operator=(const string& source) -> type& {
+  if(&source == this) return *this;
+  reset();
+  if(source._capacity >= SSO) {
+    _data = source._data;
+    _refs = source._refs;
+    _capacity = source._capacity;
+    _size = source._size;
+    ++*_refs;
+  } else {
+    memory::copy(_text, source._text, SSO);
+    _capacity = source._capacity;
+    _size = source._size;
+  }
+  return *this;
+}
+
+auto string::operator=(string&& source) -> type& {
+  if(&source == this) return *this;
+  reset();
+  memory::copy(this, &source, sizeof(string));
+  source._data = nullptr;
+  source._capacity = SSO - 1;
+  source._size = 0;
+  return *this;
+}
+
+//SSO -> COW
+auto string::_allocate() -> void {
+  char _temp[SSO];
+  memory::copy(_temp, _text, SSO);
+  _data = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
+  memory::copy(_data, _temp, SSO);
+  _refs = (unsigned*)(_data + _capacity + 1);  //always aligned by 32 via reserve()
+  *_refs = 1;
+}
+
+//COW -> Unique
+auto string::_copy() -> void {
+  auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
+  memory::copy(_temp, _data, _size = min(_capacity, _size));
+  _temp[_size] = 0;
+  --*_refs;
+  _data = _temp;
+  _refs = (unsigned*)(_data + _capacity + 1);
+  *_refs = 1;
+}
+
+//COW -> Resize
+auto string::_resize() -> void {
+  _data = (char*)memory::resize(_data, _capacity + 1 + sizeof(unsigned));
+  _refs = (unsigned*)(_data + _capacity + 1);
+  *_refs = 1;
+}
+
+}
+
+#endif
diff --git a/nall/string/allocator/copy-on-write.hpp b/nall/string/allocator/copy-on-write.hpp
index 3890ce4c..574dde97 100644
--- a/nall/string/allocator/copy-on-write.hpp
+++ b/nall/string/allocator/copy-on-write.hpp
@@ -1,102 +1,90 @@
 #ifdef NALL_STRING_INTERNAL_HPP
 
-/*
-copy on write (COW) allocator
-sizeof(string) == 24 (amd64)
-
-utilizes a shared_ptr to reference count strings
-allows string copies to execute as fast as string moves
-requires extra computations, which will be slower for all string sizes
-
-pros:
-* lower memory usage
-* pass-by-value does not require heap allocation; obviates pass-by-const-reference
-
-cons:
-* added overhead to fetch data()
-* added heap allocation for reference-count pool
-* no potential for in-place resize (always copies)
-* larger sizeof(string)
-
-*/
-
 namespace nall {
 
-char* string::data() {
-  if(!_data.unique()) _copy();
-  return _data.get();
+string::string() : _data(nullptr), _refs(nullptr), _capacity(0), _size(0) {
 }
 
-const char* string::data() const {
-  if(!_data) return "";
-  return _data.get();
+auto string::pointer() -> char* {
+  static char _null[] = "";
+  if(!_data) return _null;
+  if(*_refs > 1) _data = _copy();  //make unique for write operations
+  return _data;
 }
 
-//copy _data (to make unique or to grow in size)
-void string::_copy() {
-  auto copy = new char[_capacity + 1];
-  if(_data.get()) memcpy(copy, _data.get(), min(_capacity, _size));
-  copy[_size] = 0;
-  copy[_capacity] = 0;
-  _data.reset(copy);
+auto string::data() const -> const char* {
+  static const char _null[] = "";
+  if(!_data) return _null;
+  return _data;
 }
 
-//amortize growth to O(log n)
-//allocate one extra byte to always store null-terminator for libc usage
-void string::reserve(unsigned capacity) {
-  if(capacity > _capacity) {
-    _capacity = bit::round(capacity + 1) - 1;
-    _copy();
+auto string::reset() -> type& {
+  if(_data && !--*_refs) {
+    memory::free(_data);
+    _data = nullptr;  //_refs = nullptr; is unnecessary
   }
-}
-
-void string::resize(unsigned size) {
-  reserve(size);
-  data()[_size = size] = 0;
-}
-
-void string::reset() {
-  _data.reset();
   _capacity = 0;
   _size = 0;
-}
-
-string& string::operator=(const string& source) {
-  if(&source == this) return *this;
-  reset();
-  _data = source._data;
-  _capacity = source._capacity;
-  _size = source._size;
   return *this;
 }
 
-string& string::operator=(string&& source) {
+auto string::reserve(unsigned capacity) -> type& {
+  if(capacity > _capacity) {
+    _capacity = bit::round(max(31u, capacity) + 1) - 1;
+    _data = _data ? _copy() : _allocate();
+  }
+  return *this;
+}
+
+auto string::resize(unsigned size) -> type& {
+  reserve(size);
+  pointer()[_size = size] = 0;
+  return *this;
+}
+
+auto string::operator=(const string& source) -> string& {
   if(&source == this) return *this;
   reset();
-  _data = std::move(source._data);
+  if(source._data) {
+    _data = source._data;
+    _refs = source._refs;
+    _capacity = source._capacity;
+    _size = source._size;
+    ++*_refs;
+  }
+  return *this;
+}
+
+auto string::operator=(string&& source) -> string& {
+  if(&source == this) return *this;
+  reset();
+  _data = source._data;
+  _refs = source._refs;
   _capacity = source._capacity;
   _size = source._size;
+  source._data = nullptr;
+  source._refs = nullptr;
   source._capacity = 0;
   source._size = 0;
   return *this;
 }
 
-template<typename T, typename... Args> string::string(T&& source, Args&&... args) {
-  construct();
-  sprint(*this, std::forward<T>(source), std::forward<Args>(args)...);
+auto string::_allocate() -> char* {
+  auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
+  *_temp = 0;
+  _refs = (unsigned*)(_temp + _capacity + 1);  //this will always be aligned by 32 via reserve()
+  *_refs = 1;
+  return _temp;
 }
 
-string::string() {
-  construct();
-}
-
-string::~string() {
-  reset();
-}
-
-void string::construct() {
-  _capacity = 0;
-  _size = 0;
+auto string::_copy() -> char* {
+  auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
+  memory::copy(_temp, _data, _size = min(_capacity, _size));
+  _temp[_size] = 0;
+  --*_refs;
+  _refs = (unsigned*)(_temp + _capacity + 1);
+  *_refs = 1;
+  return _temp;
 }
 
 }
diff --git a/nall/string/allocator/small-string-optimization.hpp b/nall/string/allocator/small-string-optimization.hpp
index 8e3c4117..0430abbf 100644
--- a/nall/string/allocator/small-string-optimization.hpp
+++ b/nall/string/allocator/small-string-optimization.hpp
@@ -2,7 +2,7 @@
 
 /*
 small string optimization (SSO) allocator
-sizeof(string) == 16 (amd64)
+sizeof(string) == 8 + string::SSO
 
 utilizes a union to store small strings directly into text pointer
 bypasses the need to allocate heap memory for small strings
@@ -10,102 +10,86 @@ requires extra computations, which can be slower for large strings
 
 pros:
 * potential for in-place resize
-* no heap allocation when (capacity < 8)
+* no heap allocation when (capacity < SSO)
 
 cons:
 * added overhead to fetch data()
-* 32-bit platforms limited to (capacity < 4)
-* pass-by-value requires heap allocation
+* pass-by-value requires heap allocation when (capacity >= SSO)
 
 */
 
 namespace nall {
 
-char* string::data() {
-  if(_capacity < SSO) return _text;
-  return _data;
-}
-
-const char* string::data() const {
-  if(_capacity < SSO) return _text;
-  return _data;
-}
-
-void string::reserve(unsigned capacity) {
-  if(capacity > _capacity) {
-    if(capacity >= SSO) {
-      capacity = bit::round(capacity + 1) - 1;
-      if(_capacity < SSO) {
-        char temp[SSO];
-        memcpy(temp, _text, SSO);
-        _data = (char*)malloc(capacity + 1);
-        memcpy(_data, temp, SSO);
-      } else {
-        _data = (char*)realloc(_data, capacity + 1);
-      }
-    }
-    _capacity = capacity;
-    data()[_capacity] = 0;
-  }
-}
-
-void string::resize(unsigned size) {
-  reserve(size);
-  data()[_size = size] = 0;
-}
-
-void string::reset() {
-  if(_capacity >= SSO) free(_data);
+string::string() {
   _data = nullptr;
   _capacity = SSO - 1;
   _size = 0;
 }
 
-string& string::operator=(const string& source) {
-  if(&source == this) return *this;
-  reset();
-  if(source._capacity >= SSO) {
-    _data = (char*)malloc(source._capacity + 1);
-    _capacity = source._capacity;
-    _size = source._size;
-    memcpy(_data, source.data(), source.size() + 1);
+auto string::pointer() -> char* {
+  if(_capacity < SSO) return _text;
+  return _data;
+}
+
+auto string::data() const -> const char* {
+  if(_capacity < SSO) return _text;
+  return _data;
+}
+
+auto string::reset() -> type& {
+  if(_capacity >= SSO) memory::free(_data);
+  _data = nullptr;
+  _capacity = SSO - 1;
+  _size = 0;
+  return *this;
+}
+
+auto string::reserve(unsigned capacity) -> type& {
+  if(capacity <= _capacity) return *this;
+  capacity = bit::round(capacity + 1) - 1;
+  if(_capacity < SSO) {
+    char _temp[SSO];
+    memory::copy(_temp, _text, SSO);
+    _data = (char*)memory::allocate(_capacity = capacity + 1);
+    memory::copy(_data, _temp, SSO);
   } else {
-    memcpy(_text, source._text, SSO);
-    _capacity = SSO - 1;
-    _size = strlen(_text);
+    _data = (char*)memory::resize(_data, _capacity = capacity + 1);
   }
   return *this;
 }
 
-string& string::operator=(string&& source) {
+auto string::resize(unsigned size) -> type& {
+  reserve(size);
+  pointer()[_size = size] = 0;
+  return *this;
+}
+
+auto string::operator=(const string& source) -> type& {
   if(&source == this) return *this;
   reset();
-  memcpy(this, &source, sizeof(string));
+  if(source._capacity >= SSO) {
+    _data = (char*)memory::allocate(source._capacity + 1);
+    _capacity = source._capacity;
+    _size = source._size;
+    memory::copy(_data, source._data, source._size + 1);
+  } else {
+    memory::copy(_text, source._text, SSO);
+    _capacity = SSO - 1;
+    _size = source._size;
+  }
+  return *this;
+}
+
+auto string::operator=(string&& source) -> type& {
+  if(&source == this) return *this;
+  reset();
+  memory::copy(this, &source, sizeof(string));
   source._data = nullptr;
   source._capacity = SSO - 1;
   source._size = 0;
   return *this;
 }
 
-template<typename T, typename... Args> string::string(T&& source, Args&&... args) {
-  construct();
-  sprint(*this, std::forward<T>(source), std::forward<Args>(args)...);
-}
-
-string::string() {
-  construct();
-}
-
-string::~string() {
-  reset();
-}
-
-void string::construct() {
-  _data = nullptr;
-  _capacity = SSO - 1;
-  _size = 0;
-}
-
 }
 
 #endif
diff --git a/nall/string/allocator/vector.hpp b/nall/string/allocator/vector.hpp
index 7b7aaaaf..856221a5 100644
--- a/nall/string/allocator/vector.hpp
+++ b/nall/string/allocator/vector.hpp
@@ -19,46 +19,49 @@ cons:
 
 namespace nall {
 
-char* string::data() {
+auto string::pointer() -> char* {
   if(_capacity == 0) reserve(1);
   return _data;
 }
 
-const char* string::data() const {
+auto string::data() const -> const char* {
   if(_capacity == 0) return "";
   return _data;
 }
 
-void string::reserve(unsigned capacity) {
-  if(capacity > _capacity) {
-    _capacity = bit::round(capacity + 1) - 1;
-    _data = (char*)realloc(_data, _capacity + 1);
-    _data[_capacity] = 0;
-  }
-}
-
-void string::resize(unsigned size) {
-  reserve(size);
-  data()[_size = size] = 0;
-}
-
-void string::reset() {
-  if(_data) { free(_data); _data = nullptr; }
+auto string::reset() -> type& {
+  if(_data) { memory::free(_data); _data = nullptr; }
   _capacity = 0;
   _size = 0;
-}
-
-string& string::operator=(const string& source) {
-  if(&source == this) return *this;
-  reset();
-  _data = (char*)malloc(source._size + 1);
-  _capacity = source._size;
-  _size = source._size;
-  memcpy(_data, source.data(), source.size() + 1);
   return *this;
 }
 
-string& string::operator=(string&& source) {
+auto string::reserve(unsigned capacity) -> type& {
+  if(capacity > _capacity) {
+    _capacity = bit::round(capacity + 1) - 1;
+    _data = (char*)memory::resize(_data, _capacity + 1);
+    _data[_capacity] = 0;
+  }
+  return *this;
+}
+
+auto string::resize(unsigned size) -> type& {
+  reserve(size);
+  pointer()[_size = size] = 0;
+  return *this;
+}
+
+auto string::operator=(const string& source) -> type& {
+  if(&source == this) return *this;
+  reset();
+  _data = (char*)memory::allocate(source._size + 1);
+  _capacity = source._size;
+  _size = source._size;
+  memory::copy(_data, source.data(), source.size() + 1);
+  return *this;
+}
+
+auto string::operator=(string&& source) -> type& {
   if(&source == this) return *this;
   reset();
   _data = source._data;
@@ -70,20 +73,7 @@ string& string::operator=(string&& source) {
   return *this;
 }
 
-template<typename T, typename... Args> string::string(T&& source, Args&&... args) {
-  construct();
-  sprint(*this, std::forward<T>(source), std::forward<Args>(args)...);
-}
-
 string::string() {
-  construct();
-}
-
-string::~string() {
-  reset();
-}
-
-void string::construct() {
   _data = nullptr;
   _capacity = 0;
   _size = 0;
diff --git a/nall/string/base.hpp b/nall/string/base.hpp
index 8da9d880..178a77dd 100644
--- a/nall/string/base.hpp
+++ b/nall/string/base.hpp
@@ -3,19 +3,165 @@
 namespace nall {
 
 struct string;
+struct format;
 struct stringref;
 struct lstring;
-typedef const stringref& rstring;
+using cstring = const string&;
+using rstring = const stringref&;
 
+#define NALL_STRING_ALLOCATOR_ADAPTIVE
 //#define NALL_STRING_ALLOCATOR_COPY_ON_WRITE
-#define NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION
+//#define NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION
 //#define NALL_STRING_ALLOCATOR_VECTOR
 
+//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;
+template<signed precision = 0, char padchar = '0'> inline auto integer(intmax_t value) -> string;
+template<signed precision = 0, char padchar = '0'> inline auto decimal(uintmax_t value) -> string;
+template<signed precision = 0, char padchar = '0'> inline auto hex(uintmax_t value) -> string;
+template<signed precision = 0, char padchar = '0'> inline auto octal(uintmax_t value) -> string;
+template<signed precision = 0, char padchar = '0'> inline auto binary(uintmax_t value) -> string;
+template<signed precision = 0, typename T> inline auto pointer(const T* value) -> string;
+template<signed precision = 0> inline auto pointer(uintptr_t value) -> 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;
+
+//match.hpp
+inline auto match(const string& self, rstring source) -> bool;
+inline auto imatch(const string& self, rstring source) -> 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 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;
+
+//platform.hpp
+inline auto activepath() -> string;
+inline auto realpath(rstring name) -> string;
+inline auto programpath() -> string;
+inline auto userpath() -> string;
+inline auto configpath() -> string;
+inline auto sharedpath() -> string;
+inline auto temppath() -> string;
+
+//replace.hpp
+template<unsigned L, bool I, bool Q> inline auto _replace(string& self, rstring from, rstring to) -> string&;
+template<unsigned L = ~0u> inline auto replace(string& self, rstring from, rstring to) -> string&;
+template<unsigned L = ~0u> inline auto ireplace(string& self, rstring from, rstring to) -> string&;
+template<unsigned L = ~0u> inline auto qreplace(string& self, rstring from, rstring to) -> string&;
+template<unsigned L = ~0u> inline auto iqreplace(string& self, rstring from, rstring to) -> string&;
+
+//split.hpp
+template<unsigned L, bool I, bool Q> inline auto _split(lstring& self, rstring source, rstring find) -> lstring&;
+template<unsigned L = ~0u> inline auto split(string& self, rstring key) -> lstring;
+template<unsigned L = ~0u> inline auto isplit(string& self, rstring key) -> lstring;
+template<unsigned L = ~0u> inline auto qsplit(string& self, rstring key) -> lstring;
+template<unsigned L = ~0u> inline auto iqsplit(string& self, rstring key) -> lstring;
+
+//trim.hpp
+inline auto trim(string& self, rstring lhs, rstring rhs) -> bool;
+inline auto ltrim(string& self, rstring lhs) -> bool;
+inline auto rtrim(string& self, rstring rhs) -> bool;
+
+inline auto itrim(string& self, rstring lhs, rstring rhs) -> bool;
+inline auto iltrim(string& self, rstring lhs) -> bool;
+inline auto irtrim(string& self, rstring rhs) -> bool;
+
+inline auto strip(string& self) -> bool;
+inline auto lstrip(string& self) -> bool;
+inline auto rstrip(string& self) -> bool;
+
+//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 integer(char* result, intmax_t value) -> char*;
+inline auto decimal(char* result, uintmax_t value) -> char*;
+inline auto real(char* str, long double value) -> unsigned;
+
 struct string {
+  using type = string;
+  struct exception_out_of_bounds{};
+
 protected:
+  #if defined(NALL_STRING_ALLOCATOR_ADAPTIVE)
+  enum : unsigned { SSO = 24 };
+  union {
+    struct {  //copy-on-write
+      char* _data;
+      unsigned* _refs;
+    };
+    struct {  //small-string-optimization
+      char _text[SSO];
+    };
+  };
+  inline auto _allocate() -> void;
+  inline auto _copy() -> void;
+  inline auto _resize() -> void;
+  #endif
+
   #if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
-  inline void _copy();
-  std::shared_ptr<char> _data;
+  char* _data;
+  mutable unsigned* _refs;
+  inline auto _allocate() -> char*;
+  inline auto _copy() -> char*;
   #endif
 
   #if defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION)
@@ -34,194 +180,213 @@ protected:
   unsigned _size;
 
 public:
+  inline string();
+  inline auto pointer() -> char*;
+  inline auto data() const -> const char*;
+  inline auto reset() -> type&;
+  inline auto reserve(unsigned) -> type&;
+  inline auto resize(unsigned) -> type&;
+  inline auto operator=(const string&) -> type&;
+  inline auto operator=(string&&) -> type&;
+
+  template<typename T, typename... P> string(T&& s, P&&... p) : string() { append(std::forward<T>(s), std::forward<P>(p)...); }
+  ~string() { reset(); }
+
+  explicit operator bool() const { return _size; }
+  operator const uint8_t*() const { return (const uint8_t*)data(); }
+  operator const char*() const { return (const char*)data(); }
+
+  auto binary() const -> const uint8_t* { return (const uint8_t*)data(); }
+  auto size() const -> unsigned { return _size; }
+  auto capacity() const -> unsigned { return _capacity; }
+
+  auto operator==(const string& s) const -> bool { return size() == s.size() && memory::compare(data(), s.data(), s.size()) == 0; }
+  auto operator!=(const string& s) const -> bool { return size() != s.size() || memory::compare(data(), s.data(), s.size()) != 0; }
+
+  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 char* s) const -> bool { return strcmp(data(), s) <  0; }
+  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 char* s) const -> bool { return strcmp(data(), s) >= 0; }
+
+  string(const string& source) : string() { operator=(source); }
+  string(string&& source) : string() { operator=(std::move(source)); }
+
+  auto begin() -> char* { return &pointer()[0]; }
+  auto end() -> char* { return &pointer()[size()]; }
+  auto begin() const -> const char* { return &data()[0]; }
+  auto end() const -> const char* { return &data()[size()]; }
+
+  //nall/atoi.hpp
+  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); }
+
   //core.hpp
-  inline char* data();
-  inline const char* data() const;
-  inline unsigned length() const;
-  inline unsigned size() const;
-  inline unsigned capacity() const;
-  inline bool empty() const;
-
-  inline void reset();
-  inline void reserve(unsigned);
-  inline void resize(unsigned);
-  inline void clear(char);
-
-  inline unsigned hash() const;
-
-  template<typename... Args> inline string& assign(Args&&... args);
-  template<typename... Args> inline string& append(Args&&... args);
-
-  //file.hpp
-  inline static string read(const string& filename);
+  inline auto operator[](signed) const -> const char&;
 
   //datetime.hpp
-  inline static string date();
-  inline static string time();
-  inline static string datetime();
+  inline static auto date(time_t = 0) -> string;
+  inline static auto time(time_t = 0) -> string;
+  inline static auto datetime(time_t = 0) -> string;
 
-  //replace.hpp
-  template<unsigned Limit = 0> inline string& replace(rstring, rstring);
-  template<unsigned Limit = 0> inline string& ireplace(rstring, rstring);
-  template<unsigned Limit = 0> inline string& qreplace(rstring, rstring);
-  template<unsigned Limit = 0> inline string& iqreplace(rstring, rstring);
+  //format.hpp
+  inline auto format(const nall::format& params) -> type&;
 
-  //wrapper.hpp
-  template<unsigned Limit = 0> inline lstring split(rstring) const;
-  template<unsigned Limit = 0> inline lstring isplit(rstring) const;
-  template<unsigned Limit = 0> inline lstring qsplit(rstring) const;
-  template<unsigned Limit = 0> inline lstring iqsplit(rstring) const;
+  //utility.hpp
+  inline static auto read(const string& filename) -> string;
+  template<unsigned L> inline static auto repeat(const string& pattern) -> string;
 
-  inline signed compare(rstring) const;
-  inline signed icompare(rstring) const;
+  //extension methods
+  //=================
 
-  inline bool equals(rstring) const;
-  inline bool iequals(rstring) const;
+  //compare.hpp
+  auto compare(rstring source) const -> signed { return nall::compare(*this, source); }
+  auto icompare(rstring source) const -> signed { return nall::compare(*this, source); }
 
-  inline bool match(rstring) const;
-  inline bool imatch(rstring) const;
+  auto equals(rstring source) const -> bool { return nall::equals(*this, source); }
+  auto iequals(rstring source) const -> bool { return nall::iequals(*this, source); }
 
-  inline bool beginsWith(rstring) const;
-  inline bool ibeginsWith(rstring) const;
-  inline bool endsWith(rstring) const;
-  inline bool iendsWith(rstring) const;
+  auto beginsWith(rstring source) const -> bool { return nall::beginsWith(*this, source); }
+  auto ibeginsWith(rstring source) const -> bool { return nall::ibeginsWith(*this, source); }
 
-  inline string slice(unsigned offset, unsigned length = ~0u) const;
+  auto endsWith(rstring source) const -> bool { return nall::endsWith(*this, source); }
+  auto iendsWith(rstring source) const -> bool { return nall::iendsWith(*this, source); }
 
-  inline string& lower();
-  inline string& upper();
-  inline string& qlower();
-  inline string& qupper();
-  inline string& transform(rstring before, rstring after);
-  inline string& reverse();
+  //convert.hpp
+  auto downcase() -> type& { return nall::downcase(*this); }
+  auto upcase() -> type& { return nall::upcase(*this); }
 
-  template<unsigned limit = 0> inline string& ltrim() { return ltrim<limit>(" "); }
-  template<unsigned limit = 0> inline string& ltrim(rstring key);
+  auto qdowncase() -> type& { return nall::qdowncase(*this); }
+  auto qupcase() -> type& { return nall::qupcase(*this); }
 
-  template<unsigned limit = 0> inline string& rtrim() { return rtrim<limit>(" "); }
-  template<unsigned limit = 0> inline string& rtrim(rstring key);
-
-  template<unsigned limit = 0> inline string& trim() { return trim<limit>(" "); }
-  template<unsigned limit = 0> inline string& trim(rstring key);
-  template<unsigned limit = 0> inline string& trim(rstring key, rstring rkey);
-
-  inline string& strip();
-
-  inline maybe<unsigned> find(rstring key) const;
-  inline maybe<unsigned> ifind(rstring key) const;
-  inline maybe<unsigned> qfind(rstring key) const;
-  inline maybe<unsigned> iqfind(rstring key) const;
+  auto transform(rstring from, rstring to) -> type& { return nall::transform(*this, from, to); }
 
   //core.hpp
-  inline explicit operator bool() const;
-  inline operator const char*() const;
-  inline char& operator[](signed);
-  inline const char& operator[](signed) const;
+  template<typename... P> auto assign(P&&... p) -> type& { return nall::assign(*this, std::forward<P>(p)...); }
+  template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, std::forward<P>(p)...); }
+  auto empty() const -> bool { return nall::empty(*this); }
+  auto length() const -> unsigned { return nall::length(*this); }
 
-  inline bool operator==(const char*) const;
-  inline bool operator!=(const char*) const;
-  inline bool operator< (const char*) const;
-  inline bool operator<=(const char*) const;
-  inline bool operator> (const char*) const;
-  inline bool operator>=(const char*) const;
+  //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); }
 
-  inline string& operator=(const string&);
-  inline string& operator=(string&&);
+  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); }
 
-  template<typename T, typename... Args> inline string(T&& source, Args&&... args);
-  inline string();
-  inline string(const string&);
-  inline string(string&&);
-  inline ~string();
+  //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 char* begin() { return &data()[0]; }
-  inline char* end() { return &data()[size()]; }
-  inline const char* begin() const { return &data()[0]; }
-  inline const char* end() const { return &data()[size()]; }
+  //match.hpp
+  auto match(rstring source) const -> bool { return nall::match(*this, source); }
+  auto imatch(rstring source) const -> bool { return nall::imatch(*this, source); }
 
-//protected:
-  struct exception_out_of_bounds{};
-  template<unsigned Limit, bool Insensitive, bool Quoted> inline string& ureplace(rstring, rstring);
-  inline string& _append(const char*);
+  //path.hpp
+  auto pathname() const -> string { return nall::pathname(*this); }
+  auto filename() const -> string { return nall::filename(*this); }
 
-private:
-  inline void construct();
+  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); }
 
-#if defined(QSTRING_H)
-public:
+  //replace.hpp
+  template<unsigned L = ~0u> auto replace(rstring from, rstring to) -> type& { return nall::_replace<L, 0, 0>(*this, from, to); }
+  template<unsigned L = ~0u> auto ireplace(rstring from, rstring to) -> type& { return nall::_replace<L, 1, 0>(*this, from, to); }
+  template<unsigned L = ~0u> auto qreplace(rstring from, rstring to) -> type& { return nall::_replace<L, 0, 1>(*this, from, to); }
+  template<unsigned L = ~0u> auto iqreplace(rstring from, rstring to) -> type& { return nall::_replace<L, 1, 1>(*this, from, to); }
+
+  //split.hpp
+  template<unsigned L = ~0u> inline auto split(rstring key) const -> lstring;
+  template<unsigned L = ~0u> inline auto isplit(rstring key) const -> lstring;
+  template<unsigned L = ~0u> inline auto qsplit(rstring key) const -> lstring;
+  template<unsigned L = ~0u> inline auto iqsplit(rstring key) const -> lstring;
+
+  //trim.hpp
+  auto trim(rstring lhs, rstring rhs) -> type& { return nall::trim(*this, lhs, rhs), *this; }
+  auto ltrim(rstring lhs) -> type& { return nall::ltrim(*this, lhs), *this; }
+  auto rtrim(rstring rhs) -> type& { return nall::rtrim(*this, rhs), *this; }
+
+  auto itrim(rstring lhs, rstring rhs) -> type& { return nall::itrim(*this, lhs, rhs), *this; }
+  auto iltrim(rstring lhs) -> type& { return nall::iltrim(*this, lhs), *this; }
+  auto irtrim(rstring rhs) -> type& { return nall::irtrim(*this, rhs), *this; }
+
+  auto strip() -> type& { return nall::strip(*this), *this; }
+  auto lstrip() -> type& { return nall::lstrip(*this), *this; }
+  auto rstrip() -> type& { return nall::rstrip(*this), *this; }
+
+  //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
+  #endif
 };
 
 //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 merge(const lstring& self, const string& separator) -> string;
+inline auto strip(lstring& self) -> lstring&;
+
 struct lstring : vector<string> {
-  inline maybe<unsigned> find(rstring) const;
-  inline string merge(const string&) const;
-  inline lstring& isort();
-  inline lstring& strip();
-  inline void append() {}
-  template<typename... Args> inline void append(const string&, Args&&...);
+  using type = lstring;
 
-  inline bool operator==(const lstring&) const;
-  inline bool operator!=(const lstring&) const;
+  lstring(const lstring& source) { vector::operator=(source); }
+  lstring(lstring& source) { vector::operator=(source); }
+  lstring(lstring&& source) { vector::operator=(std::move(source)); }
+  template<typename... P> lstring(P&&... p) { append(std::forward<P>(p)...); }
 
-  inline lstring& operator=(const lstring&);
-  inline lstring& operator=(lstring&);
-  inline lstring& operator=(lstring&&);
+  //list.hpp
+  inline auto operator==(const lstring&) const -> bool;
+  inline auto operator!=(const lstring&) const -> bool;
 
-  template<typename... Args> inline lstring(Args&&... args);
-  inline lstring(const lstring&);
-  inline lstring(lstring&);
-  inline lstring(lstring&&);
+  inline auto operator=(const lstring& source) -> type& { return vector::operator=(source), *this; }
+  inline auto operator=(lstring& source) -> type& { return vector::operator=(source), *this; }
+  inline auto operator=(lstring&& source) -> type& { return vector::operator=(std::move(source)), *this; }
+
+  inline auto isort() -> type&;
+
+  //extension methods
+  //=================
+
+  //list.hpp
+  template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, std::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 merge(const string& separator) const -> string { return nall::merge(*this, separator); }
+  auto strip() -> type& { return nall::strip(*this); }
 
   //split.hpp
-  template<unsigned Limit = 0> inline lstring& split(rstring, rstring);
-  template<unsigned Limit = 0> inline lstring& isplit(rstring, rstring);
-  template<unsigned Limit = 0> inline lstring& qsplit(rstring, rstring);
-  template<unsigned Limit = 0> inline lstring& iqsplit(rstring, rstring);
-
-protected:
-  template<unsigned Limit, bool Insensitive, bool Quoted> inline lstring& usplit(rstring, rstring);
+  template<unsigned L = ~0u> auto split(rstring source, rstring on) -> type& { return nall::_split<L, 0, 0>(*this, source, on); }
+  template<unsigned L = ~0u> auto isplit(rstring source, rstring on) -> type& { return nall::_split<L, 1, 0>(*this, source, on); }
+  template<unsigned L = ~0u> auto qsplit(rstring source, rstring on) -> type& { return nall::_split<L, 0, 1>(*this, source, on); }
+  template<unsigned L = ~0u> auto iqsplit(rstring source, rstring on) -> type& { return nall::_split<L, 1, 1>(*this, source, on); }
 };
 
-//filename.hpp
-inline string dir(string name);
-inline string notdir(string name);
-inline string parentdir(string name);
-inline string basename(string name);
-inline string extension(string name);
-inline string tempname();
-
 //format.hpp
-template<signed precision = 0, char padchar = ' '> inline string format(const string& value);
-template<signed precision = 0, char padchar = '0'> inline string hex(uintmax_t value);
-template<signed precision = 0, char padchar = '0'> inline string octal(uintmax_t value);
-template<signed precision = 0, char padchar = '0'> inline string binary(uintmax_t value);
+template<typename T, typename... P> inline auto append(format& self, const T& value, P&&... p) -> format&;
+inline auto append(format& self) -> format&;
 
-//platform.hpp
-inline string activepath();
-inline string realpath(const string& name);
-inline string programpath();
-inline string userpath();
-inline string configpath();
-inline string sharedpath();
-inline string temppath();
+struct format : vector<string> {
+  using type = format;
 
-//utility.hpp
-inline string substr(rstring source, unsigned offset = 0, unsigned length = ~0u);
-inline string sha256(const uint8_t* data, unsigned size);
-inline bool tokenize(lstring& list, const char* s, const char* p);
-
-inline char* integer(char* result, intmax_t value);
-inline char* decimal(char* result, uintmax_t value);
-
-inline unsigned real(char* str, long double value);
-inline string real(long double value);
-
-//variadic.hpp
-inline void sprint(string& output);
-template<typename T, typename... Args> inline void sprint(string& output, const T& value, Args&&... args);
-template<typename... Args> inline void print(Args&&... args);
+  template<typename... P> format(P&&... p) { reserve(sizeof...(p)); append(std::forward<P>(p)...); }
+  template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, std::forward<P>(p)...); }
+};
 
 }
 
diff --git a/nall/string/cast.hpp b/nall/string/cast.hpp
index ef79a47d..5b7d13f0 100644
--- a/nall/string/cast.hpp
+++ b/nall/string/cast.hpp
@@ -1,185 +1,209 @@
 #ifdef NALL_STRING_INTERNAL_HPP
 
+//convert any (supported) type to a const char* without constructing a new nall::string
+//this is used inside string{...} to build nall::string values
+
 namespace nall {
 
-//convert any (supported) type to a const char* without constructing a new nall::string
-//this is used inside istring(...) to build nall::string values
-template<typename T> struct stringify;
-
-// base types
+//base types
 
 template<> struct stringify<bool> {
-  bool value;
-  operator const char*() const { return value ? "true" : "false"; }
-  stringify(bool value) : value(value) {}
+  bool _value;
+  auto data() const -> const char* { return _value ? "true" : "false"; }
+  auto size() const -> unsigned { return _value ? 4 : 5; }
+  stringify(bool value) : _value(value) {}
 };
 
 template<> struct stringify<char> {
-  char data[2];
-  operator const char*() const { return data; }
-  stringify(char value) { data[0] = value, data[1] = 0; }
+  char _data[2];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return 1; }
+  stringify(char source) { _data[0] = source; _data[1] = 0; }
 };
 
-// signed integers
+//signed integers
 
 template<> struct stringify<signed char> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(signed char value) { integer(data, value); }
+  char _data[2 + sizeof(signed char) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(signed char source) { integer(_data, source); }
 };
 
 template<> struct stringify<signed short> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(signed short value) { integer(data, value); }
+  char _data[2 + sizeof(signed short) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(signed short source) { integer(_data, source); }
 };
 
 template<> struct stringify<signed int> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(signed int value) { integer(data, value); }
+  char _data[2 + sizeof(signed int) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(signed int source) { integer(_data, source); }
 };
 
 template<> struct stringify<signed long> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(signed long value) { integer(data, value); }
+  char _data[2 + sizeof(signed long) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(signed long source) { integer(_data, source); }
 };
 
 template<> struct stringify<signed long long> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(signed long long value) { integer(data, value); }
+  char _data[2 + sizeof(signed long long) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(signed long long source) { integer(_data, source); }
 };
 
 template<unsigned bits> struct stringify<int_t<bits>> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(int_t<bits> value) { integer(data, value); }
+  char _data[2 + sizeof(intmax_t) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(int_t<bits> source) { integer(_data, source); }
 };
 
-// unsigned integers
+//unsigned integers
 
 template<> struct stringify<unsigned char> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(unsigned char value) { decimal(data, value); }
+  char _data[1 + sizeof(unsigned char) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(unsigned char source) { decimal(_data, source); }
 };
 
 template<> struct stringify<unsigned short> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(unsigned short value) { decimal(data, value); }
+  char _data[1 + sizeof(unsigned short) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(unsigned short source) { decimal(_data, source); }
 };
 
 template<> struct stringify<unsigned int> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(unsigned int value) { decimal(data, value); }
+  char _data[1 + sizeof(unsigned int) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(unsigned int source) { decimal(_data, source); }
 };
 
 template<> struct stringify<unsigned long> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(unsigned long value) { decimal(data, value); }
+  char _data[1 + sizeof(unsigned long) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(unsigned long source) { decimal(_data, source); }
 };
 
 template<> struct stringify<unsigned long long> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(unsigned long long value) { decimal(data, value); }
+  char _data[1 + sizeof(unsigned long long) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(unsigned long long source) { decimal(_data, source); }
 };
 
 template<unsigned bits> struct stringify<uint_t<bits>> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(uint_t<bits> value) { decimal(data, value); }
+  char _data[1 + sizeof(uintmax_t) * 3];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(uint_t<bits> source) { decimal(_data, source); }
 };
 
-// floating-point
+//floating-point
 
 template<> struct stringify<float> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(float value) { real(data, value); }
+  char _data[256];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(float source) { real(_data, source); }
 };
 
 template<> struct stringify<double> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(double value) { real(data, value); }
+  char _data[256];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(double source) { real(_data, source); }
 };
 
 template<> struct stringify<long double> {
-  char data[256];
-  operator const char*() const { return data; }
-  stringify(long double value) { real(data, value); }
+  char _data[256];
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(long double source) { real(_data, source); }
 };
 
-// arrays
+//arrays
 
 template<> struct stringify<vector<uint8_t>> {
-  char* text;
-  operator const char*() const { return text; }
-  stringify(vector<uint8_t> value) {
-    text = new char[value.size() + 1]();
-    memcpy(text, value.data(), value.size());
-  }
-  ~stringify() {
-    delete[] text;
+  vector<char> _text;
+  auto data() const -> const char* { return _text.data(); }
+  auto size() const -> unsigned { return _text.size(); }
+  stringify(vector<uint8_t> source) {
+    _text.resize(source.size() + 1);
+    memory::copy(_text.data(), source.data(), source.size());
+    _text[_text.size()] = 0;
   }
 };
 
 template<> struct stringify<const vector<uint8_t>&> {
-  char* text;
-  operator const char*() const { return text; }
-  stringify(const vector<uint8_t>& value) {
-    text = new char[value.size() + 1]();
-    memcpy(text, value.data(), value.size());
-  }
-  ~stringify() {
-    delete[] text;
+  vector<char> _text;
+  auto data() const -> const char* { return _text.data(); }
+  auto size() const -> unsigned { return _text.size(); }
+  stringify(const vector<uint8_t>& source) {
+    _text.resize(source.size() + 1);
+    memory::copy(_text.data(), source.data(), source.size());
+    _text[_text.size()] = 0;
   }
 };
 
-// strings
+//char arrays
 
 template<> struct stringify<char*> {
-  const char* value;
-  operator const char*() const { return value; }
-  stringify(char* value) : value(value) {}
+  const char* _data;
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(char* source) : _data(source) {}
 };
 
 template<> struct stringify<const char*> {
-  const char* value;
-  operator const char*() const { return value; }
-  stringify(const char* value) : value(value) {}
+  const char* _data;
+  auto data() const -> const char* { return _data; }
+  auto size() const -> unsigned { return strlen(_data); }
+  stringify(const char* source) : _data(source) {}
 };
 
+//strings
+
 template<> struct stringify<string> {
-  const string& value;
-  operator const char*() const { return value; }
-  stringify(const string& value) : value(value) {}
+  const string& _text;
+  auto data() const -> const char* { return _text.data(); }
+  auto size() const -> unsigned { return _text.size(); }
+  stringify(const string& source) : _text(source) {}
 };
 
 template<> struct stringify<const string&> {
-  const string& value;
-  operator const char*() const { return value; }
-  stringify(const string& value) : value(value) {}
+  const string& _text;
+  auto data() const -> const char* { return _text.data(); }
+  auto size() const -> unsigned { return _text.size(); }
+  stringify(const string& source) : _text(source) {}
 };
 
 #if defined(QSTRING_H)
 
+//Qt
+
 template<> struct stringify<QString> {
-  const QString& value;
-  operator const char*() const { return value.toUtf8().constData(); }
-  stringify(const QString& value) : value(value) {}
+  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 QString&> {
-  const QString& value;
-  operator const char*() const { return value.toUtf8().constData(); }
-  stringify(const QString& value) : value(value) {}
+  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) {}
 };
 
 string::operator QString() const {
@@ -190,7 +214,7 @@ string::operator QString() const {
 
 //
 
-template<typename T> stringify<T> make_string(T value) {
+template<typename T> auto make_string(T value) -> stringify<T> {
   return stringify<T>(std::forward<T>(value));
 }
 
diff --git a/nall/string/char.hpp b/nall/string/char.hpp
deleted file mode 100644
index 29ac2c77..00000000
--- a/nall/string/char.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-#include <nall/string/char/base.hpp>
-#include <nall/string/char/compare.hpp>
-#include <nall/string/char/convert.hpp>
-#include <nall/string/char/match.hpp>
-#include <nall/string/char/strm.hpp>
-#include <nall/string/char/strpos.hpp>
-#include <nall/string/char/trim.hpp>
-#include <nall/string/char/utf8.hpp>
-#include <nall/string/char/utility.hpp>
-
-#endif
diff --git a/nall/string/char/base.hpp b/nall/string/char/base.hpp
deleted file mode 100644
index 49b2fc22..00000000
--- a/nall/string/char/base.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-//collection of functions to extend libc
-//none of these functions require nall::string
-//and thus, require no changes when nall::string is modified
-
-namespace nall {
-
-//compare.hpp
-inline char chrlower(char c);
-inline char chrupper(char c);
-inline int imemcmp(const char* str1, const char* str2, unsigned size);
-inline int istrcmp(const char* str1, const char* str2);
-inline bool strbegin(const char* str, const char* key);
-inline bool istrbegin(const char* str, const char* key);
-inline bool strend(const char* str, const char* key);
-inline bool istrend(const char* str, const char* key);
-
-//convert.hpp
-inline char* strlower(char* str);
-inline char* strupper(char* str);
-inline char* qstrlower(char* str);
-inline char* qstrupper(char* str);
-inline char* strtr(char* dest, const char* before, const char* after);
-
-//match.hpp
-inline bool strmatch(const char* str, const char* pattern);
-inline bool istrmatch(const char* str, const char* pattern);
-inline bool tokenize(const char* s, const char* p);
-
-//strm.hpp
-inline unsigned strmcpy(char* target, const char* source, unsigned length);
-inline unsigned strmcat(char* target, const char* source, unsigned length);
-inline bool strccpy(char* target, const char* source, unsigned length);
-inline bool strccat(char* target, const char* source, unsigned length);
-inline void strpcpy(char*& target, const char* source, unsigned& length);
-
-//strpos.hpp
-inline maybe<unsigned> strpos(const char* str, const char* key);
-inline maybe<unsigned> istrpos(const char* str, const char* key);
-inline maybe<unsigned> qstrpos(const char* str, const char* key);
-inline maybe<unsigned> iqstrpos(const char* str, const char* key);
-template<bool Insensitive = false, bool Quoted = false> inline maybe<unsigned> ustrpos(const char* str, const char* key);
-
-//trim.hpp
-template<unsigned Limit = 0> inline char* ltrim(char* str, const char* key = " ");
-template<unsigned Limit = 0> inline char* rtrim(char* str, const char* key = " ");
-template<unsigned Limit = 0> inline char* trim(char* str, const char* key = " ");
-template<unsigned Limit = 0> inline char* trim(char* str, const char* lkey, const char* rkey);
-inline char* strip(char* s);
-
-//utf8.hpp
-struct UTF8 {
-  unsigned size;       //size of encoded codepoint
-  uint64_t data;       //encoded codepoint
-  unsigned codepoint;  //decoded codepoint
-};
-inline UTF8 utf8_read(const char* s);
-inline void utf8_write(char* s, const UTF8& utf8);
-
-//utility.hpp
-template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
-template<bool Quoted, typename T> alwaysinline bool quoteskip(T*& p);
-template<bool Quoted, typename T> alwaysinline bool quotecopy(char*& t, T*& p);
-inline char* strduplicate(const char* s);
-
-}
-
-#endif
diff --git a/nall/string/char/compare.hpp b/nall/string/char/compare.hpp
deleted file mode 100644
index ff9bf54b..00000000
--- a/nall/string/char/compare.hpp
+++ /dev/null
@@ -1,81 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-char chrlower(char c) {
-  return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
-}
-
-char chrupper(char c) {
-  return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
-}
-
-int imemcmp(const char* str1, const char* str2, unsigned size) {
-  while(size--) {
-    if(chrlower(*str1) != chrlower(*str2)) break;
-    str1++, str2++;
-  }
-  return (int)chrlower(*str1) - (int)chrlower(*str2);
-}
-
-int istrcmp(const char* str1, const char* str2) {
-  while(*str1) {
-    if(chrlower(*str1) != chrlower(*str2)) break;
-    str1++, str2++;
-  }
-  return (int)chrlower(*str1) - (int)chrlower(*str2);
-}
-
-bool strbegin(const char* str, const char* key) {
-  if(!str || !key) return false;
-  int i, ssl = strlen(str), ksl = strlen(key);
-
-  if(ksl > ssl) return false;
-  return (!memcmp(str, key, ksl));
-}
-
-bool istrbegin(const char* str, const char* key) {
-  if(!str || !key) return false;
-  int ssl = strlen(str), ksl = strlen(key);
-
-  if(ksl > ssl) return false;
-  for(int i = 0; i < ksl; i++) {
-    if(str[i] >= 'A' && str[i] <= 'Z') {
-      if(str[i] != key[i] && str[i]+0x20 != key[i])return false;
-    } else if(str[i] >= 'a' && str[i] <= 'z') {
-      if(str[i] != key[i] && str[i]-0x20 != key[i])return false;
-    } else {
-      if(str[i] != key[i])return false;
-    }
-  }
-  return true;
-}
-
-bool strend(const char* str, const char* key) {
-  if(!str || !key) return false;
-  int ssl = strlen(str), ksl = strlen(key);
-
-  if(ksl > ssl) return false;
-  return (!memcmp(str + ssl - ksl, key, ksl));
-}
-
-bool istrend(const char* str, const char* key) {
-  if(!str || !key) return false;
-  int ssl = strlen(str), ksl = strlen(key);
-
-  if(ksl > ssl) return false;
-  for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) {
-    if(str[i] >= 'A' && str[i] <= 'Z') {
-      if(str[i] != key[z] && str[i]+0x20 != key[z])return false;
-    } else if(str[i] >= 'a' && str[i] <= 'z') {
-      if(str[i] != key[z] && str[i]-0x20 != key[z])return false;
-    } else {
-      if(str[i] != key[z])return false;
-    }
-  }
-  return true;
-}
-
-}
-
-#endif
diff --git a/nall/string/char/convert.hpp b/nall/string/char/convert.hpp
deleted file mode 100644
index aa003a49..00000000
--- a/nall/string/char/convert.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-char* strlower(char* str) {
-  if(!str) return nullptr;
-  int i = 0;
-  while(str[i]) {
-    str[i] = chrlower(str[i]);
-    i++;
-  }
-  return str;
-}
-
-char* strupper(char* str) {
-  if(!str) return nullptr;
-  int i = 0;
-  while(str[i]) {
-    str[i] = chrupper(str[i]);
-    i++;
-  }
-  return str;
-}
-
-char* qstrlower(char* s) {
-  if(!s) return nullptr;
-  char* base = s;
-  bool quoted = false;
-  while(*s) {
-    if(*s == '\"' || *s == '\'') quoted ^= 1;
-    if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
-    s++;
-  }
-  return base;
-}
-
-char* qstrupper(char* s) {
-  if(!s) return nullptr;
-  char* base = s;
-  bool quoted = false;
-  while(*s) {
-    if(*s == '\"' || *s == '\'') quoted ^= 1;
-    if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
-    s++;
-  }
-  return base;
-}
-
-char* strtr(char* dest, const char* before, const char* after) {
-  if(!dest || !before || !after) return dest;
-  int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
-
-  if(bsl != asl || bsl == 0) return dest;  //patterns must be the same length for 1:1 replace
-  for(unsigned i = 0; i < sl; i++) {
-    for(unsigned l = 0; l < bsl; l++) {
-      if(dest[i] == before[l]) {
-        dest[i] = after[l];
-        break;
-      }
-    }
-  }
-
-  return dest;
-}
-
-}
-
-#endif
diff --git a/nall/string/char/strm.hpp b/nall/string/char/strm.hpp
deleted file mode 100644
index 7e2b6e7c..00000000
--- a/nall/string/char/strm.hpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-//return = strlen(target)
-unsigned strmcpy(char* target, const char* source, unsigned length) {
-  const char* origin = target;
-  if(length) {
-    while(*source && --length) *target++ = *source++;
-    *target = 0;
-  }
-  return target - origin;
-}
-
-//return = strlen(target)
-unsigned strmcat(char* target, const char* source, unsigned length) {
-  const char* origin = target;
-  while(*target && length) target++, length--;
-  return (target - origin) + strmcpy(target, source, length);
-}
-
-//return = true when all of source was copied
-bool strccpy(char* target, const char* source, unsigned length) {
-  return !source[strmcpy(target, source, length)];
-}
-
-//return = true when all of source was copied
-bool strccat(char* target, const char* source, unsigned length) {
-  while(*target && length) target++, length--;
-  return !source[strmcpy(target, source, length)];
-}
-
-//return = reserved for future use
-void strpcpy(char*& target, const char* source, unsigned& length) {
-  unsigned offset = strmcpy(target, source, length);
-  target += offset, length -= offset;
-}
-
-}
-
-#endif
diff --git a/nall/string/char/strpos.hpp b/nall/string/char/strpos.hpp
deleted file mode 100644
index 8c0baa44..00000000
--- a/nall/string/char/strpos.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-//usage example:
-//if(auto position = strpos(str, key)) print(position(), "\n");
-//prints position of key within str; but only if it is found
-
-namespace nall {
-
-template<bool Insensitive, bool Quoted>
-maybe<unsigned> ustrpos(const char* str, const char* key) {
-  const char* base = str;
-
-  while(*str) {
-    if(quoteskip<Quoted>(str)) continue;
-    for(unsigned n = 0;; n++) {
-      if(key[n] == 0) return (unsigned)(str - base);
-      if(str[n] == 0) return nothing;
-      if(!chrequal<Insensitive>(str[n], key[n])) break;
-    }
-    str++;
-  }
-
-  return nothing;
-}
-
-maybe<unsigned> strpos(const char* str, const char* key) { return ustrpos<false, false>(str, key); }
-maybe<unsigned> istrpos(const char* str, const char* key) { return ustrpos<true, false>(str, key); }
-maybe<unsigned> qstrpos(const char* str, const char* key) { return ustrpos<false, true>(str, key); }
-maybe<unsigned> iqstrpos(const char* str, const char* key) { return ustrpos<true, true>(str, key); }
-
-}
-
-#endif
diff --git a/nall/string/char/trim.hpp b/nall/string/char/trim.hpp
deleted file mode 100644
index 969b6926..00000000
--- a/nall/string/char/trim.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-//limit defaults to zero, which will underflow on first compare; equivalent to no limit
-template<unsigned Limit> char* ltrim(char* str, const char* key) {
-  if(!str || !key || !*key) return str;
-  unsigned limit = Limit;
-  while(strbegin(str, key)) {
-    char* dest = str;
-    char* src = str + strlen(key);
-    while(true) {
-      *dest = *src++;
-      if(!*dest) break;
-      dest++;
-    }
-    if(--limit == 0) break;
-  }
-  return str;
-}
-
-template<unsigned Limit> char* rtrim(char* str, const char* key) {
-  if(!str || !key || !*key) return str;
-  unsigned limit = Limit;
-  while(strend(str, key)) {
-    str[strlen(str) - strlen(key)] = 0;
-    if(--limit == 0) break;
-  }
-  return str;
-}
-
-template<unsigned limit> char* trim(char* str, const char* key) {
-  return ltrim<limit>(rtrim<limit>(str, key), key);
-}
-
-template<unsigned limit> char* trim(char* str, const char* lkey, const char* rkey) {
-  return ltrim<limit>(rtrim<limit>(str, rkey), lkey);
-}
-
-//remove whitespace characters from both left and right sides of string
-char* strip(char* s) {
-  if(!s) return nullptr;
-
-  signed n = 0, p = 0;
-  while(s[n]) {
-    if(s[n] != ' ' && s[n] != '\t' && s[n] != '\r' && s[n] != '\n') break;
-    n++;
-  }
-  while(s[n]) s[p++] = s[n++];
-  s[p--] = 0;
-  while(p >= 0) {
-    if(s[p] != ' ' && s[p] != '\t' && s[p] != '\r' && s[p] != '\n') break;
-    p--;
-  }
-  s[++p] = 0;
-
-  return s;
-}
-
-}
-
-#endif
diff --git a/nall/string/char/utf8.hpp b/nall/string/char/utf8.hpp
deleted file mode 100644
index 03eb5ef0..00000000
--- a/nall/string/char/utf8.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-UTF8 utf8_read(const char* s) {
-  UTF8 utf8;
-
-       if((*s & 0xfe) == 0xfc) utf8.size = 6;
-  else if((*s & 0xfc) == 0xf8) utf8.size = 5;
-  else if((*s & 0xf8) == 0xf0) utf8.size = 4;
-  else if((*s & 0xf0) == 0xe0) utf8.size = 3;
-  else if((*s & 0xe0) == 0xc0) utf8.size = 2;
-  else                         utf8.size = 1;
-
-  utf8.data = 0;
-  for(unsigned n = 0; n < utf8.size; n++) {
-    utf8.data = (utf8.data << 8) | (uint8_t)s[n];
-  }
-
-  static uint8_t mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
-  utf8.codepoint = s[0] & mask[utf8.size];
-  for(unsigned n = 1; n < utf8.size; n++) {
-    utf8.codepoint = (utf8.codepoint << 6) | (s[n] & 0x3f);
-  }
-
-  return utf8;
-}
-
-void utf8_write(char* s, const UTF8& utf8) {
-  for(signed n = utf8.size - 1, shift = 0; n >= 0; n--, shift += 8) {
-    s[n] = utf8.data >> shift;
-  }
-}
-
-}
-
-#endif
diff --git a/nall/string/char/utility.hpp b/nall/string/char/utility.hpp
deleted file mode 100644
index bb35c020..00000000
--- a/nall/string/char/utility.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-template<bool Insensitive>
-bool chrequal(char x, char y) {
-  if(Insensitive) return chrlower(x) == chrlower(y);
-  return x == y;
-}
-
-template<bool Quoted, typename T>
-bool quoteskip(T*& p) {
-  if(Quoted == false) return false;
-  if(*p != '\"') return false;
-
-  while(*p == '\"') {
-    char x = *p++;
-    while(*p && *p++ != x);
-  }
-
-  return true;
-}
-
-template<bool Quoted, typename T>
-bool quotecopy(char*& t, T*& p) {
-  if(Quoted == false) return false;
-  if(*p != '\"') return false;
-
-  while(*p == '\"') {
-    char x = *p++;
-    *t++ = x;
-    while(*p && *p != x) *t++ = *p++;
-    *t++ = *p++;
-  }
-
-  return true;
-}
-
-//strdup() is not a standard function, so recreate it
-char* strduplicate(const char* s) {
-  if(s == nullptr) return nullptr;
-  unsigned length = strlen(s);
-  char* result = (char*)malloc(length + 1);
-  strcpy(result, s);
-  return result;
-}
-
-}
-
-#endif
diff --git a/nall/string/compare.hpp b/nall/string/compare.hpp
new file mode 100644
index 00000000..7c4f49d6
--- /dev/null
+++ b/nall/string/compare.hpp
@@ -0,0 +1,50 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+namespace nall {
+
+template<bool Insensitive> inline auto _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 icompare(const string& self, rstring source) -> signed {
+  return memory::icompare(self.data(), self.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 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 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 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 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 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;
+}
+
+}
+
+#endif
diff --git a/nall/string/convert.hpp b/nall/string/convert.hpp
new file mode 100644
index 00000000..3ffb5a6d
--- /dev/null
+++ b/nall/string/convert.hpp
@@ -0,0 +1,55 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+namespace nall {
+
+auto downcase(string& self) -> string& {
+  char* p = self.pointer();
+  for(unsigned n = 0; n < self.size(); n++) {
+    if(p[n] >= 'A' && p[n] <= 'Z') p[n] += 0x20;
+  }
+  return self;
+}
+
+auto qdowncase(string& self) -> string& {
+  char* p = self.pointer();
+  for(unsigned n = 0, quoted = 0; n < self.size(); n++) {
+    if(p[n] == '\"') quoted ^= 1;
+    if(!quoted && p[n] >= 'A' && p[n] <= 'Z') p[n] += 0x20;
+  }
+  return self;
+}
+
+auto upcase(string& self) -> string& {
+  char* p = self.pointer();
+  for(unsigned n = 0; n < self.size(); n++) {
+    if(p[n] >= 'a' && p[n] <= 'z') p[n] -= 0x20;
+  }
+  return self;
+}
+
+auto qupcase(string& self) -> string& {
+  char* p = self.pointer();
+  for(unsigned n = 0, quoted = 0; n < self.size(); n++) {
+    if(p[n] == '\"') quoted ^= 1;
+    if(!quoted && p[n] >= 'a' && p[n] <= 'z') p[n] -= 0x20;
+  }
+  return self;
+}
+
+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.pointer();
+  for(unsigned n = 0; n < self.size(); n++) {
+    for(unsigned s = 0; s < from.size(); s++) {
+      if(p[n] == from[s]) {
+        p[n] = to[s];
+        break;
+      }
+    }
+  }
+  return self;
+}
+
+}
+
+#endif
diff --git a/nall/string/core.hpp b/nall/string/core.hpp
index 8a00645e..dee9bfd0 100644
--- a/nall/string/core.hpp
+++ b/nall/string/core.hpp
@@ -3,7 +3,9 @@
 //only allocators may access _data or modify _size and _capacity
 //all other functions must use data(), size(), capacity()
 
-#if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
+#if defined(NALL_STRING_ALLOCATOR_ADAPTIVE)
+  #include <nall/string/allocator/adaptive.hpp>
+#elif defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
   #include <nall/string/allocator/copy-on-write.hpp>
 #elif defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION)
   #include <nall/string/allocator/small-string-optimization.hpp>
@@ -13,75 +15,44 @@
 
 namespace nall {
 
-unsigned string::length() const { return strlen(data()); }
-unsigned string::size() const { return _size; }
-unsigned string::capacity() const { return _capacity; }
-bool string::empty() const { return _size == 0; }
-
-void string::clear(char c) {
-  for(unsigned n = 0; n < size(); n++) data()[n] = c;
-}
-
-unsigned string::hash() const {
-  const char* p = data();
-  unsigned result = 5381;
-  while(*p) result = (result << 5) + result + *p++;
-  return result;
-}
-
-template<typename... Args> string& string::assign(Args&&... args) {
-  resize(0);
-  sprint(*this, std::forward<Args>(args)...);
-  return *this;
-}
-
-template<typename... Args> string& string::append(Args&&... args) {
-  sprint(*this, std::forward<Args>(args)...);
-  return *this;
-}
-
-string& string::_append(const char* s) {
-  if(s == nullptr) return *this;
-  unsigned basesize = size(), length = strlen(s);
-  reserve(basesize + length);
-  memcpy(data() + basesize, s, length);
-  resize(basesize + length);
-  return *this;
-}
-
-string::operator bool() const {
-  return !empty();
-}
-
-string::operator const char*() const {
-  return data();
-}
-
-char& string::operator[](signed position) {
+auto string::operator[](signed position) const -> const char& {
   if(position > size() + 1) throw exception_out_of_bounds{};
   return data()[position];
 }
 
-const char& string::operator[](signed position) const {
-  if(position > size() + 1) throw exception_out_of_bounds{};
-  return data()[position];
+template<typename... P> auto assign(string& self, P&&... p) -> string& {
+  self.resize(0);
+  return self.append(std::forward<P>(p)...);
 }
 
-bool string::operator==(const char* str) const { return strcmp(data(), str) == 0; }
-bool string::operator!=(const char* str) const { return strcmp(data(), str) != 0; }
-bool string::operator< (const char* str) const { return strcmp(data(), str)  < 0; }
-bool string::operator<=(const char* str) const { return strcmp(data(), str) <= 0; }
-bool string::operator> (const char* str) const { return strcmp(data(), str)  > 0; }
-bool string::operator>=(const char* str) const { return strcmp(data(), str) >= 0; }
-
-string::string(const string& source) {
-  construct();
-  operator=(source);
+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)...);
 }
 
-string::string(string&& source) {
-  construct();
-  operator=(std::move(source));
+template<typename... P> auto append(string& self, const format& value, P&&... p) -> string& {
+  self.format(value);
+  return self.append(std::forward<P>(p)...);
+}
+
+auto append(string& self) -> string& {
+  return self;
+}
+
+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.pointer() + size, source.data(), length);
+  return self;
+}
+
+auto empty(const string& self) -> bool {
+  return self.size() == 0;
+}
+
+auto length(const string& self) -> unsigned {
+  return strlen(self.data());
 }
 
 }
diff --git a/nall/string/datetime.hpp b/nall/string/datetime.hpp
index b55659a1..bf27007c 100644
--- a/nall/string/datetime.hpp
+++ b/nall/string/datetime.hpp
@@ -2,28 +2,29 @@
 
 namespace nall {
 
-string string::date() {
-  time_t timestamp = ::time(nullptr);
+auto string::date(time_t timestamp) -> string {
+  if(timestamp == 0) timestamp = ::time(nullptr);
   tm* info = localtime(&timestamp);
   return {
-    format<4, '0'>(1900 + info->tm_year), "-",
-    format<2, '0'>(1 + info->tm_mon), "-",
-    format<2, '0'>(info->tm_mday)
+    nall::decimal<4>(1900 + info->tm_year), "-",
+    nall::decimal<2>(1 + info->tm_mon), "-",
+    nall::decimal<2>(info->tm_mday)
   };
 }
 
-string string::time() {
-  time_t timestamp = ::time(nullptr);
+auto string::time(time_t timestamp) -> string {
+  if(timestamp == 0) timestamp = ::time(nullptr);
   tm* info = localtime(&timestamp);
   return {
-    format<2, '0'>(info->tm_hour), ":",
-    format<2, '0'>(info->tm_min), ":",
-    format<2, '0'>(info->tm_sec)
+    nall::decimal<2>(info->tm_hour), ":",
+    nall::decimal<2>(info->tm_min), ":",
+    nall::decimal<2>(info->tm_sec)
   };
 }
 
-string string::datetime() {
-  return {string::date(), " ", string::time()};
+auto string::datetime(time_t timestamp) -> string {
+  if(timestamp == 0) timestamp = ::time(nullptr);
+  return {string::date(timestamp), " ", string::time(timestamp)};
 }
 
 }
diff --git a/nall/string/eval/evaluator.hpp b/nall/string/eval/evaluator.hpp
index 8957a30c..5e77ff7f 100644
--- a/nall/string/eval/evaluator.hpp
+++ b/nall/string/eval/evaluator.hpp
@@ -29,7 +29,7 @@ inline string evaluateExpression(Node* node) {
     for(auto& link : node->link) {
       result.append(evaluateExpression(link), ", ");
     }
-    return result.rtrim<1>(", ").append(")");
+    return result.rtrim(", ").append(")");
   }
   }
   #undef p
diff --git a/nall/string/file.hpp b/nall/string/file.hpp
deleted file mode 100644
index 1b809c2f..00000000
--- a/nall/string/file.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-string string::read(const string& filename) {
-  string result;
-
-  #if !defined(_WIN32)
-  FILE* fp = fopen(filename, "rb");
-  #else
-  FILE* fp = _wfopen(utf16_t(filename), L"rb");
-  #endif
-  if(!fp) return result;
-
-  fseek(fp, 0, SEEK_END);
-  unsigned fsize = ftell(fp);
-  rewind(fp);
-  char* fdata = new char[fsize + 1];
-  unsigned unused = fread(fdata, 1, fsize, fp);
-  fclose(fp);
-  fdata[fsize] = 0;
-  result.resize(fsize);
-  memcpy(result.data(), fdata, fsize);
-  delete[] fdata;
-
-  return result;
-}
-
-}
-
-#endif
diff --git a/nall/string/filename.hpp b/nall/string/filename.hpp
deleted file mode 100644
index d748a8ba..00000000
--- a/nall/string/filename.hpp
+++ /dev/null
@@ -1,84 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-// "/foo/bar.c" -> "/foo/"
-// "/foo/" -> "/foo/"
-// "bar.c" -> "./"
-string dir(string name) {
-  for(signed i = name.length(); i >= 0; i--) {
-    if(name[i] == '/' || name[i] == '\\') {
-      name.resize(i + 1);
-      break;
-    }
-    if(i == 0) name = "./";
-  }
-  return name;
-}
-
-// "/foo/bar.c" -> "bar.c"
-// "/foo/" -> ""
-// "bar.c" -> "bar.c"
-string notdir(string name) {
-  for(signed i = name.length(); i >= 0; i--) {
-    if(name[i] == '/' || name[i] == '\\') {
-      return (const char*)name + i + 1;
-    }
-  }
-  return name;
-}
-
-// "/foo/bar/baz" -> "/foo/bar/"
-// "/foo/bar/" -> "/foo/"
-// "/foo/bar" -> "/foo/"
-string parentdir(string name) {
-  unsigned length = name.length(), paths = 0, prev, last;
-  for(unsigned i = 0; i < length; i++) {
-    if(name[i] == '/' || name[i] == '\\') {
-      paths++;
-      prev = last;
-      last = i;
-    }
-  }
-  if(last + 1 == length) last = prev;  //if name ends in slash; use previous slash
-  if(paths > 1) name.resize(last + 1);
-  return name;
-}
-
-// "/foo/bar.c" -> "/foo/bar"
-string basename(string name) {
-  for(signed i = name.length(); i >= 0; i--) {
-    if(name[i] == '/' || name[i] == '\\') break;  //file has no extension
-    if(name[i] == '.') {
-      name.resize(i);
-      break;
-    }
-  }
-  return name;
-}
-
-// "/foo/bar.c" -> "c"
-// "/foo/bar" -> ""
-string extension(string name) {
-  for(signed i = name.length(); i >= 0; i--) {
-    if(name[i] == '/' || name[i] == '\\') return "";  //file has no extension
-    if(name[i] == '.') {
-      return (const char*)name + i + 1;
-    }
-  }
-  return name;
-}
-
-string tempname() {
-  string path = temppath();
-  srand(time(nullptr));
-  while(true) {
-    uint32_t seed = rand();
-    string filename = {path, ".temporary-", hex<8>(seed)};
-    if(access(filename, F_OK) != 0) return filename;
-  }
-}
-
-}
-
-#endif
diff --git a/nall/string/find.hpp b/nall/string/find.hpp
new file mode 100644
index 00000000..5886b3d3
--- /dev/null
+++ b/nall/string/find.hpp
@@ -0,0 +1,30 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+namespace nall {
+
+template<bool Insensitive, bool Quoted> auto _find(const string& self, signed offset, rstring source) -> 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;) {
+    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; }
+    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 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); }
+
+}
+
+#endif
diff --git a/nall/string/format.hpp b/nall/string/format.hpp
index 98c0c5b7..827850c5 100644
--- a/nall/string/format.hpp
+++ b/nall/string/format.hpp
@@ -2,69 +2,176 @@
 
 namespace nall {
 
-template<signed precision, char padchar> string format(const string& value) {
-  if(precision == 0) return value;
+//nall::format is a vector<string> of parameters that can be applied to a string
+//each {#} token will be replaced with its appropriate format parameter
 
-  bool padright = precision >= 0;
-  unsigned padding = abs(precision);
+auto string::format(const nall::format& params) -> type& {
+  signed size = this->size();
+  char* data = (char*)memory::allocate(size);
+  memory::copy(data, this->data(), size);
 
-  if(padding <= value.size()) {
-    if(padright) return substr(value, value.size() - padding);
-    else return substr(value, 0, padding);
+  signed x = 0;
+  while(x < size - 2) {  //2 = minimum tag length
+    if(data[x] != '{') { x++; continue; }
+
+    signed y = x + 1;
+    while(y < size - 1) {  //-1 avoids going out of bounds on test after this loop
+      if(data[y] != '}') { y++; continue; }
+      break;
+    }
+
+    if(data[y++] != '}') { x++; continue; }
+
+    static auto isNumeric = [](char* s, char* e) -> bool {
+      if(s == e) return false;  //ignore empty tags: {}
+      while(s < e) {
+        if(*s >= '0' && *s <= '9') { s++; continue; }
+        return false;
+      }
+      return true;
+    };
+    if(!isNumeric(&data[x + 1], &data[y - 1])) { x++; continue; }
+
+    unsigned index = nall::decimal(&data[x + 1]);
+    if(index >= params.size()) { x++; continue; }
+
+    unsigned sourceSize = y - x;
+    unsigned targetSize = params[index].size();
+    unsigned remaining = size - x;
+
+    if(sourceSize > targetSize) {
+      unsigned difference = sourceSize - targetSize;
+      memory::move(&data[x], &data[x + difference], remaining);
+      size -= difference;
+    } else if(targetSize > sourceSize) {
+      unsigned difference = targetSize - sourceSize;
+      data = (char*)realloc(data, size + difference);
+      size += difference;
+      memory::move(&data[x + difference], &data[x], remaining);
+    }
+    memory::copy(&data[x], params[index].data(), targetSize);
+    x += targetSize;
   }
 
-  string buffer;
-  buffer.resize(padding);
-  buffer.clear(padchar);
+  resize(size);
+  memory::copy(pointer(), data, size);
+  memory::free(data);
+  return *this;
+}
 
-  memcpy(buffer.data() + (padright ? padding - value.size() : 0), value, value.size());
+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)...);
+}
+
+auto append(format& self) -> format& {
+  return self;
+}
+
+template<typename... P> auto print(P&&... p) -> void {
+  string s{std::forward<P>(p)...};
+  fputs(s.data(), stdout);
+}
+
+template<signed precision, char padchar> auto integer(intmax_t value) -> string {
+  string buffer;
+  buffer.resize(1 + sizeof(intmax_t) * 3);
+  char* p = buffer.pointer();
+
+  bool negative = value < 0;
+  value = abs(value);
+  unsigned size = 0;
+  do {
+    p[size++] = '0' + (value % 10);
+    value /= 10;
+  } while(value);
+  if(negative) p[size++] = '-';
+  buffer.resize(size);
+  buffer.reverse();
+  if(precision) buffer.size(precision, padchar);
   return buffer;
 }
 
-template<signed precision, char padchar> string hex(uintmax_t value) {
+template<signed precision, char padchar> auto decimal(uintmax_t value) -> string {
+  string buffer;
+  buffer.resize(sizeof(uintmax_t) * 3);
+  char* p = buffer.pointer();
+
+  unsigned size = 0;
+  do {
+    p[size++] = '0' + (value % 10);
+    value /= 10;
+  } while(value);
+  buffer.resize(size);
+  buffer.reverse();
+  if(precision) buffer.size(precision, padchar);
+  return buffer;
+}
+
+template<signed precision, char padchar> auto hex(uintmax_t value) -> string {
   string buffer;
   buffer.resize(sizeof(uintmax_t) * 2);
+  char* p = buffer.pointer();
 
   unsigned size = 0;
   do {
     unsigned n = value & 15;
-    buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
+    p[size++] = n < 10 ? '0' + n : 'a' + n - 10;
     value >>= 4;
   } while(value);
   buffer.resize(size);
   buffer.reverse();
-
-  return format<precision, padchar>(buffer);
+  if(precision) buffer.size(precision, padchar);
+  return buffer;
 }
 
-template<signed precision, char padchar> string octal(uintmax_t value) {
+template<signed precision, char padchar> auto octal(uintmax_t value) -> string {
   string buffer;
   buffer.resize(sizeof(uintmax_t) * 3);
+  char* p = buffer.pointer();
 
   unsigned size = 0;
   do {
-    buffer[size++] = '0' + (value & 7);
+    p[size++] = '0' + (value & 7);
     value >>= 3;
   } while(value);
   buffer.resize(size);
   buffer.reverse();
-
-  return format<precision, padchar>(buffer);
+  if(precision) buffer.size(precision, padchar);
+  return buffer;
 }
 
-template<signed precision, char padchar> string binary(uintmax_t value) {
+template<signed precision, char padchar> auto binary(uintmax_t value) -> string {
   string buffer;
   buffer.resize(sizeof(uintmax_t) * 8);
+  char* p = buffer.pointer();
 
   unsigned size = 0;
   do {
-    buffer[size++] = '0' + (value & 1);
+    p[size++] = '0' + (value & 1);
     value >>= 1;
   } while(value);
   buffer.resize(size);
   buffer.reverse();
+  if(precision) buffer.size(precision, padchar);
+  return buffer;
+}
 
-  return format<precision, padchar>(buffer);
+template<signed precision, typename T> auto pointer(const T* value) -> string {
+  if(value == nullptr) return "(null)";
+  return {"0x", hex<precision>((uintptr_t)value)};
+}
+
+template<signed precision> auto pointer(uintptr_t value) -> string {
+  if(value == 0) return "(null)";
+  return {"0x", hex<precision>(value)};
+}
+
+auto real(long double value) -> string {
+  string temp;
+  temp.resize(real(nullptr, value));
+  real(temp.pointer(), value);
+  return temp;
 }
 
 }
diff --git a/nall/string/hash.hpp b/nall/string/hash.hpp
new file mode 100644
index 00000000..98084ec4
--- /dev/null
+++ b/nall/string/hash.hpp
@@ -0,0 +1,35 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+namespace nall {
+
+namespace Hash {
+  auto CRC16::digest() -> string {
+    return hex<4>(value());
+  }
+
+  auto CRC32::digest() -> string {
+    return hex<8>(value());
+  }
+
+  auto SHA256::digest() const -> string {
+    string result;
+    for(auto n : value()) result.append(hex<2>(n));
+    return result;
+  }
+}
+
+auto crc16(const string& self) -> string {
+  return Hash::CRC16(self.data(), self.size()).digest();
+}
+
+auto crc32(const string& self) -> string {
+  return Hash::CRC32(self.data(), self.size()).digest();
+}
+
+auto sha256(const string& self) -> string {
+  return Hash::SHA256(self.data(), self.size()).digest();
+}
+
+}
+
+#endif
diff --git a/nall/string/list.hpp b/nall/string/list.hpp
index 55b2e76e..6b9f4f78 100644
--- a/nall/string/list.hpp
+++ b/nall/string/list.hpp
@@ -2,42 +2,7 @@
 
 namespace nall {
 
-maybe<unsigned> lstring::find(rstring key) const {
-  for(unsigned i = 0; i < size(); i++) {
-    if(operator[](i) == key) return i;
-  }
-  return nothing;
-}
-
-string lstring::merge(const string& separator) const {
-  string output;
-  for(unsigned i = 0; i < size(); i++) {
-    output.append(operator[](i));
-    if(i < size() - 1) output.append(separator);
-  }
-  return output;
-}
-
-lstring& lstring::isort() {
-  nall::sort(pool, objectsize, [](const string& x, const string& y) {
-    return istrcmp(x, y) < 0;
-  });
-  return *this;
-}
-
-lstring& lstring::strip() {
-  for(unsigned n = 0; n < size(); n++) {
-    operator[](n).strip();
-  }
-  return *this;
-}
-
-template<typename... Args> void lstring::append(const string& data, Args&&... args) {
-  vector::append(data);
-  append(std::forward<Args>(args)...);
-}
-
-bool lstring::operator==(const lstring& source) const {
+auto lstring::operator==(const lstring& source) const -> bool {
   if(this == &source) return true;
   if(size() != source.size()) return false;
   for(unsigned n = 0; n < size(); n++) {
@@ -46,39 +11,55 @@ bool lstring::operator==(const lstring& source) const {
   return true;
 }
 
-bool lstring::operator!=(const lstring& source) const {
+auto lstring::operator!=(const lstring& source) const -> bool {
   return !operator==(source);
 }
 
-lstring& lstring::operator=(const lstring& source) {
-  vector::operator=(source);
+auto lstring::isort() -> lstring& {
+  nall::sort(pool, objectsize, [](const string& x, const string& y) {
+    return memory::icompare(x.data(), x.size(), y.data(), y.size()) < 0;
+  });
   return *this;
 }
 
-lstring& lstring::operator=(lstring& source) {
-  vector::operator=(source);
-  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;
 }
 
-lstring& lstring::operator=(lstring&& source) {
-  vector::operator=(std::move(source));
-  return *this;
+auto append(lstring& self) -> lstring& {
+  return self;
 }
 
-template<typename... Args> lstring::lstring(Args&&... args) {
-  append(std::forward<Args>(args)...);
+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;
+  }
+  return nothing;
 }
 
-lstring::lstring(const lstring& source) {
-  vector::operator=(source);
+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;
+  }
+  return nothing;
 }
 
-lstring::lstring(lstring& source) {
-  vector::operator=(source);
+auto merge(const lstring& self, const string& separator) -> string {
+  string output;
+  for(unsigned n = 0; n < self.size(); n++) {
+    output.append(self[n]);
+    if(n < self.size() - 1) output.append(separator);
+  }
+  return output;
 }
 
-lstring::lstring(lstring&& source) {
-  vector::operator=(std::move(source));
+auto strip(lstring& self) -> lstring& {
+  for(unsigned n = 0; n < self.size(); n++) {
+    self[n].strip();
+  }
+  return self;
 }
 
 }
diff --git a/nall/string/markup/bml.hpp b/nall/string/markup/bml.hpp
index 6743a80e..7f5e8252 100644
--- a/nall/string/markup/bml.hpp
+++ b/nall/string/markup/bml.hpp
@@ -71,7 +71,7 @@ protected:
       if(length == 0) throw "Invalid attribute name";
       node.name = substr(p, 0, length);
       node.parseData(p += length);
-      node.data.rtrim<1>("\n");
+      node.data.rtrim("\n");
       children.append(node);
     }
   }
@@ -98,7 +98,7 @@ protected:
       children.append(node);
     }
 
-    data.rtrim<1>("\n");
+    data.rtrim("\n");
   }
 
   //read top-level nodes
diff --git a/nall/string/markup/node.hpp b/nall/string/markup/node.hpp
index ff7ea3d6..fbf2ae46 100644
--- a/nall/string/markup/node.hpp
+++ b/nall/string/markup/node.hpp
@@ -20,6 +20,10 @@ struct Node {
     return string{data}.strip();
   }
 
+  bool boolean() const {
+    return text() != "false";
+  }
+
   intmax_t integer() const {
     return numeral(text());
   }
@@ -93,7 +97,7 @@ struct Node {
     if(name.match("*[*]")) {
       lstring side = name.split<1>("[");
       name = side(0);
-      side = side(1).rtrim<1>("]").split<1>("-");
+      side = side(1).rtrim("]").split<1>("-");
       lo = side(0).empty() ?  0u : numeral(side(0));
       hi = side(1).empty() ? ~0u : numeral(side(1));
     }
@@ -101,7 +105,7 @@ struct Node {
     if(name.match("*(*)")) {
       lstring side = name.split<1>("(");
       name = side(0);
-      rule = side(1).rtrim<1>(")");
+      rule = side(1).rtrim(")");
     }
 
     unsigned position = 0;
diff --git a/nall/string/markup/xml.hpp b/nall/string/markup/xml.hpp
index 6debcc37..fd9fe787 100644
--- a/nall/string/markup/xml.hpp
+++ b/nall/string/markup/xml.hpp
@@ -39,34 +39,34 @@ protected:
     target.reserve(length + 1);
 
     #if defined(NALL_XML_LITERAL)
-    memcpy(target(), source, length);
+    memory::copy(target.pointer(), source, length);
     target[length] = 0;
     return;
     #endif
 
-    char* output = target.data();
+    char* output = target.pointer();
     while(length) {
       if(*source == '&') {
-        if(!memcmp(source, "&lt;",   4)) { *output++ = '<';  source += 4; length -= 4; continue; }
-        if(!memcmp(source, "&gt;",   4)) { *output++ = '>';  source += 4; length -= 4; continue; }
-        if(!memcmp(source, "&amp;",  5)) { *output++ = '&';  source += 5; length -= 5; continue; }
-        if(!memcmp(source, "&apos;", 6)) { *output++ = '\''; source += 6; length -= 6; continue; }
-        if(!memcmp(source, "&quot;", 6)) { *output++ = '\"'; source += 6; length -= 6; continue; }
+        if(!memory::compare(source, "&lt;",   4)) { *output++ = '<';  source += 4; length -= 4; continue; }
+        if(!memory::compare(source, "&gt;",   4)) { *output++ = '>';  source += 4; length -= 4; continue; }
+        if(!memory::compare(source, "&amp;",  5)) { *output++ = '&';  source += 5; length -= 5; continue; }
+        if(!memory::compare(source, "&apos;", 6)) { *output++ = '\''; source += 6; length -= 6; continue; }
+        if(!memory::compare(source, "&quot;", 6)) { *output++ = '\"'; source += 6; length -= 6; continue; }
       }
 
       if(attribute == false && source[0] == '<' && source[1] == '!') {
         //comment
-        if(!memcmp(source, "<!--", 4)) {
+        if(!memory::compare(source, "<!--", 4)) {
           source += 4, length -= 4;
-          while(memcmp(source, "-->", 3)) source++, length--;
+          while(memory::compare(source, "-->", 3)) source++, length--;
           source += 3, length -= 3;
           continue;
         }
 
         //CDATA
-        if(!memcmp(source, "<![CDATA[", 9)) {
+        if(!memory::compare(source, "<![CDATA[", 9)) {
           source += 9, length -= 9;
-          while(memcmp(source, "]]>", 3)) *output++ = *source++, length--;
+          while(memory::compare(source, "]]>", 3)) *output++ = *source++, length--;
           source += 3, length -= 3;
           continue;
         }
@@ -81,23 +81,23 @@ protected:
     if(*(p + 1) != '!') return false;
 
     //comment
-    if(!memcmp(p, "<!--", 4)) {
-      while(*p && memcmp(p, "-->", 3)) p++;
+    if(!memory::compare(p, "<!--", 4)) {
+      while(*p && memory::compare(p, "-->", 3)) p++;
       if(!*p) throw "unclosed comment";
       p += 3;
       return true;
     }
 
     //CDATA
-    if(!memcmp(p, "<![CDATA[", 9)) {
-      while(*p && memcmp(p, "]]>", 3)) p++;
+    if(!memory::compare(p, "<![CDATA[", 9)) {
+      while(*p && memory::compare(p, "]]>", 3)) p++;
       if(!*p) throw "unclosed CDATA";
       p += 3;
       return true;
     }
 
     //DOCTYPE
-    if(!memcmp(p, "<!DOCTYPE", 9)) {
+    if(!memory::compare(p, "<!DOCTYPE", 9)) {
       unsigned counter = 0;
       do {
         char n = *p++;
@@ -171,7 +171,7 @@ protected:
     while(*p && *p != '>') p++;
     if(*p != '>') throw "unclosed closure element";
     const char* nameEnd = p++;
-    if(memcmp(name, nameStart, nameEnd - nameStart)) throw "closure element name mismatch";
+    if(memory::compare(name.data(), nameStart, nameEnd - nameStart)) throw "closure element name mismatch";
     return true;
   }
 
diff --git a/nall/string/char/match.hpp b/nall/string/match.hpp
similarity index 55%
rename from nall/string/char/match.hpp
rename to nall/string/match.hpp
index 2471d1ce..3cff8b8a 100644
--- a/nall/string/char/match.hpp
+++ b/nall/string/match.hpp
@@ -2,7 +2,12 @@
 
 namespace nall {
 
-bool strmatch(const char* s, const char* p) {
+//todo: these functions are not binary-safe
+
+auto match(const string& self, rstring source) -> bool {
+  const char* s = self.data();
+  const char* p = source.data();
+
   const char* cp = nullptr;
   const char* mp = nullptr;
   while(*s && *p != '*') {
@@ -23,7 +28,14 @@ bool strmatch(const char* s, const char* p) {
   return !*p;
 }
 
-bool istrmatch(const char* s, const char* p) {
+auto imatch(const string& self, rstring source) -> bool {
+  static auto chrlower = [](char c) -> char {
+    return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
+  };
+
+  const char* s = self.data();
+  const char* p = source.data();
+
   const char* cp = nullptr;
   const char* mp = nullptr;
   while(*s && *p != '*') {
@@ -44,7 +56,7 @@ bool istrmatch(const char* s, const char* p) {
   return !*p;
 }
 
-bool tokenize(const char* s, const char* p) {
+inline bool tokenize(const char* s, const char* p) {
   while(*s) {
     if(*p == '*') {
       while(*s) if(tokenize(s++, p + 1)) return true;
@@ -56,6 +68,25 @@ bool tokenize(const char* s, const char* p) {
   return !*p;
 }
 
+auto tokenize(lstring& list, const char* s, const char* p) -> bool {
+  while(*s) {
+    if(*p == '*') {
+      const char* b = s;
+      while(*s) {
+        if(tokenize(list, s++, p + 1)) {
+          list.prepend(substr(b, 0, --s - b));
+          return true;
+        }
+      }
+      list.prepend(b);
+      return !*++p;
+    }
+    if(*s++ != *p++) return false;
+  }
+  while(*p == '*') { list.prepend(s); p++; }
+  return !*p;
+}
+
 }
 
 #endif
diff --git a/nall/string/path.hpp b/nall/string/path.hpp
new file mode 100644
index 00000000..5ef4b7be
--- /dev/null
+++ b/nall/string/path.hpp
@@ -0,0 +1,73 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+namespace nall {
+
+// (/parent/child.type/)
+// (/parent/child.type/)name.type
+auto pathname(const string& 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);
+  }
+  return "";
+}
+
+// /parent/child.type/()
+// /parent/child.type/(name.type)
+auto filename(const string& 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);
+  }
+  return "";
+}
+
+// (/parent/)child.type/
+// (/parent/child.type/)name.type
+auto dirname(const string& 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
+}
+
+// /parent/(child.type/)
+// /parent/child.type/(name.type)
+auto basename(const string& 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, offset + 1);
+  }
+  return "";
+}
+
+// /parent/(child).type/
+// /parent/child.type/(name).type
+auto prefixname(const string& self) -> string {
+  const char* p = self.data() + self.size() - 1, *last = p;
+  for(signed offset = self.size() - 1, suffix = 0; offset >= 0; offset--, p--) {
+    if(*p == '/' && p == last) continue;
+    if(*p == '/') return slice(self, offset + 1, suffix ? suffix - offset - 1 : 0).rtrim("/");
+    if(*p == '.' && suffix == 0) suffix = offset;
+  }
+  return "";
+}
+
+// /parent/child(.type)/
+// /parent/child.type/name(.type)
+auto suffixname(const string& 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 == '/') break;
+    if(*p == '.') return slice(self, offset).rtrim("/");
+  }
+  return "";
+}
+
+}
+
+#endif
diff --git a/nall/string/platform.hpp b/nall/string/platform.hpp
index d80060f3..f78de306 100644
--- a/nall/string/platform.hpp
+++ b/nall/string/platform.hpp
@@ -2,7 +2,7 @@
 
 namespace nall {
 
-string activepath() {
+auto activepath() -> string {
   char path[PATH_MAX] = "";
   auto unused = getcwd(path, PATH_MAX);
   string result = path;
@@ -12,17 +12,17 @@ string activepath() {
   return result;
 }
 
-string realpath(const string& name) {
+auto realpath(rstring name) -> string {
   string result;
   char path[PATH_MAX] = "";
-  if(::realpath(name, path)) result = dir(path);
+  if(::realpath(name, path)) result = string{path}.pathname();
   if(result.empty()) result = activepath();
   result.transform("\\", "/");
   if(result.endsWith("/") == false) result.append("/");
   return result;
 }
 
-string programpath() {
+auto programpath() -> string {
   #if defined(PLATFORM_WINDOWS)
   int argc = 0;
   wchar_t** argv = CommandLineToArgvW(GetCommandLine(), &argc);
@@ -38,7 +38,7 @@ string programpath() {
 
 // /home/username/
 // c:/users/username/
-string userpath() {
+auto userpath() -> string {
   #if defined(PLATFORM_WINDOWS)
   wchar_t path[PATH_MAX] = L"";
   SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path);
@@ -55,7 +55,7 @@ string userpath() {
 
 // /home/username/.config/
 // c:/users/username/appdata/roaming/
-string configpath() {
+auto configpath() -> string {
   #if defined(PLATFORM_WINDOWS)
   wchar_t path[PATH_MAX] = L"";
   SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
@@ -74,7 +74,7 @@ string configpath() {
 // /usr/share
 // /Library/Application Support/
 // c:/ProgramData/
-string sharedpath() {
+auto sharedpath() -> string {
   #if defined(PLATFORM_WINDOWS)
   wchar_t path[PATH_MAX] = L"";
   SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
@@ -92,7 +92,7 @@ string sharedpath() {
 
 // /tmp
 // c:/users/username/AppData/Local/Temp/
-string temppath() {
+auto temppath() -> string {
   #if defined(PLATFORM_WINDOWS)
   wchar_t path[PATH_MAX] = L"";
   GetTempPathW(PATH_MAX, path);
diff --git a/nall/string/ref.hpp b/nall/string/ref.hpp
index 19200891..2c07dbbb 100644
--- a/nall/string/ref.hpp
+++ b/nall/string/ref.hpp
@@ -7,37 +7,54 @@ struct stringref {
     return _data;
   }
 
-  const char* data() const {
+  auto data() const -> const char* {
     return _data;
   }
 
-  unsigned size() const {
-    if(!_initialized) {
-      _initialized = true;
-      _size = strlen(_data);
-    }
+  auto size() const -> unsigned {
+    if(!_initialized) _size = strlen(_data), _initialized = true;
     return _size;
   }
 
-  stringref() = delete;
-  stringref(const stringref& source) = delete;
-  stringref(stringref&& source) = delete;
+  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 unsigned _size;
-  mutable bool _initialized;
+  mutable signed _size;
+  mutable unsigned _initialized;
 };
 
 }
diff --git a/nall/string/replace.hpp b/nall/string/replace.hpp
index 65af7be3..f27b06ad 100644
--- a/nall/string/replace.hpp
+++ b/nall/string/replace.hpp
@@ -3,52 +3,93 @@
 namespace nall {
 
 template<unsigned Limit, bool Insensitive, bool Quoted>
-string& string::ureplace(rstring key, rstring token) {
-  if(key.size() == 0) return *this;
-  enum : unsigned { limit = Limit ? Limit : ~0u };
+auto _replace(string& self, rstring from, rstring to) -> string& {
+  if(Limit == 0 || from.size() == 0) return self;
 
-  const char* p = data();
-  unsigned counter = 0;
+  signed size = self.size();
+  signed matches = 0;
+  signed quoted = 0;
 
-  while(*p) {
-    if(quoteskip<Quoted>(p)) continue;
-    for(unsigned n = 0;; n++) {
-      if(key[n] == 0) { counter++; p += n; break; }
-      if(!chrequal<Insensitive>(key[n], p[n])) { p++; break; }
+  //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();
+    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; }
+
+      if(++matches >= Limit) break;
+      n += from.size();
     }
   }
-  if(counter == 0) return *this;
-  if(Limit) counter = min(counter, Limit);
+  if(matches == 0) return self;
 
-  char* t = data();
-  char* base = nullptr;
-  signed displacement = token.size() - key.size();
-  signed displacementSize = displacement * counter;
+  //in-place overwrite
+  if(to.size() == from.size()) {
+    char* p = self.pointer();
 
-  if(token.size() > key.size()) {
-    t = base = strduplicate(data());
-    reserve((unsigned)(p - data()) + displacementSize);
-  }
-  char* o = data();
+    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; } }
+      if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }
 
-  while(*t && counter) {
-    if(quotecopy<Quoted>(o, t)) continue;
-    for(unsigned n = 0;; n++) {
-      if(key[n] == 0) { counter--; memcpy(o, token, token.size()); t += key.size(); o += token.size(); break; }
-      if(!chrequal<Insensitive>(key[n], t[n])) { *o++ = *t++; break; }
+      memory::copy(p + n, to.data(), to.size());
+
+      if(!--remaining) break;
+      n += from.size();
     }
   }
-  do *o++ = *t; while(*t++);
-  if(base) free(base);
 
-  resize(_size + displacementSize);
-  return *this;
+  //left-to-right shrink
+  else if(to.size() < from.size()) {
+    char* p = self.pointer();
+    signed offset = 0;
+    signed base = 0;
+
+    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; } }
+      if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }
+
+      if(offset) memory::move(p + offset, p + base, n - base);
+      memory::copy(p + offset + (n - base), to.data(), to.size());
+      offset += (n - base) + to.size();
+
+      n += from.size();
+      base = n;
+      if(!--remaining) break;
+    }
+
+    memory::move(p + offset, p + base, size - base);
+    self.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.pointer();
+
+    signed offset = self.size();
+    signed base = size;
+
+    for(signed n = size, remaining = matches; n >= (signed)from.size();) {  //quoted reused from parent scope since we are iterating backward
+      if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n--; continue; } if(quoted) { n--; continue; } }
+      if(_compare<Insensitive>(p + n - from.size(), size - n + from.size(), from.data(), from.size())) { n--; continue; }
+
+      memory::move(p + offset - (base - n), p + base - (base - n), base - n);
+      memory::copy(p + offset - (base - n) - to.size(), to.data(), to.size());
+      offset -= (base - n) + to.size();
+
+      if(!--remaining) break;
+      n -= from.size();
+      base = n;
+    }
+  }
+
+  return self;
 }
 
-template<unsigned Limit> string& string::replace(rstring key, rstring token) { return ureplace<Limit, false, false>(key, token); }
-template<unsigned Limit> string& string::ireplace(rstring key, rstring token) { return ureplace<Limit, true, false>(key, token); }
-template<unsigned Limit> string& string::qreplace(rstring key, rstring token) { return ureplace<Limit, false, true>(key, token); }
-template<unsigned Limit> string& string::iqreplace(rstring key, rstring token) { return ureplace<Limit, true, true>(key, token); }
+template<unsigned L> auto replace(string& self, rstring from, rstring to) -> string& { return _replace<L, 0, 0>(self, from, to); }
+template<unsigned L> auto ireplace(string& self, rstring from, rstring to) -> string& { return _replace<L, 1, 0>(self, from, to); }
+template<unsigned L> auto qreplace(string& self, rstring from, rstring to) -> string& { return _replace<L, 0, 1>(self, from, to); }
+template<unsigned L> auto iqreplace(string& self, rstring from, rstring to) -> string& { return _replace<L, 1, 1>(self, from, to); }
 
 };
 
diff --git a/nall/string/split.hpp b/nall/string/split.hpp
index 90521e1f..f5c5e516 100644
--- a/nall/string/split.hpp
+++ b/nall/string/split.hpp
@@ -2,36 +2,46 @@
 
 namespace nall {
 
-template<unsigned Limit, bool Insensitive, bool Quoted> lstring& lstring::usplit(rstring key, rstring base) {
-  reset();
-  if(key.size() == 0) return *this;
+template<unsigned Limit, bool Insensitive, bool Quoted> auto _split(lstring& self, rstring source, rstring find) -> lstring& {
+  self.reset();
+  if(find.size() == 0) return self;
 
-  const char* b = base;
-  const char* p = base;
+  const char* p = source.data();
+  signed size = source.size();
+  signed base = 0;
+  signed matches = 0;
 
-  while(*p) {
-    if(Limit) if(size() >= Limit) break;
-    if(quoteskip<Quoted>(p)) continue;
-    for(unsigned n = 0;; n++) {
-      if(key[n] == 0) {
-        append(substr(b, 0, p - b));
-        p += n;
-        b = p;
-        break;
-      }
-      if(!chrequal<Insensitive>(key[n], p[n])) { p++; break; }
-    }
+  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(matches >= Limit) break;
+
+    string& s = self(matches);
+    s.resize(n - base);
+    memory::copy(s.pointer(), p + base, n - base);
+
+    n += find.size();
+    base = n;
+    matches++;
   }
 
-  append(b);
-  return *this;
+  string& s = self(matches);
+  s.resize(size - base);
+  memory::copy(s.pointer(), p + base, size - base);
+
+  return self;
 }
 
-template<unsigned Limit> lstring& lstring::split(rstring key, rstring src) { return usplit<Limit, false, false>(key, src); }
-template<unsigned Limit> lstring& lstring::isplit(rstring key, rstring src) { return usplit<Limit, true, false>(key, src); }
-template<unsigned Limit> lstring& lstring::qsplit(rstring key, rstring src) { return usplit<Limit, false, true>(key, src); }
-template<unsigned Limit> lstring& lstring::iqsplit(rstring key, rstring src) { return usplit<Limit, true, true>(key, src); }
+template<unsigned L> auto split(string& self, rstring on) -> lstring { return lstring().split<L>(self, on); }
+template<unsigned L> auto isplit(string& self, rstring on) -> lstring { return lstring().isplit<L>(self, on); }
+template<unsigned L> auto qsplit(string& self, rstring on) -> lstring { return lstring().qsplit<L>(self, on); }
+template<unsigned L> auto iqsplit(string& self, rstring on) -> lstring { return lstring().iqsplit<L>(self, on); }
 
-};
+template<unsigned L> auto string::split(rstring on) const -> lstring { return lstring().split<L>(*this, on); }
+template<unsigned L> auto string::isplit(rstring on) const -> lstring { return lstring().isplit<L>(*this, on); }
+template<unsigned L> auto string::qsplit(rstring on) const -> lstring { return lstring().qsplit<L>(*this, on); }
+template<unsigned L> auto string::iqsplit(rstring on) const -> lstring { return lstring().iqsplit<L>(*this, on); }
+
+}
 
 #endif
diff --git a/nall/string/transform/cml.hpp b/nall/string/transform/cml.hpp
new file mode 100644
index 00000000..17a1b266
--- /dev/null
+++ b/nall/string/transform/cml.hpp
@@ -0,0 +1,93 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+/* CSS Markup Language (CML) v1.0 parser
+ * revision 0.01
+ */
+
+namespace nall { namespace {
+
+struct CML {
+  CML(const string& filedata, const string& pathname);
+  CML(const string& filename);
+  auto output() -> string;
+
+private:
+  struct State {
+    string output;
+  } state;
+
+  struct Variable {
+    string name;
+    string value;
+  };
+  vector<Variable> variables;
+
+  auto parse(const string& filedata, const string& pathname) -> bool;
+};
+
+CML::CML(const string& filedata, const string& pathname) {
+  parse(filedata, pathname);
+}
+
+CML::CML(const string& filename) {
+  parse(string::read(filename), filename.pathname());
+}
+
+auto CML::output() -> string {
+  return state.output;
+}
+
+auto CML::parse(const string& filedata, const string& pathname) -> bool {
+  auto vendorAppend = [&](const string& name, const string& value) {
+    state.output.append("  -moz-", name, ": ", value, ";\n");
+    state.output.append("  -webkit-", name, ": ", value, ";\n");
+  };
+
+  for(auto& block : filedata.split("\n\n")) {
+    lstring lines = block.rstrip().split("\n");
+    string name = lines.takeFirst();
+
+    if(ltrim(name, "include ")) {
+      string filename{pathname, name};
+      parse(string::read(filename), filename.pathname());
+      continue;
+    }
+
+    if(name == "variables") {
+      for(auto& line : lines) {
+        auto data = line.split<1>(":").strip();
+        variables.append({data(0), data(1)});
+      }
+      continue;
+    }
+
+    state.output.append(name, " {\n");
+    for(auto& line : lines) {
+      auto data = line.split<1>(":").strip();
+      auto name = data(0), value = data(1);
+      while(auto offset = value.find("var(")) {
+        bool found = false;
+        if(auto length = value.findFrom(*offset, ")")) {
+          string name = value.slice(*offset + 4, *length - 4);
+          for(auto& variable : variables) {
+            if(variable.name == name) {
+              value = {value.slice(0, *offset), variable.value, value.slice(*offset + *length + 1)};
+              found = true;
+              break;
+            }
+          }
+        }
+        if(!found) break;
+      }
+      state.output.append("  ", name, ": ", value, ";\n");
+      if(name == "box-sizing") vendorAppend(name, value);
+    }
+    state.output.append("}\n\n");
+  }
+
+  return true;
+}
+
+}}
+
+#endif
diff --git a/nall/string/transform/dml.hpp b/nall/string/transform/dml.hpp
new file mode 100644
index 00000000..0c5e94d5
--- /dev/null
+++ b/nall/string/transform/dml.hpp
@@ -0,0 +1,259 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+/* Document Markup Language (DML) v1.0 parser
+ * revision 0.01
+ */
+
+namespace nall { namespace {
+
+struct DML {
+  struct Settings {
+    bool allowHTML = true;
+    bool sectioned = true;
+  } settings;
+
+  DML(const string& filedata, const string& pathname);
+  DML(const string& filename);
+  auto output() -> string;
+
+private:
+  struct State {
+    string output;
+    unsigned sections = 0;
+  } state;
+
+  auto parse(const string& filedata, const string& pathname) -> bool;
+  auto parseBlock(string& block, const string& pathname) -> bool;
+  auto count(const string& text, char value) -> unsigned;
+  auto escape(const string& text) -> string;
+  auto markup(const string& text) -> string;
+};
+
+DML::DML(const string& filedata, const string& pathname) {
+  parse(filedata, pathname);
+}
+
+DML::DML(const string& filename) {
+  parse(string::read(filename), filename.pathname());
+}
+
+auto DML::output() -> string {
+  return state.output;
+}
+
+auto DML::parse(const string& filedata, const string& pathname) -> bool {
+  auto blocks = filedata.split("\n\n");
+  for(auto& block : blocks) parseBlock(block, pathname);
+  if(settings.sectioned && state.sections) state.output.append("</section>\n");
+  return true;
+}
+
+auto DML::parseBlock(string& block, const string& pathname) -> bool {
+  if(block.rstrip().empty()) return true;
+  auto lines = block.split("\n");
+
+  //include
+  if(block.beginsWith("{{include}}")) {
+    string filename{pathname, block.ltrim("{{include}}").strip()};
+    parse(string::read(filename), filename.pathname());
+  }
+
+  //html
+  else if(ltrim(block, "{{html}}") && settings.allowHTML) {
+    auto data = lines.takeFirst();
+    if(ltrim(data, "{{html}} ")) state.output.append(data, "\n");
+    for(auto& line : lines) {
+      if(ltrim(line, "  ")) state.output.append(line, "\n");
+    }
+  }
+
+  //header
+  else if(block.beginsWith("# ")) {
+    if(settings.sectioned) {
+      if(state.sections++) state.output.append("</section>");
+      state.output.append("<section>");
+    }
+    auto content = lines.takeFirst().ltrim("# ").split<1>(" => ");
+    auto data = markup(content[0]);
+    auto name = escape(content(1, data.crc32()));
+    state.output.append("<header id=\"", name, "\">", data);
+    for(auto& line : lines) {
+      if(!line.beginsWith("# ")) continue;
+      state.output.append("<span>", line.ltrim("# "), "</span>");
+    }
+    state.output.append("</header>\n");
+  }
+
+  //subheader
+  else if(auto depth = count(block, '=')) {
+    auto content = lines.takeFirst().slice(depth + 1).split<1>(" => ");
+    auto data = markup(content[0]);
+    auto name = escape(content(1, data.crc32()));
+    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("</h", depth, ">\n");
+    }
+  }
+
+  //contents
+  else if(count(block, '-')) {
+    state.output.append("<nav>\n");
+    unsigned level = 0;
+    for(auto& line : lines) {
+      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<1>(" => ");
+        auto data = markup(content[0]);
+        auto name = escape(content(1, data.crc32()));
+        state.output.append("<li><a href=\"#", name, "\">", data, "</a></li>\n");
+      }
+    }
+    while(level--) state.output.append("</ul>\n");
+    state.output.append("</nav>\n");
+  }
+
+  //list
+  else if(count(block, '*')) {
+    unsigned level = 0;
+    for(auto& line : lines) {
+      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));
+        state.output.append("<li>", data, "</li>\n");
+      }
+    }
+    while(level--) state.output.append("</ul>\n");
+  }
+
+  //quote
+  else if(count(block, '>')) {
+    unsigned level = 0;
+    for(auto& line : lines) {
+      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));
+        state.output.append(data, "\n");
+      }
+    }
+    while(level--) state.output.append("</blockquote>\n");
+  }
+
+  //code
+  else if(block.beginsWith("  ")) {
+    state.output.append("<pre>");
+    for(auto& line : lines) {
+      if(!ltrim(line, "  ")) continue;
+      state.output.append(escape(line), "\n");
+    }
+    state.output.rtrim("\n").append("</pre>\n");
+  }
+
+  //divider
+  else if(block.equals("---")) {
+    state.output.append("<hr>\n");
+  }
+
+  //paragraph
+  else {
+    state.output.append("<p>", markup(block), "</p>\n");
+  }
+
+  return true;
+}
+
+auto DML::count(const string& text, char value) -> unsigned {
+  for(unsigned n = 0; n < text.size(); n++) {
+    if(text[n] != value) {
+      if(text[n] == ' ') return n;
+      break;
+    }
+  }
+  return 0;
+}
+
+auto DML::escape(const string& text) -> string {
+  string output;
+  for(unsigned n = 0; n < text.size();) {
+    char x = text[n++];
+    if(x == '&') { output.append("&amp;"); continue; }
+    if(x == '<') { output.append("&lt;"); continue; }
+    if(x == '>') { output.append("&gt;"); continue; }
+    if(x == '"') { output.append("&quot;"); continue; }
+    output.append(x);
+  }
+  return output;
+}
+
+auto DML::markup(const string& text) -> string {
+  string output;
+  char flagStrong = 0;
+  char flagEmphasis = 0;
+  char flagInsert = 0;
+  char flagDelete = 0;
+  char flagCode = 0;
+
+  for(unsigned n = 0; n < text.size();) {
+    char x = text[n], y = text[n + 1];
+
+    if(x == '[' && y == '\\') { output.append('['); n += 2; continue; }
+
+    if(x == '[' && y == '*' && flagStrong == 0) { flagStrong = 1; output.append("<strong>"); n += 2; continue; }
+    if(x == '*' && y == ']' && flagStrong == 1) { flagStrong = 0; output.append("</strong>"); n += 2; continue; }
+
+    if(x == '[' && y == '/' && flagEmphasis == 0) { flagEmphasis = 1; output.append("<em>"); n += 2; continue; }
+    if(x == '/' && y == ']' && flagEmphasis == 1) { flagEmphasis = 0; output.append("</em>"); n += 2; continue; }
+
+    if(x == '[' && y == '_' && flagInsert == 0) { flagInsert = 1; output.append("<ins>"); n += 2; continue; }
+    if(x == '_' && y == ']' && flagInsert == 1) { flagInsert = 0; output.append("</ins>"); n += 2; continue; }
+
+    if(x == '[' && y == '-' && flagDelete == 0) { flagDelete = 1; output.append("<del>"); n += 2; continue; }
+    if(x == '-' && y == ']' && flagDelete == 1) { flagDelete = 0; output.append("</del>"); n += 2; continue; }
+
+    if(x == '[' && y == '|' && flagCode == 0) { flagCode = 1; output.append("<code>"); n += 2; continue; }
+    if(x == '|' && y == ']' && flagCode == 1) { flagCode = 0; output.append("</code>"); n += 2; continue; }
+
+    if(x == '[' && y == '[') {
+      if(auto length = text.findFrom(n + 2, "]]")) {
+        lstring content = text.slice(n + 2, *length).split<1>(" => ");
+        output.append("<a href=\"", escape(content[0]), "\">", escape(content(1, content[0])), "</a>");
+        n += *length + 4;
+        continue;
+      }
+    }
+
+    if(x == '[' && y == '{') {
+      if(auto length = text.findFrom(n + 2, "}]")) {
+        lstring content = text.slice(n + 2, *length).split<1>(" => ");
+        output.append("<img src=\"", escape(content[0]), "\" alt=\"", escape(content(1, "")), "\">");
+        n += *length + 4;
+        continue;
+      }
+    }
+
+    if(x == '&') { output.append("&amp;"); n++; continue; }
+    if(x == '<') { output.append("&lt;"); n++; continue; }
+    if(x == '>') { output.append("&gt;"); n++; continue; }
+    if(x == '"') { output.append("&quot;"); n++; continue; }
+
+    output.append(x);
+    n++;
+  }
+
+  if(flagStrong) output.append("</strong>");
+  if(flagEmphasis) output.append("</em>");
+  if(flagInsert) output.append("</ins>");
+  if(flagDelete) output.append("</del>");
+  if(flagCode) output.append("</code>");
+  return output;
+}
+
+}}
+
+#endif
diff --git a/nall/string/trim.hpp b/nall/string/trim.hpp
new file mode 100644
index 00000000..5aaff55d
--- /dev/null
+++ b/nall/string/trim.hpp
@@ -0,0 +1,82 @@
+#ifdef NALL_STRING_INTERNAL_HPP
+
+namespace nall {
+
+auto trim(string& self, rstring lhs, rstring rhs) -> bool {
+  if(lhs.size() + rhs.size() > self.size()) return false;
+  if(memory::compare(self.data(), lhs.data(), lhs.size()) != 0) return false;
+  if(memory::compare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
+  self.resize(self.size() - rhs.size());
+  self.remove(0, lhs.size());
+  return true;
+}
+
+auto ltrim(string& self, rstring lhs) -> bool {
+  if(lhs.size() > self.size()) return false;
+  if(memory::compare(self.data(), lhs.data(), lhs.size()) != 0) return false;
+  self.remove(0, lhs.size());
+  return true;
+}
+
+auto rtrim(string& self, rstring rhs) -> bool {
+  if(rhs.size() > self.size()) return false;
+  if(memory::compare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
+  self.resize(self.size() - rhs.size());
+  return true;
+}
+
+auto itrim(string& self, rstring lhs, rstring rhs) -> bool {
+  if(lhs.size() + rhs.size() > self.size()) return false;
+  if(memory::icompare(self.data(), lhs.data(), lhs.size()) != 0) return false;
+  if(memory::icompare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
+  self.resize(self.size() - rhs.size());
+  self.remove(0, lhs.size());
+  return true;
+}
+
+auto iltrim(string& self, rstring lhs) -> bool {
+  if(lhs.size() > self.size()) return false;
+  if(memory::icompare(self.data(), lhs.data(), lhs.size()) != 0) return false;
+  self.remove(0, lhs.size());
+  return true;
+}
+
+auto irtrim(string& self, rstring rhs) -> bool {
+  if(rhs.size() > self.size()) return false;
+  if(memory::icompare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
+  self.resize(self.size() - rhs.size());
+  return true;
+}
+
+auto strip(string& self) -> bool {
+  return rstrip(self) | lstrip(self);
+}
+
+auto lstrip(string& self) -> bool {
+  unsigned size = 0;
+  while(size < self.size()) {
+    char input = self[size];
+    if(input != ' ' && input != '\t' && input != '\r' && input != '\n') break;
+    size++;
+  }
+  if(size == 0) return false;
+  self.remove(0, size);
+  return true;
+}
+
+auto rstrip(string& self) -> bool {
+  unsigned size = 0;
+  while(size < self.size()) {
+    bool matched = false;
+    char input = self[self.size() - size - 1];
+    if(input != ' ' && input != '\t' && input != '\r' && input != '\n') break;
+    size++;
+  }
+  if(size == 0) return false;
+  self.resize(self.size() - size);
+  return true;
+}
+
+}
+
+#endif
diff --git a/nall/string/utility.hpp b/nall/string/utility.hpp
index 429636cd..e4f01107 100644
--- a/nall/string/utility.hpp
+++ b/nall/string/utility.hpp
@@ -2,46 +2,107 @@
 
 namespace nall {
 
-string substr(rstring source, unsigned offset, unsigned length) {
+auto string::read(const string& filename) -> string {
+  #if !defined(_WIN32)
+  FILE* fp = fopen(filename, "rb");
+  #else
+  FILE* fp = _wfopen(utf16_t(filename), L"rb");
+  #endif
+
   string result;
-  if(length == ~0u) length = source.size() - offset;
-  result.resize(length);
-  memcpy(result.data(), source.data() + offset, length);
+  if(!fp) return result;
+
+  fseek(fp, 0, SEEK_END);
+  signed filesize = ftell(fp);
+  if(filesize < 0) return fclose(fp), result;
+
+  rewind(fp);
+  result.resize(filesize);
+  fread(result.pointer(), 1, filesize, fp);
+  return fclose(fp), result;
+}
+
+template<unsigned L> auto string::repeat(const string& pattern) -> string {
+  string result;
+  unsigned times = L;
+  while(times--) result.append(pattern);
   return result;
 }
 
-string sha256(const uint8_t* data, unsigned size) {
-  sha256_ctx sha;
-  uint8_t hash[32];
-  sha256_init(&sha);
-  sha256_chunk(&sha, data, size);
-  sha256_final(&sha);
-  sha256_hash(&sha, hash);
-  string result;
-  for(auto& byte : hash) result.append(hex<2>(byte));
+auto fill(string& self, char fill) -> string& {
+  memory::fill(self.pointer(), self.size(), fill);
+  return self;
+}
+
+auto hash(const string& self) -> unsigned {
+  const char* p = self.data();
+  unsigned size = self.size();
+  unsigned result = 5381;
+  while(size--) result = (result << 5) + result + *p++;
   return result;
 }
 
-bool tokenize(lstring& list, const char* s, const char* p) {
-  while(*s) {
-    if(*p == '*') {
-      const char* b = s;
-      while(*s) {
-        if(tokenize(list, s++, p + 1)) {
-          list.prepend(substr(b, 0, --s - b));
-          return true;
-        }
-      }
-      list.prepend(b);
-      return !*++p;
-    }
-    if(*s++ != *p++) return false;
+auto remove(string& self, unsigned offset, unsigned length) -> string& {
+  char* p = self.pointer();
+  length = min(length, self.size());
+  memory::move(p + offset, p + offset + length, self.size() - length);
+  return self.resize(self.size() - length);
+}
+
+auto reverse(string& self) -> string& {
+  char* p = self.pointer();
+  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;
+}
+
+//+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;
+
+  bool right = length >= 0;
+  length = abs(length);
+
+  if(size < length) {  //expand
+    self.resize(length);
+    char* p = self.pointer();
+    unsigned displacement = length - size;
+    if(right) memory::move(p + displacement, p, size);
+    else p += size;
+    while(displacement--) *p++ = fill;
+  } else {  //shrink
+    char* p = self.pointer();
+    unsigned displacement = size - length;
+    if(right) memory::move(p, p + displacement, length);
+    self.resize(length);
   }
-  while(*p == '*') { list.prepend(s); p++; }
-  return !*p;
+
+  return self;
 }
 
-char* integer(char* result, intmax_t value) {
+auto slice(const string& self, signed offset, signed length) -> string {
+  string result;
+  if(offset < self.size()) {
+    if(length < 0) length = self.size() - offset;
+    result.resize(length);
+    memory::copy(result.pointer(), self.data() + offset, length);
+  }
+  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.pointer(), source.data() + offset, length);
+  return result;
+}
+
+auto integer(char* result, intmax_t value) -> char* {
   bool negative = value < 0;
   if(negative) value = -value;
 
@@ -54,14 +115,13 @@ char* integer(char* result, intmax_t value) {
     value /= 10;
   } while(value);
   if(negative) buffer[size++] = '-';
-//buffer[size++] = negative ? '-' : '+';
 
   for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
   result[size] = 0;
   return result;
 }
 
-char* decimal(char* result, uintmax_t value) {
+auto decimal(char* result, uintmax_t value) -> char* {
   char buffer[64];
   unsigned size = 0;
 
@@ -79,7 +139,7 @@ char* decimal(char* result, uintmax_t value) {
 //using sprintf is certainly not the most ideal method to convert
 //a double to a string ... but attempting to parse a double by
 //hand, digit-by-digit, results in subtle rounding errors.
-unsigned real(char* str, long double value) {
+auto real(char* result, long double value) -> unsigned {
   char buffer[256];
   #ifdef _WIN32
   //Windows C-runtime does not support long double via sprintf()
@@ -101,17 +161,10 @@ unsigned real(char* str, long double value) {
   }
 
   unsigned length = strlen(buffer);
-  if(str) strcpy(str, buffer);
+  if(result) strcpy(result, buffer);
   return length + 1;
 }
 
-string real(long double value) {
-  string temp;
-  temp.resize(real(nullptr, value));
-  real(temp.data(), value);
-  return temp;
-}
-
 }
 
 #endif
diff --git a/nall/string/variadic.hpp b/nall/string/variadic.hpp
deleted file mode 100644
index fed6b7f3..00000000
--- a/nall/string/variadic.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-void sprint(string& output) {
-}
-
-template<typename T, typename... Args>
-void sprint(string& output, const T& value, Args&&... args) {
-  output._append(make_string(value));
-  sprint(output, std::forward<Args>(args)...);
-}
-
-template<typename... Args> void print(Args&&... args) {
-  printf("%s", (const char*)string(std::forward<Args>(args)...));
-}
-
-}
-
-#endif
diff --git a/nall/string/wrapper.hpp b/nall/string/wrapper.hpp
deleted file mode 100644
index 0eadf3dd..00000000
--- a/nall/string/wrapper.hpp
+++ /dev/null
@@ -1,124 +0,0 @@
-#ifdef NALL_STRING_INTERNAL_HPP
-
-namespace nall {
-
-template<unsigned limit> lstring string::split(rstring key) const { lstring result; result.split<limit>(key, data()); return result; }
-template<unsigned limit> lstring string::isplit(rstring key) const { lstring result; result.isplit<limit>(key, data()); return result; }
-template<unsigned limit> lstring string::qsplit(rstring key) const { lstring result; result.qsplit<limit>(key, data()); return result; }
-template<unsigned limit> lstring string::iqsplit(rstring key) const { lstring result; result.iqsplit<limit>(key, data()); return result; }
-
-bool string::match(rstring source) const { return nall::strmatch(data(), source); }
-bool string::imatch(rstring source) const { return nall::istrmatch(data(), source); }
-
-signed string::compare(rstring source) const {
-  return strcmp(data(), source.data());
-}
-
-signed string::icompare(rstring source) const {
-  return istrcmp(data(), source.data());
-}
-
-bool string::equals(rstring source) const {
-  if(size() != source.size()) return false;
-  return compare(source) == 0;
-}
-
-bool string::iequals(rstring source) const {
-  if(size() != source.size()) return false;
-  return icompare(source) == 0;
-}
-
-bool string::beginsWith(rstring source) const {
-  if(source.size() > size()) return false;
-  return memcmp(data(), source.data(), source.size()) == 0;
-}
-
-bool string::ibeginsWith(rstring source) const {
-  if(source.size() > size()) return false;
-  return imemcmp(data(), source.data(), source.size()) == 0;
-}
-
-bool string::endsWith(rstring source) const {
-  if(source.size() > size()) return false;
-  return memcmp(data() + size() - source.size(), source.data(), source.size()) == 0;
-}
-
-bool string::iendsWith(rstring source) const {
-  if(source.size() > size()) return false;
-  return imemcmp(data() + size() - source.size(), source.data(), source.size()) == 0;
-}
-
-string string::slice(unsigned offset, unsigned length) const {
-  if(offset >= size()) return "";
-  if(length == ~0u) length = size() - offset;
-  return substr(data(), offset, length);
-}
-
-string& string::lower() { nall::strlower(data()); return *this; }
-string& string::upper() { nall::strupper(data()); return *this; }
-string& string::qlower() { nall::qstrlower(data()); return *this; }
-string& string::qupper() { nall::qstrupper(data()); return *this; }
-
-string& string::transform(rstring before, rstring after) { nall::strtr(data(), before, after); return *this; }
-
-string& string::reverse() {
-  unsigned length = size(), pivot = length >> 1;
-  for(signed x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(data()[x], data()[y]);
-  return *this;
-}
-
-template<unsigned Limit> string& string::ltrim(rstring key) {
-  if(key.size() == 0) return *this;
-  unsigned limit = Limit ? Limit : ~0u, offset = 0;
-
-  while(limit && size() - offset >= key.size()) {
-    if(memcmp(data() + offset, key.data(), key.size())) break;
-    offset += key.size();
-    limit--;
-  }
-
-  if(offset) memmove(data(), data() + offset, size() - offset);
-  resize(size() - offset);
-  return *this;
-}
-
-template<unsigned Limit> string& string::rtrim(rstring key) {
-  if(key.size() == 0) return *this;
-  unsigned limit = Limit ? Limit : ~0u, offset = 0;
-
-  while(limit && size() - offset >= key.size()) {
-    if(memcmp(data() + size() - key.size() - offset, key.data(), key.size())) break;
-    offset += key.size();
-    limit--;
-  }
-
-  resize(size() - offset);
-  return *this;
-}
-
-template<unsigned Limit> string& string::trim(rstring key) {
-  rtrim<Limit>(key);
-  ltrim<Limit>(key);
-  return *this;
-}
-
-template<unsigned Limit> string& string::trim(rstring lkey, rstring rkey) {
-  rtrim<Limit>(rkey);
-  ltrim<Limit>(lkey);
-  return *this;
-}
-
-string& string::strip() {
-  nall::strip(data());
-  resize(length());
-  return *this;
-}
-
-maybe<unsigned> string::find(rstring key) const { return strpos(data(), key); }
-maybe<unsigned> string::ifind(rstring key) const { return istrpos(data(), key); }
-maybe<unsigned> string::qfind(rstring key) const { return qstrpos(data(), key); }
-maybe<unsigned> string::iqfind(rstring key) const { return iqstrpos(data(), key); }
-
-}
-
-#endif
diff --git a/nall/thread.hpp b/nall/thread.hpp
index 5cd2846c..e07339c6 100644
--- a/nall/thread.hpp
+++ b/nall/thread.hpp
@@ -1,66 +1,69 @@
 #ifndef NALL_THREAD_HPP
 #define NALL_THREAD_HPP
 
+//simple thread library
+//primary rationale is that std::thread does not support custom stack sizes
+//this is highly critical in certain applications such as threaded web servers
+//an added bonus is that it avoids licensing issues on Windows
+//win32-pthreads (needed for std::thread) is licensed under the GPL only
+
 #include <nall/platform.hpp>
 #include <nall/function.hpp>
 #include <nall/intrinsics.hpp>
 
-#if defined(PLATFORM_XORG) || defined(PLATFORM_MACOSX)
+#if defined(PLATFORM_BSD) || defined(PLATFORM_LINUX) || defined(PLATFORM_MACOSX)
 
 #include <pthread.h>
 
 namespace nall {
 
-inline void* thread_entry_point(void*);
-
 struct thread {
-  thread(function<void ()> entryPoint) : entryPoint(entryPoint), completed(false), dead(false) {
-    initialize();
-    pthread_create(&pthread, nullptr, thread_entry_point, (void*)this);
-  }
+  inline auto join() -> void;
 
-  ~thread() {
-    join();
-  }
+  static inline auto create(const function<void (uintptr_t)>& callback, uintptr_t parameter = 0, unsigned stacksize = 0) -> thread;
+  static inline auto detach() -> void;
+  static inline auto exit() -> void;
 
-  bool active() const {
-    return completed == false;
-  }
-
-  void join() {
-    if(dead) return;
-    dead = true;
-    pthread_join(pthread, nullptr);
-  }
-
-  static bool primary() {
-    initialize();
-    return pthread_equal(primaryThread(), pthread_self());
-  }
+  struct context {
+    function<void (uintptr_t)> callback;
+    uintptr_t parameter = 0;
+  };
 
 private:
-  pthread_t pthread;
-  function<void ()> entryPoint;
-  volatile bool completed, dead;
-  friend void* thread_entry_point(void*);
-
-  static void initialize() {
-    static bool initialized = false;
-    if(initialized) return;
-    initialized = true;
-    primaryThread() = pthread_self();
-  }
-
-  static pthread_t& primaryThread() {
-    static pthread_t thread;
-    return thread;
-  }
+  pthread_t handle;
 };
 
-void* thread_entry_point(void* parameter) {
-  thread* context = (thread*)parameter;
-  context->entryPoint();
-  context->completed = true;
+inline auto _threadCallback(void* parameter) -> void* {
+  auto context = (thread::context*)parameter;
+  context->callback(context->parameter);
+  delete context;
+  return nullptr;
+}
+
+auto thread::join() -> void {
+  pthread_join(handle, nullptr);
+}
+
+auto thread::create(const function<void (uintptr_t)>& callback, uintptr_t parameter, unsigned stacksize) -> thread {
+  thread instance;
+
+  auto context = new thread::context;
+  context->callback = callback;
+  context->parameter = parameter;
+
+  pthread_attr_t attr;
+  pthread_attr_init(&attr);
+  if(stacksize) pthread_attr_setstacksize(&attr, max(PTHREAD_STACK_MIN, stacksize));
+
+  pthread_create(&instance.handle, &attr, _threadCallback, (void*)context);
+  return instance;
+}
+
+auto thread::detach() -> void {
+  pthread_detach(pthread_self());
+}
+
+auto thread::exit() -> void {
   pthread_exit(nullptr);
 }
 
@@ -70,60 +73,66 @@ void* thread_entry_point(void* parameter) {
 
 namespace nall {
 
-inline DWORD WINAPI thread_entry_point(LPVOID);
-
 struct thread {
-  thread(function<void ()> entryPoint) : entryPoint(entryPoint), completed(false), dead(false) {
-    initialize();
-    hthread = CreateThread(nullptr, 0, thread_entry_point, (void*)this, 0, nullptr);
-  }
+  inline ~thread();
+  inline auto join() -> void;
 
-  ~thread() {
-    join();
-  }
+  static inline auto create(const function<void (uintptr_t)>& callback, uintptr_t parameter = 0, unsigned stacksize = 0) -> thread;
+  static inline auto detach() -> void;
+  static inline auto exit() -> void;
 
-  bool active() const {
-    return completed == false;
-  }
-
-  void join() {
-    if(dead) return;
-    dead = true;
-    WaitForSingleObject(hthread, INFINITE);
-    CloseHandle(hthread);
-  }
-
-  static bool primary() {
-    initialize();
-    return primaryThread() == GetCurrentThreadId();
-  }
+  struct context {
+    function<void (uintptr_t)> callback;
+    uintptr_t parameter = 0;
+  };
 
 private:
-  HANDLE hthread;
-  function<void ()> entryPoint;
-  volatile bool completed, dead;
-  friend DWORD WINAPI thread_entry_point(LPVOID);
-
-  static void initialize() {
-    static bool initialized = false;
-    if(initialized) return;
-    initialized = true;
-    primaryThread() = GetCurrentThreadId();
-  }
-
-  static DWORD& primaryThread() {
-    static DWORD thread;
-    return thread;
-  }
+  HANDLE handle = 0;
 };
 
-inline DWORD WINAPI thread_entry_point(LPVOID parameter) {
-  thread *context = (thread*)parameter;
-  context->entryPoint();
-  context->completed = true;
+inline auto WINAPI _threadCallback(void* parameter) -> DWORD {
+  auto context = (thread::context*)parameter;
+  context->callback(context->parameter);
+  delete context;
   return 0;
 }
 
+thread::~thread() {
+  if(handle) {
+    CloseHandle(handle);
+    handle = 0;
+  }
+}
+
+auto thread::join() -> void {
+  if(handle) {
+    WaitForSingleObject(handle, INFINITE);
+    CloseHandle(handle);
+    handle = 0;
+  }
+}
+
+auto thread::create(const function<void (uintptr_t)>& callback, uintptr_t parameter, unsigned stacksize) -> thread {
+  thread instance;
+
+  auto context = new thread::context;
+  context->callback = callback;
+  context->parameter = parameter;
+
+  instance.handle = CreateThread(nullptr, stacksize, _threadCallback, (void*)context, 0, nullptr);
+  return instance;
+}
+
+auto thread::detach() -> void {
+  //Windows threads do not use this concept:
+  //~thread() frees resources via CloseHandle()
+  //thread continues to run even after handle is closed
+}
+
+auto thread::exit() -> void {
+  ExitThread(0);
+}
+
 }
 
 #endif
diff --git a/nall/traits.hpp b/nall/traits.hpp
index 6a140c2b..8550714c 100644
--- a/nall/traits.hpp
+++ b/nall/traits.hpp
@@ -2,32 +2,51 @@
 #define NALL_TRAITS_HPP
 
 #include <type_traits>
+#include <utility>
 
 namespace nall {
+  using std::forward;
+  using std::move;
+  using std::decay;
+  using std::declval;
 
-template<typename T> class has_default_constructor {
-  template<signed> class receive_size{};
-  template<typename U> static signed sfinae(receive_size<sizeof U()>*);
-  template<typename U> static char sfinae(...);
+  using true_type = std::true_type;
+  using false_type = std::false_type;
 
-public:
-  enum : bool { value = sizeof(sfinae<T>(0)) == sizeof(signed) };
-};
+  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<bool C, typename T = bool> struct enable_if { typedef T type; };
-template<typename T> struct enable_if<false, T> {};
+namespace nall {
+  template<bool C> struct expression { static constexpr bool value = C; };
+}
 
-template<bool C, typename T, typename F> struct type_if { typedef T type; };
-template<typename T, typename F> struct type_if<false, T, F> { typedef F type; };
+namespace nall {
+  namespace traits {
+    enum class enable_type {};
+    enum class disable_type {};
 
-template<bool A, bool B> struct static_and { enum { value = false }; };
-template<> struct static_and<true, true> { enum { value = true }; };
+    template<bool C, typename T = void> struct enable_if { using type = T; };
+    template<typename T> struct enable_if<false, T> {};
 
-template<bool A, bool B> struct static_or { enum { value = false }; };
-template<> struct static_or<false, true> { enum { value = true }; };
-template<> struct static_or<true, false> { enum { value = true }; };
-template<> struct static_or<true, true> { enum { value = true }; };
+    template<bool C, typename T = void> struct disable_if { using type = T; };
+    template<typename T> struct disable_if<true, T> {};
+  }
 
+  template<typename C, typename T = void> using enable_if = typename traits::enable_if<C::value, T>::type;
+  template<typename C, typename T = void> using disable_if = typename traits::disable_if<C::value, T>::type;
+}
+
+namespace nall {
+  namespace traits {
+    template<bool C, typename T, typename F> struct type_if { using type = T; };
+    template<typename T, typename F> struct type_if<false, T, F> { using type = F; };
+  }
+
+  template<typename C, typename T, typename F> using type_if = typename traits::type_if<C::value, T, F>::type;
 }
 
 #endif
diff --git a/nall/varint.hpp b/nall/varint.hpp
index 944e193e..e77ffb30 100644
--- a/nall/varint.hpp
+++ b/nall/varint.hpp
@@ -52,7 +52,7 @@ struct varint {
 
 template<unsigned bits> struct uint_t {
 private:
-  typedef typename type_if<bits <= 8 * sizeof(unsigned), unsigned, uintmax_t>::type type_t;
+  using type_t = type_if<expression<bits <= 8 * sizeof(unsigned)>, unsigned, uintmax_t>;
   type_t data;
 
 public:
@@ -84,7 +84,7 @@ public:
 
 template<unsigned bits> struct int_t {
 private:
-  typedef typename type_if<bits <= 8 * sizeof(signed), signed, intmax_t>::type type_t;
+  using type_t = type_if<expression<bits <= 8 * sizeof(signed)>, signed, intmax_t>;
   type_t data;
 
 public:
diff --git a/nall/vector.hpp b/nall/vector.hpp
index d1da089b..2e866bab 100644
--- a/nall/vector.hpp
+++ b/nall/vector.hpp
@@ -109,7 +109,10 @@ public:
   }
 
   void insert(unsigned position, const T& data) {
-    if(position == 0) return prepend(data);
+    if(position == 0) {
+      prepend(data);
+      return;
+    }
     append(data);
     if(position == ~0u) return;
     for(signed n = objectsize - 1; n > position; n--) {
@@ -165,7 +168,7 @@ public:
     nall::sort(pool + poolbase, objectsize, lessthan);
   }
 
-  maybe<unsigned> find(const T& data) {
+  maybe<unsigned> find(const T& data) const {
     for(unsigned n = 0; n < objectsize; n++) if(pool[poolbase + n] == data) return n;
     return nothing;
   }
@@ -243,6 +246,7 @@ public:
 
   //copy
   inline vector& operator=(const vector& source) {
+    if(this == &source) return *this;
     reset();
     reserve(source.size());
     for(auto& data : source) append(data);
@@ -251,6 +255,7 @@ public:
 
   //move
   inline vector& operator=(vector&& source) {
+    if(this == &source) return *this;
     reset();
     pool = source.pool;
     poolbase = source.poolbase;
diff --git a/nall/windows/detour.hpp b/nall/windows/detour.hpp
index 34a5d3f8..ce583912 100644
--- a/nall/windows/detour.hpp
+++ b/nall/windows/detour.hpp
@@ -75,7 +75,7 @@ bool detour::insert(const string& moduleName, const string& functionName, void*&
     #if 1
     string output = { "detour::insert(", moduleName, "::", functionName, ") failed: " };
     for(unsigned n = 0; n < 16; n++) output.append(hex<2>(sourceData[n]), " ");
-    output.rtrim<1>(" ");
+    output.rtrim(" ");
     MessageBoxA(0, output, "nall::detour", MB_OK);
     #endif
     return false;
diff --git a/nall/windows/registry.hpp b/nall/windows/registry.hpp
index f702afe4..7c9aff2c 100644
--- a/nall/windows/registry.hpp
+++ b/nall/windows/registry.hpp
@@ -92,7 +92,7 @@ struct registry {
         wchar_t name[NWR_SIZE] = L"";
         DWORD size = NWR_SIZE * sizeof(wchar_t);
         RegEnumKeyEx(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr);
-        result.append({(const char*)utf8_t(name), "/"});
+        result.append(string{(const char*)utf8_t(name), "/"});
       }
       for(unsigned n = 0; n < nodes; n++) {
         wchar_t name[NWR_SIZE] = L"";
diff --git a/nall/windows/utf8.hpp b/nall/windows/utf8.hpp
index dc3ea3bb..d6e8a480 100644
--- a/nall/windows/utf8.hpp
+++ b/nall/windows/utf8.hpp
@@ -6,11 +6,9 @@
 
 #if defined(_WIN32)
 
-#undef UNICODE
-#undef _WIN32_WINNT
-#undef  NOMINMAX
+#undef  UNICODE
 #define UNICODE
-#define _WIN32_WINNT 0x0501
+#undef  NOMINMAX
 #define NOMINMAX
 #include <winsock2.h>
 #include <windows.h>
diff --git a/nall/zip.hpp b/nall/zip.hpp
index 2c27a328..a0f05e30 100644
--- a/nall/zip.hpp
+++ b/nall/zip.hpp
@@ -3,8 +3,8 @@
 
 //creates uncompressed ZIP archives
 
-#include <nall/crc32.hpp>
 #include <nall/string.hpp>
+#include <nall/hash/crc32.hpp>
 
 namespace nall {
 
@@ -21,7 +21,7 @@ struct zip {
   //append file: append("path/file", data, size);
   void append(string filename, const uint8_t* data = nullptr, unsigned size = 0u) {
     filename.transform("\\", "/");
-    uint32_t checksum = crc32_calculate(data, size);
+    uint32_t checksum = Hash::CRC32(data, size).value();
     directory.append({filename, checksum, size, fp.offset()});
 
     fp.writel(0x04034b50, 4);         //signature
diff --git a/out/.gitignore b/out/.gitignore
index cc3f712d..98f54497 100644
--- a/out/.gitignore
+++ b/out/.gitignore
@@ -1,2 +1 @@
 higan
-loki
diff --git a/phoenix/Makefile b/phoenix/Makefile
deleted file mode 100644
index 957e361b..00000000
--- a/phoenix/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-ifeq ($(platform),)
-  phoenixflags = $(cppflags) $(flags) -DPHOENIX_REFERENCE
-  phoenixlink =
-else ifeq ($(platform),windows)
-  phoenixflags = $(cppflags) $(flags) -DPHOENIX_WINDOWS
-  phoenixlink = -lkernel32 -luser32 -lgdi32 -ladvapi32 -lole32 -lcomctl32 -lcomdlg32 -luxtheme -lmsimg32 -lshlwapi
-else ifeq ($(platform),macosx)
-  phoenixflags = $(objcppflags) $(flags) -DPHOENIX_COCOA
-  phoenixlink = -framework Cocoa -framework Carbon
-else
-  ifeq ($(phoenix),)
-    phoenix := gtk
-  endif
-
-  ifeq ($(phoenix),gtk)
-    phoenixflags = $(cppflags) $(flags) -DPHOENIX_GTK `pkg-config --cflags gtk+-2.0`
-    phoenixlink = `pkg-config --libs gtk+-2.0`
-  endif
-
-  ifeq ($(phoenix),qt)
-    phoenixflags = $(cppflags) $(flags) -DPHOENIX_QT `pkg-config --cflags QtCore QtGui`
-    phoenixlink = `pkg-config --libs QtCore QtGui`
-  endif
-endif
diff --git a/phoenix/core/core.cpp b/phoenix/core/core.cpp
deleted file mode 100644
index ac4cd54b..00000000
--- a/phoenix/core/core.cpp
+++ /dev/null
@@ -1,2046 +0,0 @@
-#if defined(PHOENIX_WINDOWS)
-  #include "../windows/header.hpp"
-#elif defined(PHOENIX_QT)
-  #include "../qt/header.hpp"
-#elif defined(PHOENIX_GTK)
-  #include "../gtk/header.hpp"
-#elif defined(PHOENIX_COCOA)
-  #include "../cocoa/header.hpp"
-#elif defined(PHOENIX_REFERENCE)
-  #include "../reference/header.hpp"
-#endif
-
-#include "core.hpp"
-using namespace nall;
-
-namespace phoenix {
-  #include "state.hpp"
-  #include "layout/fixed-layout.cpp"
-  #include "layout/horizontal-layout.cpp"
-  #include "layout/vertical-layout.cpp"
-}
-
-#if defined(PHOENIX_WINDOWS)
-  #include "../windows/platform.cpp"
-#elif defined(PHOENIX_QT)
-  #include "../qt/platform.cpp"
-#elif defined(PHOENIX_GTK)
-  #include "../gtk/platform.cpp"
-#elif defined(PHOENIX_COCOA)
-  #include "../cocoa/platform.cpp"
-#elif defined(PHOENIX_REFERENCE)
-  #include "../reference/platform.cpp"
-#endif
-
-namespace phoenix {
-
-//Application
-//===========
-
-function<void ()> Application::main;
-
-function<void ()> Application::Windows::onModalBegin;
-function<void ()> Application::Windows::onModalEnd;
-
-function<void ()> Application::Cocoa::onAbout;
-function<void ()> Application::Cocoa::onActivate;
-function<void ()> Application::Cocoa::onPreferences;
-function<void ()> Application::Cocoa::onQuit;
-
-void Application::run() {
-  return pApplication::run();
-}
-
-bool Application::pendingEvents() {
-  return pApplication::pendingEvents();
-}
-
-void Application::processEvents() {
-  return pApplication::processEvents();
-}
-
-void Application::quit() {
-  applicationState.quit = true;
-  return pApplication::quit();
-}
-
-void Application::setName(const string& name) {
-  applicationState.name = name;
-}
-
-void Application::initialize() {
-  static bool initialized = false;
-  if(initialized == false) {
-    initialized = true;
-    return pApplication::initialize();
-  }
-}
-
-//Color
-//=====
-
-uint32_t Color::rgb() const {
-  return (255 << 24) + (red << 16) + (green << 8) + (blue << 0);
-}
-
-uint32_t Color::argb() const {
-  return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0);
-}
-
-//Geometry
-//========
-
-Position Geometry::position() const {
-  return {x, y};
-}
-
-Size Geometry::size() const {
-  return {width, height};
-}
-
-string Geometry::text() const {
-  return {x, ",", y, ",", width, ",", height};
-}
-
-Geometry::Geometry(const string& text) {
-  lstring part = text.split(",");
-  x = integer(part(0, "256"));
-  y = integer(part(1, "256"));
-  width = decimal(part(2, "256"));
-  height = decimal(part(3, "256"));
-}
-
-//Font
-//====
-
-string Font::serif(unsigned size, const string& style) {
-  return pFont::serif(size, style);
-}
-
-string Font::sans(unsigned size, const string& style) {
-  return pFont::sans(size, style);
-}
-
-string Font::monospace(unsigned size, const string& style) {
-  return pFont::monospace(size, style);
-}
-
-Size Font::size(const string& font, const string& text) {
-  return pFont::size(font, text);
-}
-
-//Desktop
-//=======
-
-Size Desktop::size() {
-  return pDesktop::size();
-}
-
-Geometry Desktop::workspace() {
-  return pDesktop::workspace();
-}
-
-//Monitor
-//=======
-
-unsigned Monitor::count() {
-  return pMonitor::count();
-}
-
-Geometry Monitor::geometry(unsigned monitor) {
-  return pMonitor::geometry(monitor);
-}
-
-unsigned Monitor::primary() {
-  return pMonitor::primary();
-}
-
-//Keyboard
-//========
-
-bool Keyboard::pressed(Keyboard::Scancode scancode) {
-  return pKeyboard::pressed(scancode);
-}
-
-bool Keyboard::released(Keyboard::Scancode scancode) {
-  return !pressed(scancode);
-}
-
-vector<bool> Keyboard::state() {
-  return pKeyboard::state();
-}
-
-//Mouse
-//=====
-
-Position Mouse::position() {
-  return pMouse::position();
-}
-
-bool Mouse::pressed(Mouse::Button button) {
-  return pMouse::pressed(button);
-}
-
-bool Mouse::released(Mouse::Button button) {
-  return !pressed(button);
-}
-
-//BrowserWindow
-//=============
-
-string BrowserWindow::directory() {
-  return pBrowserWindow::directory(state);
-}
-
-string BrowserWindow::open() {
-  return pBrowserWindow::open(state);
-}
-
-string BrowserWindow::save() {
-  return pBrowserWindow::save(state);
-}
-
-BrowserWindow& BrowserWindow::setFilters(const lstring& filters) {
-  state.filters = filters;
-  return *this;
-}
-
-BrowserWindow& BrowserWindow::setParent(Window& parent) {
-  state.parent = &parent;
-  return *this;
-}
-
-BrowserWindow& BrowserWindow::setPath(const string& path) {
-  state.path = path;
-  return *this;
-}
-
-BrowserWindow& BrowserWindow::setTitle(const string& title) {
-  state.title = title;
-  return *this;
-}
-
-BrowserWindow::BrowserWindow():
-state(*new State) {
-}
-
-BrowserWindow::~BrowserWindow() {
-  delete &state;
-}
-
-//MessageWindow
-//=============
-
-MessageWindow::Response MessageWindow::error(MessageWindow::Buttons buttons) {
-  state.buttons = buttons;
-  return pMessageWindow::error(state);
-}
-
-MessageWindow::Response MessageWindow::information(MessageWindow::Buttons buttons) {
-  state.buttons = buttons;
-  return pMessageWindow::information(state);
-}
-
-MessageWindow::Response MessageWindow::question(MessageWindow::Buttons buttons) {
-  state.buttons = buttons;
-  return pMessageWindow::question(state);
-}
-
-MessageWindow& MessageWindow::setParent(Window& parent) {
-  state.parent = &parent;
-  return *this;
-}
-
-MessageWindow& MessageWindow::setText(const string& text) {
-  state.text = text;
-  return *this;
-}
-
-MessageWindow& MessageWindow::setTitle(const string& title) {
-  state.title = title;
-  return *this;
-}
-
-MessageWindow::Response MessageWindow::warning(MessageWindow::Buttons buttons) {
-  state.buttons = buttons;
-  return pMessageWindow::warning(state);
-}
-
-MessageWindow::MessageWindow(const string& text):
-state(*new State) {
-  state.text = text;
-}
-
-MessageWindow::~MessageWindow() {
-  delete &state;
-}
-
-//Object
-//======
-
-Object::Object(pObject& p):
-p(p) {
-  Application::initialize();
-  p.constructor();
-}
-
-Object::~Object() {
-  p.destructor();
-  delete &p;
-}
-
-//Timer
-//=====
-
-bool Timer::enabled() const {
-  return state.enabled;
-}
-
-unsigned Timer::interval() const {
-  return state.interval;
-}
-
-void Timer::setEnabled(bool enabled) {
-  state.enabled = enabled;
-  return p.setEnabled(enabled);
-}
-
-void Timer::setInterval(unsigned interval) {
-  state.interval = interval;
-  return p.setInterval(interval);
-}
-
-Timer::Timer():
-state(*new State),
-base_from_member<pTimer&>(*new pTimer(*this)),
-Object(base_from_member<pTimer&>::value),
-p(base_from_member<pTimer&>::value) {
-  p.constructor();
-}
-
-Timer::~Timer() {
-  p.destructor();
-  delete &state;
-}
-
-//Window
-//======
-
-void Window::append(Layout& layout) {
-  if(state.layout.append(layout)) {
-    layout.Sizable::state.parent = nullptr;
-    layout.Sizable::state.window = this;
-    p.append(layout);
-    layout.synchronizeLayout();
-  }
-}
-
-void Window::append(Menu& menu) {
-  if(state.menu.append(menu)) {
-    menu.Action::state.window = this;
-    p.append(menu);
-  }
-}
-
-void Window::append(Widget& widget) {
-  if(state.widget.append(widget)) {
-    widget.Sizable::state.window = this;
-    p.append(widget);
-    widget.synchronizeLayout();
-  }
-}
-
-Color Window::backgroundColor() const {
-  return state.backgroundColor;
-}
-
-bool Window::droppable() const {
-  return state.droppable;
-}
-
-Geometry Window::frameGeometry() {
-  Geometry geometry = p.geometry();
-  Geometry margin = p.frameMargin();
-  return {
-    geometry.x - margin.x, geometry.y - margin.y,
-    geometry.width + margin.width, geometry.height + margin.height
-  };
-}
-
-Geometry Window::frameMargin() {
-  return p.frameMargin();
-}
-
-bool Window::focused() {
-  return p.focused();
-}
-
-bool Window::fullScreen() const {
-  return state.fullScreen;
-}
-
-Geometry Window::geometry() {
-  return p.geometry();
-}
-
-string Window::menuFont() const {
-  return state.menuFont;
-}
-
-bool Window::menuVisible() const {
-  return state.menuVisible;
-}
-
-bool Window::modal() const {
-  return state.modal;
-}
-
-void Window::remove(Layout& layout) {
-  if(state.layout.remove(layout)) {
-    p.remove(layout);
-    layout.Sizable::state.window = nullptr;
-  }
-}
-
-void Window::remove(Menu& menu) {
-  if(state.menu.remove(menu)) {
-    p.remove(menu);
-    menu.Action::state.window = nullptr;
-  }
-}
-
-void Window::remove(Widget& widget) {
-  if(state.widget.remove(widget)) {
-    p.remove(widget);
-    widget.Sizable::state.window = nullptr;
-  }
-}
-
-bool Window::resizable() const {
-  return state.resizable;
-}
-
-void Window::setBackgroundColor(Color color) {
-  state.backgroundColorOverride = true;
-  state.backgroundColor = color;
-  return p.setBackgroundColor(color);
-}
-
-void Window::setDroppable(bool droppable) {
-  state.droppable = droppable;
-  return p.setDroppable(droppable);
-}
-
-void Window::setFrameGeometry(Geometry geometry) {
-  Geometry margin = p.frameMargin();
-  return setGeometry({
-    geometry.x + margin.x, geometry.y + margin.y,
-    geometry.width - margin.width, geometry.height - margin.height
-  });
-}
-
-void Window::setFocused() {
-  return p.setFocused();
-}
-
-void Window::setFullScreen(bool fullScreen) {
-  state.fullScreen = fullScreen;
-  return p.setFullScreen(fullScreen);
-}
-
-void Window::setGeometry(Geometry geometry) {
-  state.geometry = geometry;
-  return p.setGeometry(geometry);
-}
-
-void Window::setMenuFont(const string& font) {
-  state.menuFont = font;
-  return p.setMenuFont(font);
-}
-
-void Window::setMenuVisible(bool visible) {
-  state.menuVisible = visible;
-  return p.setMenuVisible(visible);
-}
-
-void Window::setModal(bool modal) {
-  state.modal = modal;
-  return p.setModal(modal);
-}
-
-void Window::setResizable(bool resizable) {
-  state.resizable = resizable;
-  return p.setResizable(resizable);
-}
-
-void Window::setStatusFont(const string& font) {
-  state.statusFont = font;
-  return p.setStatusFont(font);
-}
-
-void Window::setStatusText(const string& text) {
-  state.statusText = text;
-  return p.setStatusText(text);
-}
-
-void Window::setStatusVisible(bool visible) {
-  state.statusVisible = visible;
-  return p.setStatusVisible(visible);
-}
-
-void Window::setTitle(const string& text) {
-  state.title = text;
-  return p.setTitle(text);
-}
-
-void Window::setVisible(bool visible) {
-  state.visible = visible;
-  synchronizeLayout();
-  return p.setVisible(visible);
-}
-
-void Window::setWidgetFont(const string& font) {
-  state.widgetFont = font;
-  return p.setWidgetFont(font);
-}
-
-void Window::setWindowGeometry(Geometry geometry) {
-  Geometry margin = p.frameMargin();
-  return setGeometry({
-    geometry.x + margin.x, geometry.y + margin.y,
-    geometry.width, geometry.height
-  });
-}
-
-string Window::statusFont() const {
-  return state.statusFont;
-}
-
-string Window::statusText() const {
-  return state.statusText;
-}
-
-bool Window::statusVisible() const {
-  return state.statusVisible;
-}
-
-void Window::synchronizeLayout() {
-  if(visible() && applicationState.quit == false) setGeometry(geometry());
-}
-
-string Window::title() const {
-  return state.title;
-}
-
-bool Window::visible() const {
-  return state.visible;
-}
-
-string Window::widgetFont() const {
-  return state.widgetFont;
-}
-
-Window::Window():
-state(*new State),
-base_from_member<pWindow&>(*new pWindow(*this)),
-Object(base_from_member<pWindow&>::value),
-p(base_from_member<pWindow&>::value) {
-  p.constructor();
-}
-
-Window::~Window() {
-  p.destructor();
-  delete &state;
-}
-
-//Action
-//======
-
-bool Action::enabled() const {
-  return state.enabled;
-}
-
-void Action::setEnabled(bool enabled) {
-  state.enabled = enabled;
-  return p.setEnabled(enabled);
-}
-
-void Action::setVisible(bool visible) {
-  state.visible = visible;
-  return p.setVisible(visible);
-}
-
-bool Action::visible() const {
-  return state.visible;
-}
-
-Action::Action(pAction& p):
-state(*new State),
-Object(p),
-p(p) {
-  p.constructor();
-}
-
-Action::~Action() {
-  p.destructor();
-  delete &state;
-}
-
-//Menu
-//====
-
-void Menu::append(const group<Action>& list) {
-  for(auto& action : list) {
-    if(state.action.append(action)) {
-      action.state.menu = this;
-      p.append(action);
-    }
-  }
-}
-
-image Menu::image() const {
-  return state.image;
-}
-
-void Menu::remove(const group<Action>& list) {
-  for(auto& action : list) {
-    if(state.action.remove(action)) {
-      action.state.menu = nullptr;
-      return p.remove(action);
-    }
-  }
-}
-
-void Menu::setImage(const nall::image& image) {
-  state.image = image;
-  return p.setImage(image);
-}
-
-void Menu::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string Menu::text() const {
-  return state.text;
-}
-
-Menu::Menu():
-state(*new State),
-base_from_member<pMenu&>(*new pMenu(*this)),
-Action(base_from_member<pMenu&>::value),
-p(base_from_member<pMenu&>::value) {
-  p.constructor();
-}
-
-Menu::~Menu() {
-  p.destructor();
-  delete &state;
-}
-
-//Separator
-//=========
-
-Separator::Separator():
-base_from_member<pSeparator&>(*new pSeparator(*this)),
-Action(base_from_member<pSeparator&>::value),
-p(base_from_member<pSeparator&>::value) {
-  p.constructor();
-}
-
-Separator::~Separator() {
-  p.destructor();
-}
-
-//Item
-//====
-
-image Item::image() const {
-  return state.image;
-}
-
-void Item::setImage(const nall::image& image) {
-  state.image = image;
-  return p.setImage(image);
-}
-
-void Item::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string Item::text() const {
-  return state.text;
-}
-
-Item::Item():
-state(*new State),
-base_from_member<pItem&>(*new pItem(*this)),
-Action(base_from_member<pItem&>::value),
-p(base_from_member<pItem&>::value) {
-  p.constructor();
-}
-
-Item::~Item() {
-  p.destructor();
-  delete &state;
-}
-
-//CheckItem
-//=========
-
-bool CheckItem::checked() const {
-  return state.checked;
-}
-
-void CheckItem::setChecked(bool checked) {
-  state.checked = checked;
-  return p.setChecked(checked);
-}
-
-void CheckItem::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string CheckItem::text() const {
-  return state.text;
-}
-
-CheckItem::CheckItem():
-state(*new State),
-base_from_member<pCheckItem&>(*new pCheckItem(*this)),
-Action(base_from_member<pCheckItem&>::value),
-p(base_from_member<pCheckItem&>::value) {
-  p.constructor();
-}
-
-CheckItem::~CheckItem() {
-  p.destructor();
-  delete &state;
-}
-
-//RadioItem
-//=========
-
-void RadioItem::group(const nall::group<RadioItem>& list) {
-  for(auto& item : list) item.p.setGroup(item.state.group = list);
-  if(list.size()) list.first().setChecked();
-}
-
-bool RadioItem::checked() const {
-  return state.checked;
-}
-
-void RadioItem::setChecked() {
-  for(auto& item : state.group) item.state.checked = false;
-  state.checked = true;
-  return p.setChecked();
-}
-
-void RadioItem::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string RadioItem::text() const {
-  return state.text;
-}
-
-RadioItem::RadioItem():
-state(*new State),
-base_from_member<pRadioItem&>(*new pRadioItem(*this)),
-Action(base_from_member<pRadioItem&>::value),
-p(base_from_member<pRadioItem&>::value) {
-  p.constructor();
-}
-
-RadioItem::~RadioItem() {
-  for(auto& item : state.group) {
-    if(&item != this) item.state.group.remove(*this);
-  }
-  p.destructor();
-  delete &state;
-}
-
-//Sizable
-//=======
-
-bool Sizable::enabled() const {
-  return state.enabled;
-}
-
-bool Sizable::enabledToAll() const {
-  if(state.enabled == false) return false;
-  if(state.parent) return state.parent->enabledToAll();
-  return true;
-}
-
-Layout* Sizable::layout() const {
-  if(state.parent && dynamic_cast<Layout*>(state.parent)) return (Layout*)state.parent;
-  return nullptr;
-}
-
-Sizable* Sizable::parent() const {
-  return state.parent;
-}
-
-bool Sizable::visible() const {
-  return state.visible;
-}
-
-bool Sizable::visibleToAll() const {
-  if(state.visible == false) return false;
-  if(state.parent) return state.parent->visibleToAll();
-  return true;
-}
-
-Window* Sizable::window() const {
-  return state.window;
-}
-
-Sizable::Sizable(pSizable& p):
-state(*new State),
-Object(p),
-p(p) {
-  p.constructor();
-}
-
-Sizable::~Sizable() {
-  if(layout()) layout()->remove(*this);
-  p.destructor();
-  delete &state;
-}
-
-//Layout
-//======
-
-void Layout::append(Sizable& sizable) {
-  sizable.state.parent = this;
-  sizable.state.window = Sizable::state.window;
-
-  if(dynamic_cast<Layout*>(&sizable)) {
-    Layout& layout = (Layout&)sizable;
-    layout.synchronizeLayout();
-  }
-
-  if(dynamic_cast<Widget*>(&sizable)) {
-    Widget& widget = (Widget&)sizable;
-    if(sizable.window()) sizable.window()->append(widget);
-  }
-}
-
-void Layout::remove(Sizable& sizable) {
-  if(dynamic_cast<Widget*>(&sizable)) {
-    Widget& widget = (Widget&)sizable;
-    if(sizable.window()) sizable.window()->remove(widget);
-  }
-
-  sizable.state.parent = nullptr;
-  sizable.state.window = nullptr;
-}
-
-void Layout::reset() {
-}
-
-Layout::Layout():
-state(*new State),
-base_from_member<pLayout&>(*new pLayout(*this)),
-Sizable(base_from_member<pLayout&>::value),
-p(base_from_member<pLayout&>::value) {
-}
-
-Layout::Layout(pLayout& p):
-state(*new State),
-base_from_member<pLayout&>(p),
-Sizable(p),
-p(p) {
-}
-
-Layout::~Layout() {
-  if(layout()) layout()->remove(*this);
-  if(window()) window()->remove(*this);
-  p.destructor();
-  delete &state;
-}
-
-//Widget
-//======
-
-bool Widget::focused() {
-  return p.focused();
-}
-
-string Widget::font() const {
-  return state.font;
-}
-
-Geometry Widget::geometry() const {
-  return state.geometry;
-}
-
-Size Widget::minimumSize() {
-  return p.minimumSize();
-}
-
-void Widget::setEnabled(bool enabled) {
-  Sizable::state.enabled = enabled;
-  return p.setEnabled(enabled);
-}
-
-void Widget::setFocused() {
-  return p.setFocused();
-}
-
-void Widget::setFont(const string& font) {
-  state.font = font;
-  return p.setFont(font);
-}
-
-void Widget::setGeometry(Geometry geometry) {
-  state.geometry = geometry;
-  return p.setGeometry(geometry);
-}
-
-void Widget::setVisible(bool visible) {
-  Sizable::state.visible = visible;
-  return p.setVisible(visible);
-}
-
-void Widget::synchronizeLayout() {
-}
-
-Widget::Widget():
-state(*new State),
-base_from_member<pWidget&>(*new pWidget(*this)),
-Sizable(base_from_member<pWidget&>::value),
-p(base_from_member<pWidget&>::value) {
-  state.abstract = true;
-  p.constructor();
-}
-
-Widget::Widget(pWidget& p):
-state(*new State),
-base_from_member<pWidget&>(p),
-Sizable(base_from_member<pWidget&>::value),
-p(base_from_member<pWidget&>::value) {
-  p.constructor();
-}
-
-Widget::~Widget() {
-  p.destructor();
-  delete &state;
-}
-
-//Button
-//======
-
-image Button::image() const {
-  return state.image;
-}
-
-Orientation Button::orientation() const {
-  return state.orientation;
-}
-
-void Button::setImage(const nall::image& image, Orientation orientation) {
-  state.image = image;
-  state.orientation = orientation;
-  return p.setImage(image, orientation);
-}
-
-void Button::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-nall::string Button::text() const {
-  return state.text;
-}
-
-Button::Button():
-state(*new State),
-base_from_member<pButton&>(*new pButton(*this)),
-Widget(base_from_member<pButton&>::value),
-p(base_from_member<pButton&>::value) {
-  p.constructor();
-}
-
-Button::~Button() {
-  p.destructor();
-  delete &state;
-}
-
-//Canvas
-//======
-
-Color Canvas::color() const {
-  return state.color;
-}
-
-uint32_t* Canvas::data() const {
-  return state.data;
-}
-
-bool Canvas::droppable() const {
-  return state.droppable;
-}
-
-vector<Color> Canvas::gradient() const {
-  return state.gradient;
-}
-
-image Canvas::image() const {
-  return state.image;
-}
-
-Canvas::Mode Canvas::mode() const {
-  return state.mode;
-}
-
-void Canvas::setColor(Color color) {
-  state.color = color;
-  return setMode(Canvas::Mode::Color);
-}
-
-void Canvas::setData() {
-  if(state.width == 0 || state.height == 0) return;  //dynamic sizing not supported in Mode::Data
-  return setMode(Canvas::Mode::Data);
-}
-
-void Canvas::setDroppable(bool droppable) {
-  state.droppable = droppable;
-  return p.setDroppable(droppable);
-}
-
-void Canvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) {
-  state.gradient[0] = topLeft;
-  state.gradient[1] = topRight;
-  state.gradient[2] = bottomLeft;
-  state.gradient[3] = bottomRight;
-  return setMode(Canvas::Mode::Gradient);
-}
-
-void Canvas::setHorizontalGradient(Color left, Color right) {
-  state.gradient[0] = state.gradient[2] = left;
-  state.gradient[1] = state.gradient[3] = right;
-  return setMode(Canvas::Mode::Gradient);
-}
-
-void Canvas::setImage(const nall::image& image) {
-  state.image = image;
-  return setMode(Canvas::Mode::Image);
-}
-
-void Canvas::setMode(Mode mode) {
-  state.mode = mode;
-  return p.setMode(mode);
-}
-
-void Canvas::setSize(Size size) {
-  if(size.width == Size::Maximum) size.width = 0;
-  if(size.height == Size::Maximum) size.height = 0;
-  state.width = size.width;
-  state.height = size.height;
-  delete[] state.data;
-  state.data = new uint32_t[state.width * state.height]();
-  return setMode(state.mode);
-}
-
-void Canvas::setVerticalGradient(Color top, Color bottom) {
-  state.gradient[0] = state.gradient[1] = top;
-  state.gradient[2] = state.gradient[3] = bottom;
-  return setMode(Canvas::Mode::Gradient);
-}
-
-Size Canvas::size() const {
-  return {state.width, state.height};
-}
-
-Canvas::Canvas():
-state(*new State),
-base_from_member<pCanvas&>(*new pCanvas(*this)),
-Widget(base_from_member<pCanvas&>::value),
-p(base_from_member<pCanvas&>::value) {
-  state.data = new uint32_t[state.width * state.height]();
-  p.constructor();
-}
-
-Canvas::~Canvas() {
-  p.destructor();
-  delete[] state.data;
-  delete &state;
-}
-
-//CheckButton
-//===========
-
-bool CheckButton::checked() const {
-  return state.checked;
-}
-
-image CheckButton::image() const {
-  return state.image;
-}
-
-void CheckButton::setChecked(bool checked) {
-  state.checked = checked;
-  return p.setChecked(checked);
-}
-
-void CheckButton::setImage(const nall::image& image, Orientation orientation) {
-  state.image = image;
-  state.orientation = orientation;
-  return p.setImage(image, orientation);
-}
-
-void CheckButton::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string CheckButton::text() const {
-  return state.text;
-}
-
-CheckButton::CheckButton():
-state(*new State),
-base_from_member<pCheckButton&>(*new pCheckButton(*this)),
-Widget(base_from_member<pCheckButton&>::value),
-p(base_from_member<pCheckButton&>::value) {
-  p.constructor();
-}
-
-CheckButton::~CheckButton() {
-  p.destructor();
-  delete &state;
-}
-
-//CheckLabel
-//==========
-
-bool CheckLabel::checked() const {
-  return state.checked;
-}
-
-void CheckLabel::setChecked(bool checked) {
-  state.checked = checked;
-  return p.setChecked(checked);
-}
-
-void CheckLabel::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string CheckLabel::text() const {
-  return state.text;
-}
-
-CheckLabel::CheckLabel():
-state(*new State),
-base_from_member<pCheckLabel&>(*new pCheckLabel(*this)),
-Widget(base_from_member<pCheckLabel&>::value),
-p(base_from_member<pCheckLabel&>::value) {
-  p.constructor();
-}
-
-CheckLabel::~CheckLabel() {
-  p.destructor();
-  delete &state;
-}
-
-
-//ComboButton
-//===========
-
-void ComboButton::append(const string& text) {
-  state.text.append(text);
-  return p.append(text);
-}
-
-void ComboButton::remove(unsigned selection) {
-  if(selection >= state.text.size()) return;
-  state.text.remove(selection);
-  p.remove(selection);
-}
-
-void ComboButton::reset() {
-  state.selection = 0;
-  state.text.reset();
-  return p.reset();
-}
-
-unsigned ComboButton::rows() const {
-  return state.text.size();
-}
-
-unsigned ComboButton::selection() const {
-  return state.selection;
-}
-
-void ComboButton::setSelection(unsigned selection) {
-  if(selection >= state.text.size()) return;
-  state.selection = selection;
-  return p.setSelection(selection);
-}
-
-void ComboButton::setText(unsigned selection, const string& text) {
-  if(selection >= state.text.size()) return;
-  state.text[selection] = text;
-  p.setText(selection, text);
-}
-
-string ComboButton::text() const {
-  if(state.text.empty()) return "";
-  return state.text[state.selection];
-}
-
-string ComboButton::text(unsigned selection) const {
-  if(selection >= state.text.size()) return "";
-  return state.text[selection];
-}
-
-ComboButton::ComboButton():
-state(*new State),
-base_from_member<pComboButton&>(*new pComboButton(*this)),
-Widget(base_from_member<pComboButton&>::value),
-p(base_from_member<pComboButton&>::value) {
-  p.constructor();
-}
-
-ComboButton::~ComboButton() {
-  p.destructor();
-  delete &state;
-}
-
-//Console
-//=======
-
-Color Console::backgroundColor() const {
-  return state.backgroundColor;
-}
-
-Color Console::foregroundColor() const {
-  return state.foregroundColor;
-}
-
-void Console::print(const string& text) {
-  return p.print(text);
-}
-
-string Console::prompt() const {
-  return state.prompt;
-}
-
-void Console::reset() {
-  return p.reset();
-}
-
-void Console::setBackgroundColor(Color color) {
-  state.backgroundColor = color;
-  return p.setBackgroundColor(color);
-}
-
-void Console::setForegroundColor(Color color) {
-  state.foregroundColor = color;
-  return p.setForegroundColor(color);
-}
-
-void Console::setPrompt(const string& prompt) {
-  state.prompt = prompt;
-  return p.setPrompt(prompt);
-}
-
-Console::Console():
-state(*new State),
-base_from_member<pConsole&>(*new pConsole(*this)),
-Widget(base_from_member<pConsole&>::value),
-p(base_from_member<pConsole&>::value) {
-  p.constructor();
-}
-
-Console::~Console() {
-  p.destructor();
-  delete &state;
-}
-
-//Frame
-//=====
-
-void Frame::setLayout(Layout& layout) {
-  state.layout = &layout;
-  synchronizeLayout();
-}
-
-void Frame::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-void Frame::synchronizeLayout() {
-  if(state.layout == nullptr) return;
-  state.layout->Sizable::state.window = Sizable::state.window;
-  state.layout->Sizable::state.parent = this;
-  state.layout->state.widget = this;
-  state.layout->synchronizeLayout();
-}
-
-string Frame::text() const {
-  return state.text;
-}
-
-Frame::Frame():
-state(*new State),
-base_from_member<pFrame&>(*new pFrame(*this)),
-Widget(base_from_member<pFrame&>::value),
-p(base_from_member<pFrame&>::value) {
-  p.constructor();
-}
-
-Frame::~Frame() {
-  p.destructor();
-  delete &state;
-}
-
-//HexEdit
-//=======
-
-Color HexEdit::backgroundColor() const {
-  return state.backgroundColor;
-}
-
-unsigned HexEdit::columns() const {
-  return state.columns;
-}
-
-Color HexEdit::foregroundColor() const {
-  return state.foregroundColor;
-}
-
-unsigned HexEdit::length() const {
-  return state.length;
-}
-
-unsigned HexEdit::offset() const {
-  return state.offset;
-}
-
-unsigned HexEdit::rows() const {
-  return state.rows;
-}
-
-void HexEdit::setBackgroundColor(Color color) {
-  state.backgroundColor = color;
-  return p.setBackgroundColor(color);
-}
-
-void HexEdit::setColumns(unsigned columns) {
-  state.columns = columns;
-  return p.setColumns(columns);
-}
-
-void HexEdit::setForegroundColor(Color color) {
-  state.foregroundColor = color;
-  return p.setForegroundColor(color);
-}
-
-void HexEdit::setLength(unsigned length) {
-  state.length = length;
-  return p.setLength(length);
-}
-
-void HexEdit::setOffset(unsigned offset) {
-  state.offset = offset;
-  return p.setOffset(offset);
-}
-
-void HexEdit::setRows(unsigned rows) {
-  state.rows = rows;
-  return p.setRows(rows);
-}
-
-void HexEdit::update() {
-  return p.update();
-}
-
-HexEdit::HexEdit():
-state(*new State),
-base_from_member<pHexEdit&>(*new pHexEdit(*this)),
-Widget(base_from_member<pHexEdit&>::value),
-p(base_from_member<pHexEdit&>::value) {
-  p.constructor();
-}
-
-HexEdit::~HexEdit() {
-  p.destructor();
-  delete &state;
-}
-
-//HorizontalScroller
-//==================
-
-unsigned HorizontalScroller::length() const {
-  return state.length;
-}
-
-unsigned HorizontalScroller::position() const {
-  return state.position;
-}
-
-void HorizontalScroller::setLength(unsigned length) {
-  state.length = length;
-  return p.setLength(length);
-}
-
-void HorizontalScroller::setPosition(unsigned position) {
-  state.position = position;
-  return p.setPosition(position);
-}
-
-HorizontalScroller::HorizontalScroller():
-state(*new State),
-base_from_member<pHorizontalScroller&>(*new pHorizontalScroller(*this)),
-Widget(base_from_member<pHorizontalScroller&>::value),
-p(base_from_member<pHorizontalScroller&>::value) {
-  p.constructor();
-}
-
-HorizontalScroller::~HorizontalScroller() {
-  p.destructor();
-  delete &state;
-}
-
-//HorizontalSlider
-//================
-
-unsigned HorizontalSlider::length() const {
-  return state.length;
-}
-
-unsigned HorizontalSlider::position() const {
-  return state.position;
-}
-
-void HorizontalSlider::setLength(unsigned length) {
-  state.length = length;
-  return p.setLength(length);
-}
-
-void HorizontalSlider::setPosition(unsigned position) {
-  state.position = position;
-  return p.setPosition(position);
-}
-
-HorizontalSlider::HorizontalSlider():
-state(*new State),
-base_from_member<pHorizontalSlider&>(*new pHorizontalSlider(*this)),
-Widget(base_from_member<pHorizontalSlider&>::value),
-p(base_from_member<pHorizontalSlider&>::value) {
-  p.constructor();
-}
-
-HorizontalSlider::~HorizontalSlider() {
-  p.destructor();
-  delete &state;
-}
-
-//Label
-//=====
-
-void Label::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string Label::text() const {
-  return state.text;
-}
-
-Label::Label():
-state(*new State),
-base_from_member<pLabel&>(*new pLabel(*this)),
-Widget(base_from_member<pLabel&>::value),
-p(base_from_member<pLabel&>::value) {
-  p.constructor();
-}
-
-Label::~Label() {
-  p.destructor();
-  delete &state;
-}
-
-//LineEdit
-//========
-
-Color LineEdit::backgroundColor() const {
-  return state.backgroundColor;
-}
-
-bool LineEdit::editable() const {
-  return state.editable;
-}
-
-Color LineEdit::foregroundColor() const {
-  return state.foregroundColor;
-}
-
-void LineEdit::setBackgroundColor(Color color) {
-  state.backgroundColor = color;
-  return p.setBackgroundColor(color);
-}
-
-void LineEdit::setEditable(bool editable) {
-  state.editable = editable;
-  return p.setEditable(editable);
-}
-
-void LineEdit::setForegroundColor(Color color) {
-  state.foregroundColor = color;
-  return p.setForegroundColor(color);
-}
-
-void LineEdit::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string LineEdit::text() {
-  return p.text();
-}
-
-LineEdit::LineEdit():
-state(*new State),
-base_from_member<pLineEdit&>(*new pLineEdit(*this)),
-Widget(base_from_member<pLineEdit&>::value),
-p(base_from_member<pLineEdit&>::value) {
-  p.constructor();
-}
-
-LineEdit::~LineEdit() {
-  p.destructor();
-  delete &state;
-}
-
-//ListView
-//========
-
-void ListView::append(const lstring& text) {
-  state.checked.append(false);
-  state.image.append({});
-  state.text.append(text);
-  return p.append(text);
-}
-
-void ListView::autoSizeColumns() {
-  return p.autoSizeColumns();
-}
-
-Color ListView::backgroundColor() const {
-  return state.backgroundColor;
-}
-
-bool ListView::checkable() const {
-  return state.checkable;
-}
-
-bool ListView::checked(unsigned selection) const {
-  if(selection >= state.text.size()) return false;
-  return state.checked[selection];
-}
-
-unsigned ListView::columns() const {
-  return max(1u, state.headerText.size());
-}
-
-Color ListView::foregroundColor() const {
-  return state.foregroundColor;
-}
-
-bool ListView::headerVisible() const {
-  return state.headerVisible;
-}
-
-image ListView::image(unsigned selection, unsigned position) const {
-  if(selection >= state.text.size()) return {};
-  return state.image[selection](position);
-}
-
-void ListView::remove(unsigned selection) {
-  if(selection >= state.text.size()) return;
-  state.checked.remove(selection);
-  state.image.remove(selection);
-  state.text.remove(selection);
-  return p.remove(selection);
-}
-
-void ListView::reset() {
-  state.checked.reset();
-  state.image.reset();
-  state.selected = false;
-  state.selection = 0;
-  state.text.reset();
-  return p.reset();
-}
-
-unsigned ListView::rows() const {
-  return state.text.size();
-}
-
-bool ListView::selected() const {
-  return state.selected;
-}
-
-unsigned ListView::selection() const {
-  return state.selection;
-}
-
-void ListView::setBackgroundColor(Color color) {
-  state.backgroundColor = color;
-  return p.setBackgroundColor(color);
-}
-
-void ListView::setCheckable(bool checkable) {
-  state.checkable = checkable;
-  return p.setCheckable(checkable);
-}
-
-void ListView::setChecked(unsigned selection, bool checked) {
-  if(selection >= state.text.size()) return;
-  state.checked[selection] = checked;
-  return p.setChecked(selection, checked);
-}
-
-void ListView::setForegroundColor(Color color) {
-  state.foregroundColor = color;
-  return p.setForegroundColor(color);
-}
-
-void ListView::setHeaderText(const lstring& text) {
-  state.headerText = text;
-  return p.setHeaderText(text);
-}
-
-void ListView::setHeaderVisible(bool visible) {
-  state.headerVisible = visible;
-  return p.setHeaderVisible(visible);
-}
-
-void ListView::setImage(unsigned selection, unsigned position, const nall::image& image) {
-  if(selection >= state.text.size()) return;
-  state.image[selection](position) = image;
-  return p.setImage(selection, position, image);
-}
-
-void ListView::setSelected(bool selected) {
-  state.selected = selected;
-  return p.setSelected(selected);
-}
-
-void ListView::setSelection(unsigned selection) {
-  if(selection >= state.text.size()) return;
-  state.selected = true;
-  state.selection = selection;
-  return p.setSelection(selection);
-}
-
-void ListView::setText(unsigned selection, const lstring& text) {
-  if(selection >= state.text.size()) return;
-  for(unsigned position = 0; position < text.size(); position++) {
-    setText(selection, position, text[position]);
-  }
-}
-
-void ListView::setText(unsigned selection, unsigned position, const string& text) {
-  if(selection >= state.text.size()) return;
-  state.text[selection](position) = text;
-  return p.setText(selection, position, text);
-}
-
-string ListView::text(unsigned selection, unsigned position) const {
-  if(selection >= state.text.size()) return "";
-  return state.text[selection](position);
-}
-
-ListView::ListView():
-state(*new State),
-base_from_member<pListView&>(*new pListView(*this)),
-Widget(base_from_member<pListView&>::value),
-p(base_from_member<pListView&>::value) {
-  p.constructor();
-}
-
-ListView::~ListView() {
-  p.destructor();
-  delete &state;
-}
-
-//ProgressBar
-//===========
-
-unsigned ProgressBar::position() const {
-  return state.position;
-}
-
-void ProgressBar::setPosition(unsigned position) {
-  state.position = position;
-  return p.setPosition(position);
-}
-
-ProgressBar::ProgressBar():
-state(*new State),
-base_from_member<pProgressBar&>(*new pProgressBar(*this)),
-Widget(base_from_member<pProgressBar&>::value),
-p(base_from_member<pProgressBar&>::value) {
-  p.constructor();
-}
-
-ProgressBar::~ProgressBar() {
-  p.destructor();
-  delete &state;
-}
-
-//RadioButton
-//===========
-
-void RadioButton::group(const nall::group<RadioButton>& list) {
-  for(auto& item : list) item.p.setGroup(item.state.group = list);
-  if(list.size()) list.first().setChecked();
-}
-
-bool RadioButton::checked() const {
-  return state.checked;
-}
-
-image RadioButton::image() const {
-  return state.image;
-}
-
-void RadioButton::setChecked() {
-  for(auto& item : state.group) item.state.checked = false;
-  state.checked = true;
-  return p.setChecked();
-}
-
-void RadioButton::setImage(const nall::image& image, Orientation orientation) {
-  state.image = image;
-  state.orientation = orientation;
-  return p.setImage(image, orientation);
-}
-
-void RadioButton::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string RadioButton::text() const {
-  return state.text;
-}
-
-RadioButton::RadioButton():
-state(*new State),
-base_from_member<pRadioButton&>(*new pRadioButton(*this)),
-Widget(base_from_member<pRadioButton&>::value),
-p(base_from_member<pRadioButton&>::value) {
-  p.constructor();
-}
-
-RadioButton::~RadioButton() {
-  for(auto& item : state.group) {
-    if(&item != this) item.state.group.remove(*this);
-  }
-  p.destructor();
-  delete &state;
-}
-
-//RadioLabel
-//==========
-
-void RadioLabel::group(const nall::group<RadioLabel>& list) {
-  for(auto& item : list) item.p.setGroup(item.state.group = list);
-  if(list.size()) list.first().setChecked();
-}
-
-bool RadioLabel::checked() const {
-  return state.checked;
-}
-
-void RadioLabel::setChecked() {
-  for(auto &item : state.group) item.state.checked = false;
-  state.checked = true;
-  return p.setChecked();
-}
-
-void RadioLabel::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-string RadioLabel::text() const {
-  return state.text;
-}
-
-RadioLabel::RadioLabel():
-state(*new State),
-base_from_member<pRadioLabel&>(*new pRadioLabel(*this)),
-Widget(base_from_member<pRadioLabel&>::value),
-p(base_from_member<pRadioLabel&>::value) {
-  p.constructor();
-}
-
-RadioLabel::~RadioLabel() {
-  for(auto& item : state.group) {
-    if(&item != this) item.state.group.remove(*this);
-  }
-  p.destructor();
-  delete &state;
-}
-
-//TabFrame
-//========
-
-void TabFrame::append(const string& text, const nall::image& image) {
-  state.image.append(image);
-  state.layout.append(nullptr);
-  state.text.append(text);
-  return p.append(text, image);
-}
-
-image TabFrame::image(unsigned selection) const {
-  if(selection >= state.text.size()) return {};
-  return state.image[selection];
-}
-
-void TabFrame::remove(unsigned selection) {
-  if(selection >= state.text.size()) return;
-  state.image.remove(selection);
-  state.layout.remove(selection);
-  state.text.remove(selection);
-  return p.remove(selection);
-}
-
-unsigned TabFrame::selection() const {
-  return state.selection;
-}
-
-void TabFrame::setImage(unsigned selection, const nall::image& image) {
-  if(selection >= state.text.size()) return;
-  state.image[selection] = image;
-  return p.setImage(selection, image);
-}
-
-void TabFrame::setLayout(unsigned selection, Layout& layout) {
-  if(selection >= state.text.size()) return;
-  state.layout[selection] = &layout;
-  synchronizeLayout();
-}
-
-void TabFrame::setSelection(unsigned selection) {
-  state.selection = selection;
-  return p.setSelection(selection);
-}
-
-void TabFrame::setText(unsigned selection, const string& text) {
-  if(selection >= state.text.size()) return;
-  state.text[selection] = text;
-  return p.setText(selection, text);
-}
-
-void TabFrame::synchronizeLayout() {
-  for(unsigned n = 0; n < state.layout.size(); n++) {
-    Layout* layout = state.layout[n];
-    if(layout == nullptr) continue;
-    layout->Sizable::state.parent = this;
-    layout->Sizable::state.window = Sizable::state.window;
-    layout->state.widget = this;
-    layout->state.widgetSelection = n;
-    layout->synchronizeLayout();
-  }
-}
-
-unsigned TabFrame::tabs() const {
-  return state.text.size();
-}
-
-string TabFrame::text(unsigned selection) const {
-  if(selection >= state.text.size()) return "";
-  return state.text[selection];
-}
-
-TabFrame::TabFrame():
-state(*new State),
-base_from_member<pTabFrame&>(*new pTabFrame(*this)),
-Widget(base_from_member<pTabFrame&>::value),
-p(base_from_member<pTabFrame&>::value) {
-  p.constructor();
-}
-
-TabFrame::~TabFrame() {
-  p.destructor();
-  delete &state;
-}
-
-//TextEdit
-//========
-
-Color TextEdit::backgroundColor() const {
-  return state.backgroundColor;
-}
-
-bool TextEdit::editable() const {
-  return state.editable;
-}
-
-Color TextEdit::foregroundColor() const {
-  return state.foregroundColor;
-}
-
-void TextEdit::setBackgroundColor(Color color) {
-  state.backgroundColor = color;
-  return p.setBackgroundColor(color);
-}
-
-void TextEdit::setCursorPosition(unsigned position) {
-  state.cursorPosition = position;
-  return p.setCursorPosition(position);
-}
-
-void TextEdit::setEditable(bool editable) {
-  state.editable = editable;
-  return p.setEditable(editable);
-}
-
-void TextEdit::setForegroundColor(Color color) {
-  state.foregroundColor = color;
-  return p.setForegroundColor(color);
-}
-
-void TextEdit::setText(const string& text) {
-  state.text = text;
-  return p.setText(text);
-}
-
-void TextEdit::setWordWrap(bool wordWrap) {
-  state.wordWrap = wordWrap;
-  return p.setWordWrap(wordWrap);
-}
-
-string TextEdit::text() {
-  return p.text();
-}
-
-bool TextEdit::wordWrap() const {
-  return state.wordWrap;
-}
-
-TextEdit::TextEdit():
-state(*new State),
-base_from_member<pTextEdit&>(*new pTextEdit(*this)),
-Widget(base_from_member<pTextEdit&>::value),
-p(base_from_member<pTextEdit&>::value) {
-  p.constructor();
-}
-
-TextEdit::~TextEdit() {
-  p.destructor();
-  delete &state;
-}
-
-//VerticalScroller
-//================
-
-unsigned VerticalScroller::length() const {
-  return state.length;
-}
-
-unsigned VerticalScroller::position() const {
-  return state.position;
-}
-
-void VerticalScroller::setLength(unsigned length) {
-  state.length = length;
-  return p.setLength(length);
-}
-
-void VerticalScroller::setPosition(unsigned position) {
-  state.position = position;
-  return p.setPosition(position);
-}
-
-VerticalScroller::VerticalScroller():
-state(*new State),
-base_from_member<pVerticalScroller&>(*new pVerticalScroller(*this)),
-Widget(base_from_member<pVerticalScroller&>::value),
-p(base_from_member<pVerticalScroller&>::value) {
-  p.constructor();
-}
-
-VerticalScroller::~VerticalScroller() {
-  p.destructor();
-  delete &state;
-}
-
-//VerticalSlider
-//==============
-
-unsigned VerticalSlider::length() const {
-  return state.length;
-}
-
-unsigned VerticalSlider::position() const {
-  return state.position;
-}
-
-void VerticalSlider::setLength(unsigned length) {
-  state.length = length;
-  return p.setLength(length);
-}
-
-void VerticalSlider::setPosition(unsigned position) {
-  state.position = position;
-  return p.setPosition(position);
-}
-
-VerticalSlider::VerticalSlider():
-state(*new State),
-base_from_member<pVerticalSlider&>(*new pVerticalSlider(*this)),
-Widget(base_from_member<pVerticalSlider&>::value),
-p(base_from_member<pVerticalSlider&>::value) {
-  p.constructor();
-}
-
-VerticalSlider::~VerticalSlider() {
-  p.destructor();
-  delete &state;
-}
-
-//Viewport
-//========
-
-bool Viewport::droppable() const {
-  return state.droppable;
-}
-
-uintptr_t Viewport::handle() {
-  return p.handle();
-}
-
-void Viewport::setDroppable(bool droppable) {
-  state.droppable = droppable;
-  return p.setDroppable(droppable);
-}
-
-Viewport::Viewport():
-state(*new State),
-base_from_member<pViewport&>(*new pViewport(*this)),
-Widget(base_from_member<pViewport&>::value),
-p(base_from_member<pViewport&>::value) {
-  p.constructor();
-}
-
-Viewport::~Viewport() {
-  p.destructor();
-  delete &state;
-}
-
-}
diff --git a/phoenix/core/core.hpp b/phoenix/core/core.hpp
deleted file mode 100644
index cc25c5ba..00000000
--- a/phoenix/core/core.hpp
+++ /dev/null
@@ -1,826 +0,0 @@
-#include <nall/platform.hpp>
-#include <nall/config.hpp>
-#include <nall/directory.hpp>
-#include <nall/function.hpp>
-#include <nall/group.hpp>
-#include <nall/image.hpp>
-#include <nall/map.hpp>
-#include <nall/stdint.hpp>
-#include <nall/string.hpp>
-#include <nall/utility.hpp>
-#include <nall/vector.hpp>
-
-namespace phoenix {
-
-struct Application;
-struct Font;
-struct Window;
-struct Menu;
-struct Sizable;
-struct Layout;
-struct Widget;
-
-struct pApplication;
-struct pFont;
-struct pObject;
-struct pTimer;
-struct pWindow;
-struct pAction;
-struct pMenu;
-struct pSeparator;
-struct pItem;
-struct pCheckItem;
-struct pRadioItem;
-struct pSizable;
-struct pLayout;
-struct pWidget;
-struct pButton;
-struct pCanvas;
-struct pCheckButton;
-struct pCheckLabel;
-struct pComboButton;
-struct pConsole;
-struct pFrame;
-struct pHexEdit;
-struct pHorizontalScroller;
-struct pHorizontalSlider;
-struct pLabel;
-struct pLineEdit;
-struct pListView;
-struct pProgressBar;
-struct pRadioButton;
-struct pRadioLabel;
-struct pTabFrame;
-struct pTextEdit;
-struct pVerticalScroller;
-struct pVerticalSlider;
-struct pViewport;
-
-struct Application {
-  static nall::function<void ()> main;
-
-  static void run();
-  static bool pendingEvents();
-  static void processEvents();
-  static void quit();
-  static void setName(const nall::string& name);
-
-  Application() = delete;
-  struct State;
-  static void initialize();
-
-  struct Windows {
-    static nall::function<void ()> onModalBegin;
-    static nall::function<void ()> onModalEnd;
-  };
-
-  struct Cocoa {
-    static nall::function<void ()> onAbout;
-    static nall::function<void ()> onActivate;
-    static nall::function<void ()> onPreferences;
-    static nall::function<void ()> onQuit;
-  };
-};
-
-struct Color {
-  uint8_t red, green, blue, alpha;
-  uint32_t rgb() const;
-  uint32_t argb() const;
-  inline Color() : alpha(255), red(0), green(0), blue(0) {}
-  inline Color(uint8_t red, uint8_t green, uint8_t blue) : alpha(255), red(red), green(green), blue(blue) {}
-  inline Color(uint8_t alpha, uint8_t red, uint8_t green, uint8_t blue) : alpha(alpha), red(red), green(green), blue(blue) {}
-};
-
-struct Position {
-  signed x, y;
-  inline Position() : x(0), y(0) {}
-  template<typename X, typename Y> inline Position(X x, Y y) : x(x), y(y) {}
-};
-
-struct Size {
-  static const unsigned Maximum = ~0u;
-  static const unsigned Minimum =  0u;
-
-  unsigned width, height;
-  inline Size() : width(0), height(0) {}
-  template<typename W, typename H> inline Size(W width, H height) : width(width), height(height) {}
-};
-
-struct Geometry {
-  signed x, y;
-  unsigned width, height;
-  Position position() const;
-  Size size() const;
-  nall::string text() const;
-  inline Geometry() : x(0), y(0), width(0), height(0) {}
-  inline Geometry(Position position, Size size) : x(position.x), y(position.y), width(size.width), height(size.height) {}
-  template<typename X, typename Y, typename W, typename H> inline Geometry(X x, Y y, W width, H height) : x(x), y(y), width(width), height(height) {}
-  Geometry(const nall::string& text);
-};
-
-enum class Orientation : unsigned { Horizontal, Vertical };
-
-struct Font {
-  static nall::string serif(unsigned size = 0, const nall::string& style = "");
-  static nall::string sans(unsigned size = 0, const nall::string& style = "");
-  static nall::string monospace(unsigned size = 0, const nall::string& style = "");
-  static Size size(const nall::string& font, const nall::string& text);
-  Font() = delete;
-};
-
-struct Desktop {
-  static Size size();
-  static Geometry workspace();
-  Desktop() = delete;
-};
-
-struct Monitor {
-  static unsigned count();
-  static Geometry geometry(unsigned monitor);
-  static unsigned primary();
-};
-
-struct Keyboard {
-  #include "keyboard.hpp"
-  static bool pressed(Scancode scancode);
-  static bool released(Scancode scancode);
-  static nall::vector<bool> state();
-  Keyboard() = delete;
-};
-
-struct Mouse {
-  enum class Button : unsigned { Left, Middle, Right };
-  static Position position();
-  static bool pressed(Button);
-  static bool released(Button);
-  Mouse() = delete;
-};
-
-struct BrowserWindow {
-  nall::string directory();
-  nall::string open();
-  nall::string save();
-  BrowserWindow& setFilters(const nall::lstring& filters);
-  BrowserWindow& setParent(Window& parent);
-  BrowserWindow& setPath(const nall::string& path);
-  BrowserWindow& setTitle(const nall::string& title);
-
-  BrowserWindow();
-  ~BrowserWindow();
-  struct State;
-  State& state;
-};
-
-struct MessageWindow {
-  enum class Buttons : unsigned {
-    Ok,
-    OkCancel,
-    YesNo,
-    YesNoCancel,
-  };
-
-  enum class Response : unsigned {
-    Ok,
-    Cancel,
-    Yes,
-    No,
-  };
-
-  Response error(Buttons = Buttons::Ok);
-  Response information(Buttons = Buttons::Ok);
-  Response question(Buttons = Buttons::YesNo);
-  MessageWindow& setParent(Window& parent);
-  MessageWindow& setText(const nall::string& text);
-  MessageWindow& setTitle(const nall::string& title);
-  Response warning(Buttons = Buttons::Ok);
-
-  MessageWindow(const nall::string& text = "");
-  ~MessageWindow();
-  struct State;
-  State& state;
-};
-
-struct Object {
-  Object(pObject& p);
-  Object& operator=(const Object&) = delete;
-  Object(const Object&) = delete;
-  virtual ~Object();
-  pObject& p;
-};
-
-struct Timer : private nall::base_from_member<pTimer&>, Object {
-  nall::function<void ()> onActivate;
-
-  bool enabled() const;
-  unsigned interval() const;
-  void setEnabled(bool enabled = true);
-  void setInterval(unsigned interval);  //in milliseconds
-
-  Timer();
-  ~Timer();
-  struct State;
-  State& state;
-  pTimer& p;
-};
-
-struct Window : private nall::base_from_member<pWindow&>, Object {
-  nall::function<void ()> onClose;
-  nall::function<void (nall::lstring)> onDrop;
-  nall::function<void (Keyboard::Keycode)> onKeyPress;
-  nall::function<void (Keyboard::Keycode)> onKeyRelease;
-  nall::function<void ()> onMove;
-  nall::function<void ()> onSize;
-
-  void append(Layout& layout);
-  void append(Menu& menu);
-  void append(Widget& widget);
-  Color backgroundColor() const;
-  bool droppable() const;
-  Geometry frameGeometry();
-  Geometry frameMargin();
-  bool focused();
-  bool fullScreen() const;
-  Geometry geometry();
-  nall::string menuFont() const;
-  bool menuVisible() const;
-  bool modal() const;
-  void remove(Layout& layout);
-  void remove(Menu& menu);
-  void remove(Widget& widget);
-  bool resizable() const;
-  void setBackgroundColor(Color color);
-  void setDroppable(bool droppable = true);
-  void setFrameGeometry(Geometry geometry);
-  void setFocused();
-  void setFullScreen(bool fullScreen = true);
-  void setGeometry(Geometry geometry);
-  void setMenuFont(const nall::string& font);
-  void setMenuVisible(bool visible = true);
-  void setModal(bool modal = true);
-  void setResizable(bool resizable = true);
-  void setStatusFont(const nall::string& font);
-  void setStatusText(const nall::string& text);
-  void setStatusVisible(bool visible = true);
-  void setTitle(const nall::string& text);
-  void setVisible(bool visible = true);
-  void setWidgetFont(const nall::string& font);
-  void setWindowGeometry(Geometry geometry);
-  nall::string statusFont() const;
-  nall::string statusText() const;
-  bool statusVisible() const;
-  void synchronizeLayout();
-  nall::string title() const;
-  bool visible() const;
-  nall::string widgetFont() const;
-
-  Window();
-  ~Window();
-  struct State;
-  State& state;
-  pWindow& p;
-};
-
-struct Action : Object {
-  bool enabled() const;
-  void setEnabled(bool enabled = true);
-  void setVisible(bool visible = true);
-  bool visible() const;
-
-  Action(pAction& p);
-  ~Action();
-  struct State;
-  State& state;
-  pAction& p;
-};
-
-struct Menu : private nall::base_from_member<pMenu&>, Action {
-  void append(const nall::group<Action>& list);
-  nall::image image() const;
-  void remove(const nall::group<Action>& list);
-  void setImage(const nall::image& image = nall::image{});
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  Menu();
-  ~Menu();
-  struct State;
-  State& state;
-  pMenu& p;
-};
-
-struct Separator : private nall::base_from_member<pSeparator&>, Action {
-  Separator();
-  ~Separator();
-  pSeparator& p;
-};
-
-struct Item : private nall::base_from_member<pItem&>, Action {
-  nall::function<void ()> onActivate;
-
-  nall::image image() const;
-  void setImage(const nall::image& image = nall::image{});
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  Item();
-  ~Item();
-  struct State;
-  State& state;
-  pItem& p;
-};
-
-struct CheckItem : private nall::base_from_member<pCheckItem&>, Action {
-  nall::function<void ()> onToggle;
-
-  bool checked() const;
-  void setChecked(bool checked = true);
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  CheckItem();
-  ~CheckItem();
-  struct State;
-  State& state;
-  pCheckItem& p;
-};
-
-struct RadioItem : private nall::base_from_member<pRadioItem&>, Action {
-  template<typename... Args> static void group(Args&&... args) { group({std::forward<Args>(args)...}); }
-  static void group(const nall::group<RadioItem>& list);
-
-  nall::function<void ()> onActivate;
-
-  bool checked() const;
-  void setChecked();
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  RadioItem();
-  ~RadioItem();
-  struct State;
-  State& state;
-  pRadioItem& p;
-};
-
-struct Sizable : Object {
-  bool enabled() const;
-  bool enabledToAll() const;
-  Layout* layout() const;
-  virtual Size minimumSize() = 0;
-  Sizable* parent() const;
-  virtual void setEnabled(bool enabled = true) = 0;
-  virtual void setGeometry(Geometry geometry) = 0;
-  virtual void setVisible(bool visible = true) = 0;
-  virtual void synchronizeLayout() = 0;
-  bool visible() const;
-  bool visibleToAll() const;
-  Window* window() const;
-
-  Sizable(pSizable& p);
-  ~Sizable();
-  struct State;
-  State& state;
-  pSizable& p;
-};
-
-struct Layout : private nall::base_from_member<pLayout&>, Sizable {
-  virtual void append(Sizable& sizable);
-  virtual void remove(Sizable& sizable);
-  virtual void reset();
-
-  Layout();
-  Layout(pLayout& p);
-  ~Layout();
-  struct State;
-  State& state;
-  pLayout& p;
-};
-
-struct Widget : private nall::base_from_member<pWidget&>, Sizable {
-  nall::function<void ()> onSize;
-
-  bool focused();
-  nall::string font() const;
-  Geometry geometry() const;
-  Size minimumSize();
-  void setEnabled(bool enabled = true);
-  void setFocused();
-  void setFont(const nall::string& font);
-  void setGeometry(Geometry geometry);
-  void setVisible(bool visible = true);
-  void synchronizeLayout();
-
-  Widget();
-  Widget(pWidget& p);
-  ~Widget();
-  struct State;
-  State& state;
-  pWidget& p;
-};
-
-struct Button : private nall::base_from_member<pButton&>, Widget {
-  nall::function<void ()> onActivate;
-
-  nall::image image() const;
-  Orientation orientation() const;
-  void setImage(const nall::image& image = nall::image{}, Orientation orientation = Orientation::Horizontal);
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  Button();
-  ~Button();
-  struct State;
-  State& state;
-  pButton& p;
-};
-
-struct Canvas : private nall::base_from_member<pCanvas&>, Widget {
-  enum class Mode : unsigned { Color, Gradient, Image, Data };
-
-  nall::function<void (nall::lstring)> onDrop;
-  nall::function<void ()> onMouseLeave;
-  nall::function<void (Position)> onMouseMove;
-  nall::function<void (Mouse::Button)> onMousePress;
-  nall::function<void (Mouse::Button)> onMouseRelease;
-
-  Color color() const;
-  uint32_t* data() const;
-  bool droppable() const;
-  nall::vector<Color> gradient() const;
-  nall::image image() const;
-  Mode mode() const;
-  void setColor(Color color);
-  void setData();
-  void setDroppable(bool droppable = true);
-  void setHorizontalGradient(Color left, Color right);
-  void setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight);
-  void setImage(const nall::image& image);
-  void setMode(Mode mode);
-  void setSize(Size size);
-  void setVerticalGradient(Color top, Color bottom);
-  Size size() const;
-
-  Canvas();
-  ~Canvas();
-  struct State;
-  State& state;
-  pCanvas& p;
-};
-
-struct CheckButton : private nall::base_from_member<pCheckButton&>, Widget {
-  nall::function<void ()> onToggle;
-
-  bool checked() const;
-  nall::image image() const;
-  void setChecked(bool checked = true);
-  void setImage(const nall::image& image = nall::image{}, Orientation orientation = Orientation::Horizontal);
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  CheckButton();
-  ~CheckButton();
-  struct State;
-  State& state;
-  pCheckButton& p;
-};
-
-struct CheckLabel : private nall::base_from_member<pCheckLabel&>, Widget {
-  nall::function<void ()> onToggle;
-
-  bool checked() const;
-  void setChecked(bool checked = true);
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  CheckLabel();
-  ~CheckLabel();
-  struct State;
-  State& state;
-  pCheckLabel& p;
-};
-
-struct ComboButton : private nall::base_from_member<pComboButton&>, Widget {
-  nall::function<void ()> onChange;
-
-  void append(const nall::string& text = "");
-  void remove(unsigned selection);
-  void reset();
-  unsigned rows() const;
-  unsigned selection() const;
-  void setSelection(unsigned selection);
-  void setText(unsigned selection, const nall::string& text);
-  nall::string text() const;
-  nall::string text(unsigned selection) const;
-
-  ComboButton();
-  ~ComboButton();
-  struct State;
-  State& state;
-  pComboButton& p;
-};
-
-struct Console : private nall::base_from_member<pConsole&>, Widget {
-  nall::function<void (nall::string)> onActivate;
-
-  template<typename... Args> void print(Args&&... args) { print({args...}); }
-
-  Color backgroundColor() const;
-  Color foregroundColor() const;
-  void print(const nall::string& text);
-  nall::string prompt() const;
-  void reset();
-  void setBackgroundColor(Color color);
-  void setForegroundColor(Color color);
-  void setPrompt(const nall::string& prompt);
-
-  Console();
-  ~Console();
-  struct State;
-  State& state;
-  pConsole& p;
-};
-
-struct Frame : private nall::base_from_member<pFrame&>, Widget {
-  void setLayout(Layout& layout);
-  void setText(const nall::string& text);
-  void synchronizeLayout();
-  nall::string text() const;
-
-  Frame();
-  ~Frame();
-  struct State;
-  State& state;
-  pFrame& p;
-};
-
-struct HexEdit : private nall::base_from_member<pHexEdit&>, Widget {
-  nall::function<uint8_t (unsigned)> onRead;
-  nall::function<void (unsigned, uint8_t)> onWrite;
-
-  Color backgroundColor() const;
-  unsigned columns() const;
-  Color foregroundColor() const;
-  unsigned length() const;
-  unsigned offset() const;
-  unsigned rows() const;
-  void setBackgroundColor(Color color);
-  void setColumns(unsigned columns);
-  void setForegroundColor(Color color);
-  void setLength(unsigned length);
-  void setOffset(unsigned offset);
-  void setRows(unsigned rows);
-  void update();
-
-  HexEdit();
-  ~HexEdit();
-  struct State;
-  State& state;
-  pHexEdit& p;
-};
-
-struct HorizontalScroller : private nall::base_from_member<pHorizontalScroller&>, Widget {
-  nall::function<void ()> onChange;
-
-  unsigned length() const;
-  unsigned position() const;
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  HorizontalScroller();
-  ~HorizontalScroller();
-  struct State;
-  State& state;
-  pHorizontalScroller& p;
-};
-
-struct HorizontalSlider : private nall::base_from_member<pHorizontalSlider&>, Widget {
-  nall::function<void ()> onChange;
-
-  unsigned length() const;
-  unsigned position() const;
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  HorizontalSlider();
-  ~HorizontalSlider();
-  struct State;
-  State& state;
-  pHorizontalSlider& p;
-};
-
-struct Label : private nall::base_from_member<pLabel&>, Widget {
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  Label();
-  ~Label();
-  struct State;
-  State& state;
-  pLabel& p;
-};
-
-struct LineEdit : private nall::base_from_member<pLineEdit&>, Widget {
-  nall::function<void ()> onActivate;
-  nall::function<void ()> onChange;
-
-  Color backgroundColor() const;
-  bool editable() const;
-  Color foregroundColor() const;
-  void setBackgroundColor(Color color);
-  void setEditable(bool editable = true);
-  void setForegroundColor(Color color);
-  void setText(const nall::string& text);
-  nall::string text();
-
-  LineEdit();
-  ~LineEdit();
-  struct State;
-  State& state;
-  pLineEdit& p;
-};
-
-struct ListView : private nall::base_from_member<pListView&>, Widget {
-  nall::function<void ()> onActivate;
-  nall::function<void ()> onChange;
-  nall::function<void (unsigned)> onToggle;
-
-  void append(const nall::lstring& text);
-  void autoSizeColumns();
-  Color backgroundColor() const;
-  bool checkable() const;
-  bool checked(unsigned selection) const;
-  unsigned columns() const;
-  Color foregroundColor() const;
-  bool headerVisible() const;
-  nall::image image(unsigned selection, unsigned position) const;
-  void remove(unsigned selection);
-  void reset();
-  unsigned rows() const;
-  bool selected() const;
-  unsigned selection() const;
-  void setBackgroundColor(Color color);
-  void setCheckable(bool checkable = true);
-  void setChecked(unsigned selection, bool checked = true);
-  void setForegroundColor(Color color);
-  void setHeaderText(const nall::lstring& text);
-  void setHeaderVisible(bool visible = true);
-  void setImage(unsigned selection, unsigned position, const nall::image& image = nall::image{});
-  void setSelected(bool selected = true);
-  void setSelection(unsigned selection);
-  void setText(unsigned selection, const nall::lstring& text);
-  void setText(unsigned selection, unsigned position, const nall::string& text);
-  nall::string text(unsigned selection, unsigned position) const;
-
-  ListView();
-  ~ListView();
-  struct State;
-  State& state;
-  pListView& p;
-};
-
-struct ProgressBar : private nall::base_from_member<pProgressBar&>, Widget {
-  unsigned position() const;
-  void setPosition(unsigned position);
-
-  ProgressBar();
-  ~ProgressBar();
-  struct State;
-  State& state;
-  pProgressBar& p;
-};
-
-struct RadioButton : private nall::base_from_member<pRadioButton&>, Widget {
-  template<typename... Args> static void group(Args&&... args) { group({std::forward<Args>(args)...}); }
-  static void group(const nall::group<RadioButton>& list);
-
-  nall::function<void ()> onActivate;
-
-  bool checked() const;
-  nall::image image() const;
-  void setChecked();
-  void setImage(const nall::image& image = nall::image{}, Orientation orientation = Orientation::Horizontal);
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  RadioButton();
-  ~RadioButton();
-  struct State;
-  State& state;
-  pRadioButton& p;
-};
-
-struct RadioLabel : private nall::base_from_member<pRadioLabel&>, Widget {
-  template<typename... Args> static void group(Args&&... args) { group({std::forward<Args>(args)...}); }
-  static void group(const nall::group<RadioLabel>& list);
-
-  nall::function<void ()> onActivate;
-
-  bool checked() const;
-  void setChecked();
-  void setText(const nall::string& text);
-  nall::string text() const;
-
-  RadioLabel();
-  ~RadioLabel();
-  struct State;
-  State& state;
-  pRadioLabel& p;
-};
-
-struct TabFrame : private nall::base_from_member<pTabFrame&>, Widget {
-  nall::function<void ()> onChange;
-
-  void append(const nall::string& text = "", const nall::image& image = nall::image{});
-  nall::image image(unsigned selection) const;
-  void remove(unsigned selection);
-  unsigned selection() const;
-  void setImage(unsigned selection, const nall::image& image = nall::image{});
-  void setLayout(unsigned selection, Layout& layout);
-  void setSelection(unsigned selection);
-  void setText(unsigned selection, const nall::string& text);
-  void synchronizeLayout();
-  unsigned tabs() const;
-  nall::string text(unsigned selection) const;
-
-  TabFrame();
-  ~TabFrame();
-  struct State;
-  State& state;
-  pTabFrame& p;
-};
-
-struct TextEdit : private nall::base_from_member<pTextEdit&>, Widget {
-  nall::function<void ()> onChange;
-
-  Color backgroundColor() const;
-  bool editable() const;
-  Color foregroundColor() const;
-  void setBackgroundColor(Color color);
-  void setCursorPosition(unsigned position);
-  void setEditable(bool editable = true);
-  void setForegroundColor(Color color);
-  void setText(const nall::string& text);
-  void setWordWrap(bool wordWrap = true);
-  nall::string text();
-  bool wordWrap() const;
-
-  TextEdit();
-  ~TextEdit();
-  struct State;
-  State& state;
-  pTextEdit& p;
-};
-
-struct VerticalScroller : private nall::base_from_member<pVerticalScroller&>, Widget {
-  nall::function<void ()> onChange;
-
-  unsigned length() const;
-  unsigned position() const;
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  VerticalScroller();
-  ~VerticalScroller();
-  struct State;
-  State& state;
-  pVerticalScroller& p;
-};
-
-struct VerticalSlider : private nall::base_from_member<pVerticalSlider&>, Widget {
-  nall::function<void ()> onChange;
-
-  unsigned length() const;
-  unsigned position() const;
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  VerticalSlider();
-  ~VerticalSlider();
-  struct State;
-  State& state;
-  pVerticalSlider& p;
-};
-
-struct Viewport : private nall::base_from_member<pViewport&>, Widget {
-  nall::function<void (nall::lstring)> onDrop;
-  nall::function<void ()> onMouseLeave;
-  nall::function<void (Position)> onMouseMove;
-  nall::function<void (Mouse::Button)> onMousePress;
-  nall::function<void (Mouse::Button)> onMouseRelease;
-
-  bool droppable() const;
-  uintptr_t handle();
-  void setDroppable(bool droppable = true);
-
-  Viewport();
-  ~Viewport();
-  struct State;
-  State& state;
-  pViewport& p;
-};
-
-#include "layout/fixed-layout.hpp"
-#include "layout/horizontal-layout.hpp"
-#include "layout/vertical-layout.hpp"
-
-}
diff --git a/phoenix/core/keyboard.hpp b/phoenix/core/keyboard.hpp
deleted file mode 100644
index ed04ec05..00000000
--- a/phoenix/core/keyboard.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//each code refers to a physical key
-//names are taken assuming: NumLock on, CapsLock off, Shift off
-//layout uses US-104 keyboard
-enum class Scancode : unsigned {
-  None,
-
-  Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
-  PrintScreen, ScrollLock, Pause,
-  Insert, Delete, Home, End, PageUp, PageDown,
-  Up, Down, Left, Right,
-
-  Grave, Number1, Number2, Number3, Number4, Number5, Number6, Number7, Number8, Number9, Number0, Minus, Equal, Backspace,
-  BracketLeft, BracketRight, Backslash, Semicolon, Apostrophe, Comma, Period, Slash,
-  Tab, CapsLock, Return, ShiftLeft, ShiftRight, ControlLeft, ControlRight, SuperLeft, SuperRight, AltLeft, AltRight, Space, Menu,
-  A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
-
-  NumLock, Divide, Multiply, Subtract, Add, Enter, Point,
-  Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0,
-
-  Limit,
-};
-
-//each enum refers to a translated scancode (eg Shift+1 = !)
-enum class Keycode : unsigned {
-  None,
-
-  Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
-  PrintScreen, SysRq, ScrollLock, Pause, Break,
-  Insert, Delete, Home, End, PageUp, PageDown,
-  Up, Down, Left, Right,
-
-  Grave, Number1, Number2, Number3, Number4, Number5, Number6, Number7, Number8, Number9, Number0, Minus, Equal, Backspace,
-  Tilde, Exclamation, At, Pound, Dollar, Percent, Power, Ampersand, Asterisk, ParenthesisLeft, ParenthesisRight, Underscore, Plus,
-  BracketLeft, BracketRight, Backslash, Semicolon, Apostrophe, Comma, Period, Slash,
-  BraceLeft, BraceRight, Pipe, Colon, Quote, CaretLeft, CaretRight, Question,
-  Tab, CapsLock, Return, ShiftLeft, ShiftRight, ControlLeft, ControlRight, SuperLeft, SuperRight, AltLeft, AltRight, Space, Menu,
-  A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
-  a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
-
-  NumLock, Divide, Multiply, Subtract, Add, Enter, Point,
-  Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0,
-  KeypadInsert, KeypadDelete, KeypadHome, KeypadEnd, KeypadPageUp, KeypadPageDown, KeypadUp, KeypadDown, KeypadLeft, KeypadRight, KeypadCenter,
-
-  Limit,
-};
diff --git a/phoenix/core/layout/fixed-layout.cpp b/phoenix/core/layout/fixed-layout.cpp
deleted file mode 100644
index d29189e5..00000000
--- a/phoenix/core/layout/fixed-layout.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-void FixedLayout::append(Sizable& sizable, Geometry geometry) {
-  children.append({&sizable, geometry});
-  synchronizeLayout();
-  if(window()) window()->synchronizeLayout();
-}
-
-void FixedLayout::append(Sizable& sizable) {
-  for(auto& child : children) if(child.sizable == &sizable) return;
-  Layout::append(sizable);
-  if(window()) window()->synchronizeLayout();
-}
-
-Size FixedLayout::minimumSize() {
-  unsigned width = Size::Minimum, height = Size::Minimum;
-  for(auto& child : children) {
-    width  = max(width,  child.sizable->minimumSize().width);
-    height = max(height, child.sizable->minimumSize().height);
-  }
-  return {width, height};
-}
-
-void FixedLayout::remove(Sizable& sizable) {
-  for(unsigned n = 0; n < children.size(); n++) {
-    if(children[n].sizable == &sizable) {
-      children.remove(n);
-      Layout::remove(sizable);
-      if(window()) window()->synchronizeLayout();
-      break;
-    }
-  }
-}
-
-void FixedLayout::reset() {
-  for(auto& child : children) {
-    if(window() && dynamic_cast<Widget*>(child.sizable)) window()->remove((Widget&)*child.sizable);
-  }
-}
-
-void FixedLayout::setEnabled(bool enabled) {
-  Sizable::state.enabled = enabled;
-  for(auto& child : children) {
-    child.sizable->setEnabled(child.sizable->enabled());
-  }
-}
-
-void FixedLayout::setGeometry(Geometry geometry) {
-}
-
-void FixedLayout::setVisible(bool visible) {
-  Sizable::state.visible = visible;
-  for(auto& child : children) {
-    child.sizable->setVisible(child.sizable->visible());
-  }
-}
-
-void FixedLayout::synchronizeLayout() {
-  for(auto& child : children) {
-    Layout::append(*child.sizable);
-    Geometry childGeometry = child.geometry;
-    if((signed)childGeometry.width < 1) childGeometry.width = 1;
-    if((signed)childGeometry.height < 1) childGeometry.height = 1;
-    child.sizable->setGeometry(childGeometry);
-  }
-}
-
-FixedLayout::~FixedLayout() {
-  while(children.size()) remove(*children[0].sizable);
-}
diff --git a/phoenix/core/layout/fixed-layout.hpp b/phoenix/core/layout/fixed-layout.hpp
deleted file mode 100644
index 881bee76..00000000
--- a/phoenix/core/layout/fixed-layout.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-struct FixedLayout : Layout {
-  void append(Sizable& sizable, Geometry geometry);
-  void append(Sizable& sizable);
-  Size minimumSize();
-  void remove(Sizable& sizable);
-  void reset();
-  void setEnabled(bool enabled = true);
-  void setGeometry(Geometry geometry);
-  void setVisible(bool visible = true);
-  void synchronizeLayout();
-  ~FixedLayout();
-
-//private:
-  struct Children {
-    Sizable* sizable;
-    Geometry geometry;
-  };
-  nall::vector<Children> children;
-};
diff --git a/phoenix/core/layout/horizontal-layout.cpp b/phoenix/core/layout/horizontal-layout.cpp
deleted file mode 100644
index 574d48e3..00000000
--- a/phoenix/core/layout/horizontal-layout.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-void HorizontalLayout::append(Sizable& sizable, Size size, unsigned spacing) {
-  for(auto& child : children) if(child.sizable == &sizable) return;
-  children.append({&sizable, size.width, size.height, spacing});
-  synchronizeLayout();
-  if(window()) window()->synchronizeLayout();
-}
-
-void HorizontalLayout::append(Sizable& sizable) {
-  for(auto& child : children) if(child.sizable == &sizable) return;
-  Layout::append(sizable);
-  if(window()) window()->synchronizeLayout();
-}
-
-Size HorizontalLayout::minimumSize() {
-  unsigned width = 0, height = 0;
-
-  for(auto& child : children) {
-    width += child.spacing;
-    if(child.width == Size::Minimum || child.width == Size::Maximum) {
-      width += child.sizable->minimumSize().width;
-      continue;
-    }
-    width += child.width;
-  }
-
-  for(auto& child : children) {
-    if(child.height == Size::Minimum || child.height == Size::Maximum) {
-      height = max(height, child.sizable->minimumSize().height);
-      continue;
-    }
-    height = max(height, child.height);
-  }
-
-  return {state.margin * 2 + width, state.margin * 2 + height};
-}
-
-void HorizontalLayout::remove(Sizable& sizable) {
-  for(unsigned n = 0; n < children.size(); n++) {
-    if(children[n].sizable == &sizable) {
-      if(dynamic_cast<Layout*>(children[n].sizable)) {
-        Layout* layout = (Layout*)children[n].sizable;
-        layout->reset();
-      }
-      children.remove(n);
-      Layout::remove(sizable);
-      if(window()) window()->synchronizeLayout();
-      break;
-    }
-  }
-}
-
-void HorizontalLayout::reset() {
-  for(auto& child : children) {
-    if(window() && dynamic_cast<Layout*>(child.sizable)) ((Layout*)child.sizable)->reset();
-    if(window() && dynamic_cast<Widget*>(child.sizable)) window()->remove((Widget&)*child.sizable);
-  }
-}
-
-void HorizontalLayout::setAlignment(double alignment) {
-  state.alignment = max(0.0, min(1.0, alignment));
-}
-
-void HorizontalLayout::setEnabled(bool enabled) {
-  Sizable::state.enabled = enabled;
-  for(auto& child : children) {
-    child.sizable->setEnabled(child.sizable->enabled());
-  }
-}
-
-void HorizontalLayout::setGeometry(Geometry containerGeometry) {
-  auto children = this->children;
-  for(auto& child : children) {
-    if(child.width  == Size::Minimum) child.width  = child.sizable->minimumSize().width;
-    if(child.height == Size::Minimum) child.height = child.sizable->minimumSize().height;
-  }
-
-  Geometry geometry = containerGeometry;
-  geometry.x      += state.margin;
-  geometry.y      += state.margin;
-  geometry.width  -= state.margin * 2;
-  geometry.height -= state.margin * 2;
-
-  unsigned minimumWidth = 0, maximumWidthCounter = 0;
-  for(auto& child : children) {
-    if(child.width == Size::Maximum) maximumWidthCounter++;
-    if(child.width != Size::Maximum) minimumWidth += child.width;
-    minimumWidth += child.spacing;
-  }
-
-  for(auto& child : children) {
-    if(child.width  == Size::Maximum) child.width  = (geometry.width - minimumWidth) / maximumWidthCounter;
-    if(child.height == Size::Maximum) child.height = geometry.height;
-  }
-
-  unsigned maximumHeight = 0;
-  for(auto& child : children) maximumHeight = max(maximumHeight, child.height);
-
-  for(auto& child : children) {
-    unsigned pivot = (maximumHeight - child.height) * state.alignment;
-    Geometry childGeometry = {geometry.x, geometry.y + pivot, child.width, child.height};
-    if((signed)childGeometry.width < 1) childGeometry.width = 1;
-    if((signed)childGeometry.height < 1) childGeometry.height = 1;
-    child.sizable->setGeometry(childGeometry);
-
-    geometry.x += child.width + child.spacing;
-    geometry.width -= child.width + child.spacing;
-  }
-}
-
-void HorizontalLayout::setMargin(unsigned margin) {
-  state.margin = margin;
-}
-
-void HorizontalLayout::setVisible(bool visible) {
-  Sizable::state.visible = visible;
-  for(auto& child : children) {
-    child.sizable->setVisible(child.sizable->visible());
-  }
-}
-
-void HorizontalLayout::synchronizeLayout() {
-  for(auto& child : children) Layout::append(*child.sizable);
-}
-
-HorizontalLayout::~HorizontalLayout() {
-  while(children.size()) remove(*children[0].sizable);
-}
diff --git a/phoenix/core/layout/horizontal-layout.hpp b/phoenix/core/layout/horizontal-layout.hpp
deleted file mode 100644
index c12eda8b..00000000
--- a/phoenix/core/layout/horizontal-layout.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-struct HorizontalLayout : public Layout {
-  void append(Sizable& sizable, Size size, unsigned spacing = 0);
-  void append(Sizable& sizable);
-  Size minimumSize();
-  void remove(Sizable& sizable);
-  void reset();
-  void setAlignment(double alignment);
-  void setEnabled(bool enabled = true);
-  void setGeometry(Geometry geometry);
-  void setMargin(unsigned margin);
-  void setVisible(bool visible = true);
-  void synchronizeLayout();
-  ~HorizontalLayout();
-
-//private:
-  struct State {
-    double alignment = 0.5;
-    unsigned margin = 0;
-  } state;
-
-  struct Children {
-    Sizable* sizable;
-    unsigned width, height, spacing;
-  };
-  nall::vector<Children> children;
-};
diff --git a/phoenix/core/layout/vertical-layout.cpp b/phoenix/core/layout/vertical-layout.cpp
deleted file mode 100644
index 804a26db..00000000
--- a/phoenix/core/layout/vertical-layout.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-void VerticalLayout::append(Sizable& sizable, Size size, unsigned spacing) {
-  for(auto& child : children) if(child.sizable == &sizable) return;
-  children.append({&sizable, size.width, size.height, spacing});
-  synchronizeLayout();
-  if(window()) window()->synchronizeLayout();
-}
-
-void VerticalLayout::append(Sizable& sizable) {
-  for(auto& child : children) if(child.sizable == &sizable) return;
-  Layout::append(sizable);
-  if(window()) window()->synchronizeLayout();
-}
-
-Size VerticalLayout::minimumSize() {
-  unsigned width = 0, height = 0;
-
-  for(auto& child : children) {
-    if(child.width == Size::Minimum || child.width == Size::Maximum) {
-      width = max(width, child.sizable->minimumSize().width);
-      continue;
-    }
-    width = max(width, child.width);
-  }
-
-  for(auto& child : children) {
-    height += child.spacing;
-    if(child.height == Size::Minimum || child.height == Size::Maximum) {
-      height += child.sizable->minimumSize().height;
-      continue;
-    }
-    height += child.height;
-  }
-
-  return {state.margin * 2 + width, state.margin * 2 + height};
-}
-
-void VerticalLayout::remove(Sizable& sizable) {
-  for(unsigned n = 0; n < children.size(); n++) {
-    if(children[n].sizable == &sizable) {
-      if(dynamic_cast<Layout*>(children[n].sizable)) {
-        Layout* layout = (Layout*)children[n].sizable;
-        layout->reset();
-      }
-      children.remove(n);
-      Layout::remove(sizable);
-      if(window()) window()->synchronizeLayout();
-      break;
-    }
-  }
-}
-
-void VerticalLayout::reset() {
-  for(auto& child : children) {
-    if(window() && dynamic_cast<Layout*>(child.sizable)) ((Layout*)child.sizable)->reset();
-    if(window() && dynamic_cast<Widget*>(child.sizable)) window()->remove((Widget&)*child.sizable);
-  }
-}
-
-void VerticalLayout::setAlignment(double alignment) {
-  state.alignment = max(0.0, min(1.0, alignment));
-}
-
-void VerticalLayout::setEnabled(bool enabled) {
-  Sizable::state.enabled = enabled;
-  for(auto& child : children) {
-    child.sizable->setEnabled(child.sizable->enabled());
-  }
-}
-
-void VerticalLayout::setGeometry(Geometry containerGeometry) {
-  auto children = this->children;
-  for(auto& child : children) {
-    if(child.width  == Size::Minimum) child.width  = child.sizable->minimumSize().width;
-    if(child.height == Size::Minimum) child.height = child.sizable->minimumSize().height;
-  }
-
-  Geometry geometry = containerGeometry;
-  geometry.x      += state.margin;
-  geometry.y      += state.margin;
-  geometry.width  -= state.margin * 2;
-  geometry.height -= state.margin * 2;
-
-  unsigned minimumHeight = 0, maximumHeightCounter = 0;
-  for(auto& child : children) {
-    if(child.height == Size::Maximum) maximumHeightCounter++;
-    if(child.height != Size::Maximum) minimumHeight += child.height;
-    minimumHeight += child.spacing;
-  }
-
-  for(auto& child : children) {
-    if(child.width  == Size::Maximum) child.width  = geometry.width;
-    if(child.height == Size::Maximum) child.height = (geometry.height - minimumHeight) / maximumHeightCounter;
-  }
-
-  unsigned maximumWidth = 0;
-  for(auto& child : children) maximumWidth = max(maximumWidth, child.width);
-
-  for(auto& child : children) {
-    unsigned pivot = (maximumWidth - child.width) * state.alignment;
-    Geometry childGeometry = {geometry.x + pivot, geometry.y, child.width, child.height};
-    if((signed)childGeometry.width < 1) childGeometry.width = 1;
-    if((signed)childGeometry.height < 1) childGeometry.height = 1;
-    child.sizable->setGeometry(childGeometry);
-
-    geometry.y += child.height + child.spacing;
-    geometry.height -= child.height + child.spacing;
-  }
-}
-
-void VerticalLayout::setMargin(unsigned margin) {
-  state.margin = margin;
-}
-
-void VerticalLayout::setVisible(bool visible) {
-  Sizable::state.visible = visible;
-  for(auto& child : children) {
-    child.sizable->setVisible(child.sizable->visible());
-  }
-}
-
-void VerticalLayout::synchronizeLayout() {
-  for(auto& child : children) Layout::append(*child.sizable);
-}
-
-VerticalLayout::~VerticalLayout() {
-  while(children.size()) remove(*children[0].sizable);
-}
diff --git a/phoenix/core/layout/vertical-layout.hpp b/phoenix/core/layout/vertical-layout.hpp
deleted file mode 100644
index db96d6e7..00000000
--- a/phoenix/core/layout/vertical-layout.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-struct VerticalLayout : public Layout {
-  void append(Sizable& sizable, Size size, unsigned spacing = 0);
-  void append(Sizable& sizable);
-  Size minimumSize();
-  void remove(Sizable& sizable);
-  void reset();
-  void setAlignment(double alignment);
-  void setEnabled(bool enabled = true);
-  void setGeometry(Geometry geometry);
-  void setMargin(unsigned margin);
-  void setVisible(bool visible = true);
-  void synchronizeLayout();
-  ~VerticalLayout();
-
-//private:
-  struct State {
-    double alignment = 0.0;
-    unsigned margin = 0;
-  } state;
-
-  struct Children {
-    Sizable* sizable;
-    unsigned width, height, spacing;
-  };
-  nall::vector<Children> children;
-};
diff --git a/phoenix/core/state.hpp b/phoenix/core/state.hpp
deleted file mode 100644
index 3a48de87..00000000
--- a/phoenix/core/state.hpp
+++ /dev/null
@@ -1,227 +0,0 @@
-struct Application::State {
-  string name;
-  bool quit = false;
-} applicationState;
-
-struct Timer::State {
-  bool enabled = false;
-  unsigned interval = 0;
-};
-
-struct BrowserWindow::State {
-  lstring filters;
-  Window* parent = nullptr;
-  string path;
-  string title;
-};
-
-struct MessageWindow::State {
-  MessageWindow::Buttons buttons = MessageWindow::Buttons::Ok;
-  Window* parent = nullptr;
-  string text;
-  string title;
-};
-
-struct Window::State {
-  bool backgroundColorOverride = false;
-  Color backgroundColor = {0, 0, 0};
-  bool droppable = false;
-  bool fullScreen = false;
-  Geometry geometry = {128, 128, 256, 256};
-  group<Layout> layout;
-  group<Menu> menu;
-  string menuFont;
-  bool menuVisible = false;
-  bool modal = false;
-  bool resizable = true;
-  string statusFont;
-  string statusText;
-  bool statusVisible = false;
-  string title;
-  bool visible = false;
-  group<Widget> widget;
-  string widgetFont;
-};
-
-struct Action::State {
-  bool enabled = true;
-  Menu* menu = nullptr;
-  bool visible = true;
-  Window* window = nullptr;
-};
-
-struct Menu::State {
-  group<Action> action;
-  nall::image image;
-  string text;
-};
-
-struct Item::State {
-  nall::image image;
-  string text;
-};
-
-struct CheckItem::State {
-  bool checked = false;
-  string text;
-};
-
-struct RadioItem::State {
-  bool checked = true;
-  nall::group<RadioItem> group;
-  string text;
-};
-
-struct Sizable::State {
-  bool enabled = true;
-  Sizable* parent = nullptr;
-  bool visible = true;
-  Window* window = nullptr;
-};
-
-struct Layout::State {
-  Widget* widget = nullptr;
-  unsigned widgetSelection = 0;
-};
-
-struct Widget::State {
-  bool abstract = false;
-  string font;
-  Geometry geometry = {0, 0, 0, 0};
-};
-
-struct Button::State {
-  nall::image image;
-  Orientation orientation = Orientation::Horizontal;
-  string text;
-};
-
-struct Canvas::State {
-  Color color;
-  uint32_t* data = nullptr;
-  bool droppable = false;
-  vector<Color> gradient = {{}, {}, {}, {}};
-  nall::image image;
-  Canvas::Mode mode = Canvas::Mode::Color;
-  unsigned width = 0;
-  unsigned height = 0;
-};
-
-struct CheckButton::State {
-  bool checked = false;
-  nall::image image;
-  Orientation orientation = Orientation::Horizontal;
-  string text;
-};
-
-struct CheckLabel::State {
-  bool checked = false;
-  string text;
-};
-
-struct ComboButton::State {
-  unsigned selection = 0;
-  vector<string> text;
-};
-
-struct Console::State {
-  Color backgroundColor = {255, 255, 255};
-  Color foregroundColor = {0, 0, 0};
-  string prompt;
-};
-
-struct Frame::State {
-  Layout* layout = nullptr;
-  string text;
-};
-
-struct HexEdit::State {
-  Color backgroundColor = {255, 255, 255};
-  unsigned columns = 16;
-  Color foregroundColor = {0, 0, 0};
-  unsigned length = 0;
-  unsigned offset = 0;
-  unsigned rows = 16;
-};
-
-struct HorizontalScroller::State {
-  unsigned length = 101;
-  unsigned position = 0;
-};
-
-struct HorizontalSlider::State {
-  unsigned length = 101;
-  unsigned position = 0;
-};
-
-struct Label::State {
-  string text;
-};
-
-struct LineEdit::State {
-  Color backgroundColor = {255, 255, 255};
-  bool editable = true;
-  Color foregroundColor = {0, 0, 0};
-  string text;
-};
-
-struct ListView::State {
-  Color backgroundColor = {255, 255, 255};
-  bool checkable = false;
-  vector<bool> checked;
-  Color foregroundColor = {0, 0, 0};
-  lstring headerText;
-  bool headerVisible = false;
-  vector<vector<nall::image>> image;
-  bool selected = false;
-  unsigned selection = 0;
-  vector<lstring> text;
-};
-
-struct ProgressBar::State {
-  unsigned position = 0;
-};
-
-struct RadioButton::State {
-  bool checked = true;
-  nall::group<RadioButton> group;
-  nall::image image;
-  Orientation orientation = Orientation::Horizontal;
-  string text;
-};
-
-struct RadioLabel::State {
-  bool checked = true;
-  nall::group<RadioLabel> group;
-  string text;
-};
-
-struct TabFrame::State {
-  vector<nall::image> image;
-  vector<Layout*> layout;
-  unsigned selection = 0;
-  lstring text;
-};
-
-struct TextEdit::State {
-  Color backgroundColor = {255, 255, 255};
-  unsigned cursorPosition = 0;
-  bool editable = true;
-  Color foregroundColor = {0, 0, 0};
-  string text;
-  bool wordWrap = true;
-};
-
-struct VerticalScroller::State {
-  unsigned length = 101;
-  unsigned position = 0;
-};
-
-struct VerticalSlider::State {
-  unsigned length = 101;
-  unsigned position = 0;
-};
-
-struct Viewport::State {
-  bool droppable = false;
-};
diff --git a/phoenix/gtk/action/check-item.cpp b/phoenix/gtk/action/check-item.cpp
deleted file mode 100644
index 59d6299b..00000000
--- a/phoenix/gtk/action/check-item.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-namespace phoenix {
-
-static void CheckItem_toggle(GtkCheckMenuItem* gtkCheckMenuItem, CheckItem* self) {
-  self->state.checked = gtk_check_menu_item_get_active(gtkCheckMenuItem);
-  if(!self->p.locked && self->onToggle) self->onToggle();
-}
-
-void pCheckItem::setChecked(bool checked) {
-  locked = true;
-  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), checked);
-  locked = false;
-}
-
-void pCheckItem::setText(string text) {
-  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), mnemonic(text));
-}
-
-void pCheckItem::constructor() {
-  widget = gtk_check_menu_item_new_with_mnemonic("");
-  setChecked(checkItem.state.checked);
-  setText(checkItem.state.text);
-  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(CheckItem_toggle), (gpointer)&checkItem);
-}
-
-void pCheckItem::destructor() {
-  gtk_widget_destroy(widget);
-}
-
-void pCheckItem::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/action/item.cpp b/phoenix/gtk/action/item.cpp
deleted file mode 100644
index 29b053e4..00000000
--- a/phoenix/gtk/action/item.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-namespace phoenix {
-
-static void Item_activate(Item* self) {
-  if(self->onActivate) self->onActivate();
-}
-
-void pItem::setImage(const image& image) {
-  if(image.empty() == false) {
-    GtkImage* gtkImage = CreateImage(image, true);
-    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), (GtkWidget*)gtkImage);
-  } else {
-    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr);
-  }
-}
-
-void pItem::setText(string text) {
-  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), mnemonic(text));
-}
-
-void pItem::constructor() {
-  widget = gtk_image_menu_item_new_with_mnemonic("");
-  g_signal_connect_swapped(G_OBJECT(widget), "activate", G_CALLBACK(Item_activate), (gpointer)&item);
-  setText(item.state.text);
-}
-
-void pItem::destructor() {
-  gtk_widget_destroy(widget);
-}
-
-void pItem::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/action/menu.cpp b/phoenix/gtk/action/menu.cpp
deleted file mode 100644
index c0e4b375..00000000
--- a/phoenix/gtk/action/menu.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-namespace phoenix {
-
-void pMenu::append(Action& action) {
-  action.state.window = this->action.state.window;
-
-  gtk_menu_shell_append(GTK_MENU_SHELL(gtkMenu), action.p.widget);
-  if(action.state.window && action.state.window->state.menuFont != "") {
-    action.p.setFont(action.state.window->state.menuFont);
-  }
-  gtk_widget_show(action.p.widget);
-}
-
-void pMenu::remove(Action& action) {
-  action.p.orphan();
-  action.state.window = nullptr;
-}
-
-void pMenu::setImage(const image& image) {
-  if(image.empty() == false) {
-    GtkImage* gtkImage = CreateImage(image, true);
-    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), (GtkWidget*)gtkImage);
-  } else {
-    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr);
-  }
-}
-
-void pMenu::setText(string text) {
-  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), mnemonic(text));
-}
-
-void pMenu::constructor() {
-  gtkMenu = gtk_menu_new();
-  widget = gtk_image_menu_item_new_with_mnemonic("");
-  gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), gtkMenu);
-  setText(menu.state.text);
-}
-
-void pMenu::destructor() {
-  gtk_widget_destroy(gtkMenu);
-  gtk_widget_destroy(widget);
-}
-
-void pMenu::orphan() {
-  for(auto& action : menu.state.action) action.p.orphan();
-  destructor();
-  constructor();
-  for(auto& action : menu.state.action) append(action);
-}
-
-void pMenu::setFont(string font) {
-  pAction::setFont(font);
-  for(auto& item : menu.state.action) item.p.setFont(font);
-}
-
-}
diff --git a/phoenix/gtk/action/radio-item.cpp b/phoenix/gtk/action/radio-item.cpp
deleted file mode 100644
index f00f9a76..00000000
--- a/phoenix/gtk/action/radio-item.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-namespace phoenix {
-
-static void RadioItem_activate(GtkCheckMenuItem* gtkCheckMenuItem, RadioItem* self) {
-  self->p.onActivate();
-}
-
-void pRadioItem::setChecked() {
-  parent().locked = true;
-  for(auto& item : radioItem.state.group) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), false);
-  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true);
-  parent().locked = false;
-}
-
-void pRadioItem::setGroup(const group<RadioItem>& group) {
-  parent().locked = true;
-  for(auto& item : group) {
-    if(&item == &group.first()) continue;
-    GSList* currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(group.first().p.widget));
-    if(currentGroup != gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item.p.widget))) {
-      gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item.p.widget), currentGroup);
-    }
-  }
-  parent().locked = false;
-}
-
-void pRadioItem::setText(string text) {
-  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), mnemonic(text));
-}
-
-void pRadioItem::constructor() {
-  widget = gtk_radio_menu_item_new_with_mnemonic(0, "");
-  setGroup(radioItem.state.group);
-  setText(radioItem.state.text);
-  for(auto& item : radioItem.state.group) {
-    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), item.state.checked);
-  }
-  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(RadioItem_activate), (gpointer)&radioItem);
-}
-
-void pRadioItem::destructor() {
-  gtk_widget_destroy(widget);
-}
-
-void pRadioItem::orphan() {
-  destructor();
-  constructor();
-}
-
-void pRadioItem::onActivate() {
-  if(parent().locked) return;
-  bool wasChecked = radioItem.state.checked;
-  radioItem.setChecked();
-  if(wasChecked) return;
-  if(radioItem.onActivate) radioItem.onActivate();
-}
-
-pRadioItem& pRadioItem::parent() {
-  if(radioItem.state.group.size()) return radioItem.state.group.first().p;
-  return *this;
-}
-
-}
diff --git a/phoenix/gtk/action/separator.cpp b/phoenix/gtk/action/separator.cpp
deleted file mode 100644
index 0db95b2c..00000000
--- a/phoenix/gtk/action/separator.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace phoenix {
-
-void pSeparator::constructor() {
-  widget = gtk_separator_menu_item_new();
-}
-
-void pSeparator::destructor() {
-  gtk_widget_destroy(widget);
-}
-
-void pSeparator::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/application.cpp b/phoenix/gtk/application.cpp
deleted file mode 100644
index 35091107..00000000
--- a/phoenix/gtk/application.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-namespace phoenix {
-
-XlibDisplay* pApplication::display = nullptr;
-
-void pApplication::run() {
-  if(Application::main) {
-    while(applicationState.quit == false) {
-      processEvents();
-      Application::main();
-    }
-  } else {
-    gtk_main();
-  }
-}
-
-bool pApplication::pendingEvents() {
-  return gtk_events_pending();
-}
-
-void pApplication::processEvents() {
-  while(pendingEvents()) gtk_main_iteration_do(false);
-}
-
-void pApplication::quit() {
-  //if gtk_main() was invoked, call gtk_main_quit()
-  if(gtk_main_level()) gtk_main_quit();
-}
-
-void pApplication::initialize() {
-  display = XOpenDisplay(nullptr);
-
-  settings = new Settings;
-  settings->load();
-
-  int argc = 1;
-  char* argv[] = {new char[8], nullptr};
-  strcpy(argv[0], "phoenix");
-  char** argvp = argv;
-  gtk_init(&argc, &argvp);
-
-  GtkSettings* gtkSettings = gtk_settings_get_default();
-//g_object_set(gtkSettings, "gtk-button-images", true, nullptr);
-
-  gtk_rc_parse_string(R"(
-    style "phoenix-gtk"
-    {
-      GtkWindow::resize-grip-width = 0
-      GtkWindow::resize-grip-height = 0
-      GtkTreeView::vertical-separator = 0
-      GtkComboBox::appears-as-list = 1
-    }
-    class "GtkWindow" style "phoenix-gtk"
-    class "GtkTreeView" style "phoenix-gtk"
-  # class "GtkComboBox" style "phoenix-gtk"
-  )");
-
-  pKeyboard::initialize();
-}
-
-}
diff --git a/phoenix/gtk/platform.hpp b/phoenix/gtk/platform.hpp
deleted file mode 100644
index ead42f24..00000000
--- a/phoenix/gtk/platform.hpp
+++ /dev/null
@@ -1,640 +0,0 @@
-namespace phoenix {
-
-struct pApplication {
-  static XlibDisplay* display;
-
-  static void run();
-  static bool pendingEvents();
-  static void processEvents();
-  static void quit();
-
-  static void initialize();
-};
-
-struct Settings : Configuration::Document {
-  bimap<Keyboard::Scancode, unsigned> keymap;
-
-  struct Geometry : Configuration::Node {
-    unsigned frameX;
-    unsigned frameY;
-    unsigned frameWidth;
-    unsigned frameHeight;
-    unsigned menuHeight;
-    unsigned statusHeight;
-  } geometry;
-
-  struct Window : Configuration::Node {
-    unsigned backgroundColor;
-  } window;
-
-  void load();
-  void save();
-  Settings();
-};
-
-struct pWindow;
-struct pMenu;
-struct pLayout;
-struct pWidget;
-
-struct pFont {
-  static string serif(unsigned size, string style);
-  static string sans(unsigned size, string style);
-  static string monospace(unsigned size, string style);
-  static Size size(string font, string text);
-
-  static PangoFontDescription* create(string description);
-  static void free(PangoFontDescription* font);
-  static Size size(PangoFontDescription* font, string text);
-  static void setFont(GtkWidget* widget, string font);
-  static void setFont(GtkWidget* widget, gpointer font);
-};
-
-struct pDesktop {
-  static Size size();
-  static Geometry workspace();
-};
-
-struct pMonitor {
-  static unsigned count();
-  static Geometry geometry(unsigned monitor);
-  static unsigned primary();
-};
-
-struct pKeyboard {
-  static bool pressed(Keyboard::Scancode scancode);
-  static vector<bool> state();
-
-  static void initialize();
-};
-
-struct pMouse {
-  static Position position();
-  static bool pressed(Mouse::Button button);
-};
-
-struct pBrowserWindow {
-  static string directory(BrowserWindow::State& state);
-  static string open(BrowserWindow::State& state);
-  static string save(BrowserWindow::State& state);
-};
-
-struct pMessageWindow {
-  static MessageWindow::Response error(MessageWindow::State& state);
-  static MessageWindow::Response information(MessageWindow::State& state);
-  static MessageWindow::Response question(MessageWindow::State& state);
-  static MessageWindow::Response warning(MessageWindow::State& state);
-};
-
-struct pObject {
-  Object& object;
-  bool locked;
-
-  pObject(Object& object) : object(object), locked(false) {}
-  virtual ~pObject() {}
-
-  void constructor() {}
-  void destructor() {}
-};
-
-struct pTimer : public pObject {
-  Timer& timer;
-
-  void setEnabled(bool enabled);
-  void setInterval(unsigned interval);
-
-  pTimer(Timer& timer) : pObject(timer), timer(timer) {}
-  void constructor();
-};
-
-struct pWindow : public pObject {
-  Window& window;
-  GtkWidget* widget;
-  GtkWidget* menuContainer;
-  GtkWidget* formContainer;
-  GtkWidget* statusContainer;
-  GtkWidget* menu;
-  GtkWidget* status;
-  GtkAllocation lastAllocation;
-  bool onSizePending;
-
-  static Window& none();
-
-  void append(Layout& layout);
-  void append(Menu& menu);
-  void append(Widget& widget);
-  bool focused();
-  Geometry frameMargin();
-  Geometry geometry();
-  void remove(Layout& layout);
-  void remove(Menu& menu);
-  void remove(Widget& widget);
-  void setBackgroundColor(Color color);
-  void setDroppable(bool droppable);
-  void setFocused();
-  void setFullScreen(bool fullScreen);
-  void setGeometry(Geometry geometry);
-  void setMenuFont(string font);
-  void setMenuVisible(bool visible);
-  void setModal(bool modal);
-  void setResizable(bool resizable);
-  void setStatusFont(string font);
-  void setStatusText(string text);
-  void setStatusVisible(bool visible);
-  void setTitle(string text);
-  void setVisible(bool visible);
-  void setWidgetFont(string font);
-
-  pWindow(Window& window) : pObject(window), window(window) {}
-  void constructor();
-  unsigned menuHeight();
-  unsigned statusHeight();
-};
-
-struct pAction : public pObject {
-  Action& action;
-  GtkWidget* widget;
-
-  void setEnabled(bool enabled);
-  void setVisible(bool visible);
-
-  pAction(Action& action) : pObject(action), action(action) {}
-  void constructor();
-  virtual void orphan();
-  string mnemonic(string text);
-  virtual void setFont(string font);
-};
-
-struct pMenu : public pAction {
-  Menu& menu;
-  GtkWidget* gtkMenu;
-
-  void append(Action& action);
-  void remove(Action& action);
-  void setImage(const image& image);
-  void setText(string text);
-
-  pMenu(Menu &menu) : pAction(menu), menu(menu) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  void setFont(string font);
-};
-
-struct pSeparator : public pAction {
-  Separator& separator;
-
-  pSeparator(Separator& separator) : pAction(separator), separator(separator) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pItem : public pAction {
-  Item& item;
-
-  void setImage(const image& image);
-  void setText(string text);
-
-  pItem(Item& item) : pAction(item), item(item) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pCheckItem : public pAction {
-  CheckItem& checkItem;
-
-  void setChecked(bool checked);
-  void setText(string text);
-
-  pCheckItem(CheckItem& checkItem) : pAction(checkItem), checkItem(checkItem) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pRadioItem : public pAction {
-  RadioItem& radioItem;
-
-  void setChecked();
-  void setGroup(const group<RadioItem>& group);
-  void setText(string text);
-
-  pRadioItem(RadioItem& radioItem) : pAction(radioItem), radioItem(radioItem) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  void onActivate();
-  pRadioItem& parent();
-};
-
-struct pSizable : public pObject {
-  Sizable& sizable;
-
-  virtual Position displacement() { return {0, 0}; }
-
-  pSizable(Sizable &sizable) : pObject(sizable), sizable(sizable) {}
-};
-
-struct pLayout : public pSizable {
-  Layout& layout;
-
-  pLayout(Layout& layout) : pSizable(layout), layout(layout) {}
-};
-
-struct pWidget : public pSizable {
-  Widget& widget;
-  GtkWidget* gtkWidget = nullptr;
-  GtkWidget* gtkParent = nullptr;
-
-  virtual GtkWidget* container(Widget& widget);
-  virtual bool focused();
-  virtual Size minimumSize();
-  virtual void setEnabled(bool enabled);
-  virtual void setFocused();
-  virtual void setFont(string font);
-  virtual void setGeometry(Geometry geometry);
-  virtual void setVisible(bool visible);
-
-  pWidget(Widget& widget) : pSizable(widget), widget(widget) {}
-  void constructor();
-  void destructor();
-  virtual void orphan();
-};
-
-struct pButton : public pWidget {
-  Button& button;
-
-  Size minimumSize();
-  void setImage(const image& image, Orientation orientation);
-  void setText(string text);
-
-  pButton(Button& button) : pWidget(button), button(button) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pCanvas : public pWidget {
-  Canvas& canvas;
-  GdkPixbuf* surface = nullptr;
-  unsigned surfaceWidth = 0;
-  unsigned surfaceHeight = 0;
-
-  Size minimumSize();
-  void setDroppable(bool droppable);
-  void setGeometry(Geometry geometry);
-  void setMode(Canvas::Mode mode);
-  void setSize(Size size);
-
-  pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  void onExpose(GdkEventExpose* event);
-  void rasterize();
-  void redraw();
-  void release();
-};
-
-struct pCheckButton : public pWidget {
-  CheckButton& checkButton;
-
-  Size minimumSize();
-  void setChecked(bool checked);
-  void setImage(const image& image, Orientation orientation);
-  void setText(string text);
-
-  pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  void onToggle();
-};
-
-struct pCheckLabel : public pWidget {
-  CheckLabel& checkLabel;
-
-  Size minimumSize();
-  void setChecked(bool checked);
-  void setText(string text);
-
-  pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pComboButton : public pWidget {
-  ComboButton& comboButton;
-  unsigned itemCounter;
-
-  void append(string text);
-  Size minimumSize();
-  void remove(unsigned selection);
-  void reset();
-  void setSelection(unsigned selection);
-  void setText(unsigned selection, string text);
-
-  pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pConsole : public pWidget {
-  Console& console;
-  GtkWidget* subWidget;
-  GtkTextBuffer* textBuffer;
-  string previousPrompt;
-  lstring history;
-  unsigned historyOffset = 0;
-
-  void print(string text);
-  void reset();
-  void setBackgroundColor(Color color);
-  void setForegroundColor(Color color);
-  void setPrompt(string prompt);
-
-  pConsole(Console& console) : pWidget(console), console(console) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  bool keyPress(unsigned scancode, unsigned mask);
-  void seekToEnd();
-  void seekToMark();
-};
-
-struct pFrame : public pWidget {
-  Frame& frame;
-
-  GtkWidget* container(Widget& widget);
-  Position containerOffset();
-  void setEnabled(bool enabled);
-  void setGeometry(Geometry geometry);
-  void setText(string text);
-  void setVisible(bool visible);
-
-  pFrame(Frame& frame) : pWidget(frame), frame(frame) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pHexEdit : public pWidget {
-  HexEdit& hexEdit;
-  GtkWidget* container;
-  GtkWidget* subWidget;
-  GtkWidget* scrollBar;
-  GtkTextBuffer* textBuffer;
-  GtkTextMark* textCursor;
-
-  bool focused();
-  void setBackgroundColor(Color color);
-  void setColumns(unsigned columns);
-  void setForegroundColor(Color color);
-  void setLength(unsigned length);
-  void setOffset(unsigned offset);
-  void setRows(unsigned rows);
-  void update();
-
-  pHexEdit(HexEdit& hexEdit) : pWidget(hexEdit), hexEdit(hexEdit) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  unsigned cursorPosition();
-  bool keyPress(unsigned scancode, unsigned mask);
-  signed rows();
-  signed rowsScrollable();
-  void scroll(signed position);
-  void setCursorPosition(unsigned position);
-  void setScroll();
-  void updateScroll();
-};
-
-struct pHorizontalScroller : public pWidget {
-  HorizontalScroller& horizontalScroller;
-
-  Size minimumSize();
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  pHorizontalScroller(HorizontalScroller& horizontalScroller) : pWidget(horizontalScroller), horizontalScroller(horizontalScroller) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pHorizontalSlider : public pWidget {
-  HorizontalSlider& horizontalSlider;
-
-  Size minimumSize();
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  pHorizontalSlider(HorizontalSlider& horizontalSlider) : pWidget(horizontalSlider), horizontalSlider(horizontalSlider) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pLabel : public pWidget {
-  Label& label;
-
-  Size minimumSize();
-  void setText(string text);
-
-  pLabel(Label& label) : pWidget(label), label(label) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pLineEdit : public pWidget {
-  LineEdit& lineEdit;
-
-  Size minimumSize();
-  void setBackgroundColor(Color color);
-  void setEditable(bool editable);
-  void setForegroundColor(Color color);
-  void setText(string text);
-  string text();
-
-  pLineEdit(LineEdit& lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pListView : public pWidget {
-  ListView& listView;
-  GtkWidget* subWidget;
-  GtkListStore* store;
-  struct GtkColumn {
-    GtkTreeViewColumn* column;
-    GtkCellRenderer* checkbutton;
-    GtkCellRenderer* icon;
-    GtkCellRenderer* text;
-    GtkWidget *label;
-  };
-  vector<GtkColumn> column;
-
-  void append(const lstring& text);
-  void autoSizeColumns();
-  bool focused();
-  void remove(unsigned selection);
-  void reset();
-  void setBackgroundColor(Color color);
-  void setCheckable(bool checkable);
-  void setChecked(unsigned selection, bool checked);
-  void setForegroundColor(Color color);
-  void setHeaderText(const lstring& text);
-  void setHeaderVisible(bool visible);
-  void setImage(unsigned selection, unsigned position, const image& image);
-  void setSelected(bool selected);
-  void setSelection(unsigned row);
-  void setText(unsigned selection, unsigned position, string text);
-
-  pListView(ListView& listView) : pWidget(listView), listView(listView) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  void setFocused();
-  void setFont(string font);
-};
-
-struct pProgressBar : public pWidget {
-  ProgressBar& progressBar;
-
-  Size minimumSize();
-  void setPosition(unsigned position);
-
-  pProgressBar(ProgressBar& progressBar) : pWidget(progressBar), progressBar(progressBar) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pRadioButton : public pWidget {
-  RadioButton& radioButton;
-
-  Size minimumSize();
-  void setChecked();
-  void setGroup(const group<RadioButton>& group);
-  void setImage(const image& image, Orientation orientation);
-  void setText(string text);
-
-  pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  void onActivate();
-  pRadioButton& parent();
-};
-
-struct pRadioLabel : public pWidget {
-  RadioLabel& radioLabel;
-
-  Size minimumSize();
-  void setChecked();
-  void setGroup(const group<RadioLabel>& group);
-  void setText(string text);
-
-  pRadioLabel(RadioLabel& radioLabel) : pWidget(radioLabel), radioLabel(radioLabel) {}
-  void onActivate();
-  pRadioLabel& parent();
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pTabFrame : public pWidget {
-  TabFrame& tabFrame;
-  struct Tab {
-    GtkWidget* child;
-    GtkWidget* container;
-    GtkWidget* layout;
-    GtkWidget* image;
-    GtkWidget* title;
-  };
-  vector<Tab> tabs;
-
-  void append(string text, const image& image);
-  GtkWidget* container(Widget& widget);
-  Position displacement();
-  void remove(unsigned selection);
-  void setEnabled(bool enabled);
-  void setGeometry(Geometry geometry);
-  void setImage(unsigned selection, const image& image);
-  void setSelection(unsigned selection);
-  void setText(unsigned selection, string text);
-  void setVisible(bool visible);
-
-  pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {}
-  void constructor();
-  void destructor();
-  void orphan();
-  void setFont(string font);
-  void synchronizeLayout();
-};
-
-struct pTextEdit : public pWidget {
-  TextEdit& textEdit;
-  GtkWidget* subWidget;
-  GtkTextBuffer* textBuffer;
-
-  bool focused();
-  void setBackgroundColor(Color color);
-  void setCursorPosition(unsigned position);
-  void setEditable(bool editable);
-  void setForegroundColor(Color color);
-  void setText(string text);
-  void setWordWrap(bool wordWrap);
-  string text();
-
-  pTextEdit(TextEdit& textEdit) : pWidget(textEdit), textEdit(textEdit) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pVerticalScroller : public pWidget {
-  VerticalScroller& verticalScroller;
-
-  Size minimumSize();
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  pVerticalScroller(VerticalScroller& verticalScroller) : pWidget(verticalScroller), verticalScroller(verticalScroller) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pVerticalSlider : public pWidget {
-  VerticalSlider& verticalSlider;
-
-  Size minimumSize();
-  void setLength(unsigned length);
-  void setPosition(unsigned position);
-
-  pVerticalSlider(VerticalSlider& verticalSlider) : pWidget(verticalSlider), verticalSlider(verticalSlider) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-struct pViewport : public pWidget {
-  Viewport& viewport;
-
-  uintptr_t handle();
-  void setDroppable(bool droppable);
-
-  pViewport(Viewport& viewport) : pWidget(viewport), viewport(viewport) {}
-  void constructor();
-  void destructor();
-  void orphan();
-};
-
-}
diff --git a/phoenix/gtk/timer.cpp b/phoenix/gtk/timer.cpp
deleted file mode 100644
index 3be9964f..00000000
--- a/phoenix/gtk/timer.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace phoenix {
-
-static guint Timer_trigger(pTimer* self) {
-  //timer may have been disabled prior to triggering, so check state
-  if(self->timer.state.enabled) {
-    if(self->timer.onActivate) self->timer.onActivate();
-  }
-  //callback may have disabled timer, so check state again
-  if(self->timer.state.enabled) {
-    g_timeout_add(self->timer.state.interval, (GSourceFunc)Timer_trigger, (gpointer)self);
-  }
-  //kill this timer instance (it is spawned above if needed again)
-  return false;
-}
-
-void pTimer::setEnabled(bool enabled) {
-  if(enabled) {
-    g_timeout_add(timer.state.interval, (GSourceFunc)Timer_trigger, (gpointer)this);
-  }
-}
-
-void pTimer::setInterval(unsigned interval) {
-}
-
-void pTimer::constructor() {
-}
-
-}
diff --git a/phoenix/gtk/utility.cpp b/phoenix/gtk/utility.cpp
deleted file mode 100644
index 1645ab76..00000000
--- a/phoenix/gtk/utility.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-namespace phoenix {
-
-static GdkColor CreateColor(uint8_t r, uint8_t g, uint8_t b) {
-  GdkColor color;
-  color.pixel = (r << 16) | (g << 8) | (b << 0);
-  color.red = (r << 8) | (r << 0);
-  color.green = (g << 8) | (g << 0);
-  color.blue = (b << 8) | (b << 0);
-  return color;
-}
-
-static GdkPixbuf* CreatePixbuf(const nall::image& image, bool scale = false) {
-  nall::image gdkImage = image;
-  gdkImage.transform(0, 32, 255u << 24, 255u << 0, 255u << 8, 255u << 16);
-  if(scale) gdkImage.scale(15, 15);
-
-  GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, gdkImage.width, gdkImage.height);
-  memcpy(gdk_pixbuf_get_pixels(pixbuf), gdkImage.data, gdkImage.width * gdkImage.height * 4);
-
-  return pixbuf;
-}
-
-static GtkImage* CreateImage(const nall::image& image, bool scale = false) {
-  GdkPixbuf* pixbuf = CreatePixbuf(image, scale);
-  GtkImage* gtkImage = (GtkImage*)gtk_image_new_from_pixbuf(pixbuf);
-  g_object_unref(pixbuf);
-  return gtkImage;
-}
-
-static lstring DropPaths(GtkSelectionData* data) {
-  gchar** uris = gtk_selection_data_get_uris(data);
-  if(uris == nullptr) return {};
-
-  lstring paths;
-  for(unsigned n = 0; uris[n] != nullptr; n++) {
-    gchar* pathname = g_filename_from_uri(uris[n], nullptr, nullptr);
-    if(pathname == nullptr) continue;
-
-    string path = pathname;
-    g_free(pathname);
-    if(directory::exists(path) && !path.endsWith("/")) path.append("/");
-    paths.append(path);
-  }
-
-  g_strfreev(uris);
-  return paths;
-}
-
-static Position GetDisplacement(Sizable* sizable) {
-  Position position;
-  while(sizable->state.parent) {
-    Position displacement = sizable->state.parent->p.displacement();
-    position.x += displacement.x;
-    position.y += displacement.y;
-    sizable = sizable->state.parent;
-  }
-  return position;
-}
-
-static Layout* GetParentWidgetLayout(Sizable* sizable) {
-  while(sizable) {
-    if(sizable->state.parent && dynamic_cast<TabFrame*>(sizable->state.parent)) return (Layout*)sizable;
-    sizable = sizable->state.parent;
-  }
-  return nullptr;
-}
-
-static Widget* GetParentWidget(Sizable* sizable) {
-  while(sizable) {
-    if(sizable->state.parent && dynamic_cast<TabFrame*>(sizable->state.parent)) return (Widget*)sizable->state.parent;
-    sizable = sizable->state.parent;
-  }
-  return nullptr;
-}
-
-static Keyboard::Keycode Keysym(unsigned keysym) {
-  switch(keysym) {
-  case GDK_Escape: return Keyboard::Keycode::Escape;
-  case GDK_F1: return Keyboard::Keycode::F1;
-  case GDK_F2: return Keyboard::Keycode::F2;
-  case GDK_F3: return Keyboard::Keycode::F3;
-  case GDK_F4: return Keyboard::Keycode::F4;
-  case GDK_F5: return Keyboard::Keycode::F5;
-  case GDK_F6: return Keyboard::Keycode::F6;
-  case GDK_F7: return Keyboard::Keycode::F7;
-  case GDK_F8: return Keyboard::Keycode::F8;
-  case GDK_F9: return Keyboard::Keycode::F9;
-  case GDK_F10: return Keyboard::Keycode::F10;
-  case GDK_F11: return Keyboard::Keycode::F11;
-  case GDK_F12: return Keyboard::Keycode::F12;
-
-  case GDK_Print: return Keyboard::Keycode::PrintScreen;
-  //Keyboard::Keycode::SysRq
-  case GDK_Scroll_Lock: return Keyboard::Keycode::ScrollLock;
-  case GDK_Pause: return Keyboard::Keycode::Pause;
-  //Keyboard::Keycode::Break
-
-  case GDK_Insert: return Keyboard::Keycode::Insert;
-  case GDK_Delete: return Keyboard::Keycode::Delete;
-  case GDK_Home: return Keyboard::Keycode::Home;
-  case GDK_End: return Keyboard::Keycode::End;
-  case GDK_Prior: return Keyboard::Keycode::PageUp;
-  case GDK_Next: return Keyboard::Keycode::PageDown;
-
-  case GDK_Up: return Keyboard::Keycode::Up;
-  case GDK_Down: return Keyboard::Keycode::Down;
-  case GDK_Left: return Keyboard::Keycode::Left;
-  case GDK_Right: return Keyboard::Keycode::Right;
-
-  case GDK_grave: return Keyboard::Keycode::Grave;
-  case GDK_1: return Keyboard::Keycode::Number1;
-  case GDK_2: return Keyboard::Keycode::Number2;
-  case GDK_3: return Keyboard::Keycode::Number3;
-  case GDK_4: return Keyboard::Keycode::Number4;
-  case GDK_5: return Keyboard::Keycode::Number5;
-  case GDK_6: return Keyboard::Keycode::Number6;
-  case GDK_7: return Keyboard::Keycode::Number7;
-  case GDK_8: return Keyboard::Keycode::Number8;
-  case GDK_9: return Keyboard::Keycode::Number9;
-  case GDK_0: return Keyboard::Keycode::Number0;
-  case GDK_minus: return Keyboard::Keycode::Minus;
-  case GDK_equal: return Keyboard::Keycode::Equal;
-  case GDK_BackSpace: return Keyboard::Keycode::Backspace;
-
-  case GDK_asciitilde: return Keyboard::Keycode::Tilde;
-  case GDK_exclam: return Keyboard::Keycode::Exclamation;
-  case GDK_at: return Keyboard::Keycode::At;
-  case GDK_numbersign: return Keyboard::Keycode::Pound;
-  case GDK_dollar: return Keyboard::Keycode::Dollar;
-  case GDK_percent: return Keyboard::Keycode::Percent;
-  case GDK_asciicircum: return Keyboard::Keycode::Power;
-  case GDK_ampersand: return Keyboard::Keycode::Ampersand;
-  case GDK_asterisk: return Keyboard::Keycode::Asterisk;
-  case GDK_parenleft: return Keyboard::Keycode::ParenthesisLeft;
-  case GDK_parenright: return Keyboard::Keycode::ParenthesisRight;
-  case GDK_underscore: return Keyboard::Keycode::Underscore;
-  case GDK_plus: return Keyboard::Keycode::Plus;
-
-  case GDK_bracketleft: return Keyboard::Keycode::BracketLeft;
-  case GDK_bracketright: return Keyboard::Keycode::BracketRight;
-  case GDK_backslash: return Keyboard::Keycode::Backslash;
-  case GDK_semicolon: return Keyboard::Keycode::Semicolon;
-  case GDK_apostrophe: return Keyboard::Keycode::Apostrophe;
-  case GDK_comma: return Keyboard::Keycode::Comma;
-  case GDK_period: return Keyboard::Keycode::Period;
-  case GDK_slash: return Keyboard::Keycode::Slash;
-
-  case GDK_braceleft: return Keyboard::Keycode::BraceLeft;
-  case GDK_braceright: return Keyboard::Keycode::BraceRight;
-  case GDK_bar: return Keyboard::Keycode::Pipe;
-  case GDK_colon: return Keyboard::Keycode::Colon;
-  case GDK_quotedbl: return Keyboard::Keycode::Quote;
-  case GDK_less: return Keyboard::Keycode::CaretLeft;
-  case GDK_greater: return Keyboard::Keycode::CaretRight;
-  case GDK_question: return Keyboard::Keycode::Question;
-
-  case GDK_Tab: return Keyboard::Keycode::Tab;
-  case GDK_Caps_Lock: return Keyboard::Keycode::CapsLock;
-  case GDK_Return: return Keyboard::Keycode::Return;
-  case GDK_Shift_L: return Keyboard::Keycode::ShiftLeft;
-  case GDK_Shift_R: return Keyboard::Keycode::ShiftRight;
-  case GDK_Control_L: return Keyboard::Keycode::ControlLeft;
-  case GDK_Control_R: return Keyboard::Keycode::ControlRight;
-  case GDK_Super_L: return Keyboard::Keycode::SuperLeft;
-  case GDK_Super_R: return Keyboard::Keycode::SuperRight;
-  case GDK_Alt_L: return Keyboard::Keycode::AltLeft;
-  case GDK_Alt_R: return Keyboard::Keycode::AltRight;
-  case GDK_space: return Keyboard::Keycode::Space;
-  case GDK_Menu: return Keyboard::Keycode::Menu;
-
-  case GDK_A: return Keyboard::Keycode::A;
-  case GDK_B: return Keyboard::Keycode::B;
-  case GDK_C: return Keyboard::Keycode::C;
-  case GDK_D: return Keyboard::Keycode::D;
-  case GDK_E: return Keyboard::Keycode::E;
-  case GDK_F: return Keyboard::Keycode::F;
-  case GDK_G: return Keyboard::Keycode::G;
-  case GDK_H: return Keyboard::Keycode::H;
-  case GDK_I: return Keyboard::Keycode::I;
-  case GDK_J: return Keyboard::Keycode::J;
-  case GDK_K: return Keyboard::Keycode::K;
-  case GDK_L: return Keyboard::Keycode::L;
-  case GDK_M: return Keyboard::Keycode::M;
-  case GDK_N: return Keyboard::Keycode::N;
-  case GDK_O: return Keyboard::Keycode::O;
-  case GDK_P: return Keyboard::Keycode::P;
-  case GDK_Q: return Keyboard::Keycode::Q;
-  case GDK_R: return Keyboard::Keycode::R;
-  case GDK_S: return Keyboard::Keycode::S;
-  case GDK_T: return Keyboard::Keycode::T;
-  case GDK_U: return Keyboard::Keycode::U;
-  case GDK_V: return Keyboard::Keycode::V;
-  case GDK_W: return Keyboard::Keycode::W;
-  case GDK_X: return Keyboard::Keycode::X;
-  case GDK_Y: return Keyboard::Keycode::Y;
-  case GDK_Z: return Keyboard::Keycode::Z;
-
-  case GDK_a: return Keyboard::Keycode::a;
-  case GDK_b: return Keyboard::Keycode::b;
-  case GDK_c: return Keyboard::Keycode::c;
-  case GDK_d: return Keyboard::Keycode::d;
-  case GDK_e: return Keyboard::Keycode::e;
-  case GDK_f: return Keyboard::Keycode::f;
-  case GDK_g: return Keyboard::Keycode::g;
-  case GDK_h: return Keyboard::Keycode::h;
-  case GDK_i: return Keyboard::Keycode::i;
-  case GDK_j: return Keyboard::Keycode::j;
-  case GDK_k: return Keyboard::Keycode::k;
-  case GDK_l: return Keyboard::Keycode::l;
-  case GDK_m: return Keyboard::Keycode::m;
-  case GDK_n: return Keyboard::Keycode::n;
-  case GDK_o: return Keyboard::Keycode::o;
-  case GDK_p: return Keyboard::Keycode::p;
-  case GDK_q: return Keyboard::Keycode::q;
-  case GDK_r: return Keyboard::Keycode::r;
-  case GDK_s: return Keyboard::Keycode::s;
-  case GDK_t: return Keyboard::Keycode::t;
-  case GDK_u: return Keyboard::Keycode::u;
-  case GDK_v: return Keyboard::Keycode::v;
-  case GDK_w: return Keyboard::Keycode::w;
-  case GDK_x: return Keyboard::Keycode::x;
-  case GDK_y: return Keyboard::Keycode::y;
-  case GDK_z: return Keyboard::Keycode::z;
-
-  case GDK_Num_Lock: return Keyboard::Keycode::NumLock;
-  case GDK_KP_Divide: return Keyboard::Keycode::Divide;
-  case GDK_KP_Multiply: return Keyboard::Keycode::Multiply;
-  case GDK_KP_Subtract: return Keyboard::Keycode::Subtract;
-  case GDK_KP_Add: return Keyboard::Keycode::Add;
-  case GDK_KP_Enter: return Keyboard::Keycode::Enter;
-  case GDK_KP_Decimal: return Keyboard::Keycode::Point;
-
-  case GDK_KP_1: return Keyboard::Keycode::Keypad1;
-  case GDK_KP_2: return Keyboard::Keycode::Keypad2;
-  case GDK_KP_3: return Keyboard::Keycode::Keypad3;
-  case GDK_KP_4: return Keyboard::Keycode::Keypad4;
-  case GDK_KP_5: return Keyboard::Keycode::Keypad5;
-  case GDK_KP_6: return Keyboard::Keycode::Keypad6;
-  case GDK_KP_7: return Keyboard::Keycode::Keypad7;
-  case GDK_KP_8: return Keyboard::Keycode::Keypad8;
-  case GDK_KP_9: return Keyboard::Keycode::Keypad9;
-  case GDK_KP_0: return Keyboard::Keycode::Keypad0;
-
-  case GDK_KP_Home: return Keyboard::Keycode::KeypadHome;
-  case GDK_KP_End: return Keyboard::Keycode::KeypadEnd;
-  case GDK_KP_Page_Up: return Keyboard::Keycode::KeypadPageUp;
-  case GDK_KP_Page_Down: return Keyboard::Keycode::KeypadPageDown;
-  case GDK_KP_Up: return Keyboard::Keycode::KeypadUp;
-  case GDK_KP_Down: return Keyboard::Keycode::KeypadDown;
-  case GDK_KP_Left: return Keyboard::Keycode::KeypadLeft;
-  case GDK_KP_Right: return Keyboard::Keycode::KeypadRight;
-  case GDK_KP_Begin: return Keyboard::Keycode::KeypadCenter;
-  case GDK_KP_Insert: return Keyboard::Keycode::KeypadInsert;
-  case GDK_KP_Delete: return Keyboard::Keycode::KeypadDelete;
-  }
-  return Keyboard::Keycode::None;
-}
-
-}
diff --git a/phoenix/gtk/widget/button.cpp b/phoenix/gtk/widget/button.cpp
deleted file mode 100644
index 16e6bb37..00000000
--- a/phoenix/gtk/widget/button.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-namespace phoenix {
-
-static void Button_activate(Button* self) {
-  if(self->onActivate) self->onActivate();
-}
-
-Size pButton::minimumSize() {
-  Size size = pFont::size(widget.state.font, button.state.text);
-
-  if(button.state.orientation == Orientation::Horizontal) {
-    size.width += button.state.image.width;
-    size.height = max(button.state.image.height, size.height);
-  }
-
-  if(button.state.orientation == Orientation::Vertical) {
-    size.width = max(button.state.image.width, size.width);
-    size.height += button.state.image.height;
-  }
-
-  return {size.width + 24, size.height + 12};
-}
-
-void pButton::setImage(const image& image, Orientation orientation) {
-  if(image.empty() == false) {
-    GtkImage* gtkImage = CreateImage(image);
-    gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage);
-  } else {
-    gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr);
-  }
-  switch(orientation) {
-  case Orientation::Horizontal: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_LEFT); break;
-  case Orientation::Vertical:   gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_TOP);  break;
-  }
-}
-
-void pButton::setText(string text) {
-  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
-  setFont(widget.state.font);
-}
-
-void pButton::constructor() {
-  gtkWidget = gtk_button_new();
-  g_signal_connect_swapped(G_OBJECT(gtkWidget), "clicked", G_CALLBACK(Button_activate), (gpointer)&button);
-
-  setText(button.state.text);
-}
-
-void pButton::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pButton::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/canvas.cpp b/phoenix/gtk/widget/canvas.cpp
deleted file mode 100644
index 4fad9909..00000000
--- a/phoenix/gtk/widget/canvas.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-namespace phoenix {
-
-static void Canvas_drop(GtkWidget* widget, GdkDragContext* context, signed x, signed y,
-GtkSelectionData* data, unsigned type, unsigned timestamp, Canvas* canvas) {
-  if(!canvas->state.droppable) return;
-  lstring paths = DropPaths(data);
-  if(paths.empty()) return;
-  if(canvas->onDrop) canvas->onDrop(paths);
-}
-
-static signed Canvas_expose(GtkWidget* widget, GdkEventExpose* event, Canvas* self) {
-  self->p.onExpose(event);
-  return true;
-}
-
-static signed Canvas_mouseLeave(GtkWidget* widget, GdkEventButton* event, Canvas* self) {
-  if(self->onMouseLeave) self->onMouseLeave();
-  return true;
-}
-
-static signed Canvas_mouseMove(GtkWidget* widget, GdkEventButton* event, Canvas* self) {
-  if(self->onMouseMove) self->onMouseMove({(signed)event->x, (signed)event->y});
-  return true;
-}
-
-static signed Canvas_mousePress(GtkWidget* widget, GdkEventButton* event, Canvas* self) {
-  if(self->onMousePress) switch(event->button) {
-  case 1: self->onMousePress(Mouse::Button::Left); break;
-  case 2: self->onMousePress(Mouse::Button::Middle); break;
-  case 3: self->onMousePress(Mouse::Button::Right); break;
-  }
-  return true;
-}
-
-static signed Canvas_mouseRelease(GtkWidget* widget, GdkEventButton* event, Canvas* self) {
-  if(self->onMouseRelease) switch(event->button) {
-  case 1: self->onMouseRelease(Mouse::Button::Left); break;
-  case 2: self->onMouseRelease(Mouse::Button::Middle); break;
-  case 3: self->onMouseRelease(Mouse::Button::Right); break;
-  }
-  return true;
-}
-
-Size pCanvas::minimumSize() {
-  return {canvas.state.width, canvas.state.height};
-}
-
-void pCanvas::setDroppable(bool droppable) {
-  gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
-  if(droppable) gtk_drag_dest_add_uri_targets(gtkWidget);
-}
-
-void pCanvas::setGeometry(Geometry geometry) {
-  if(canvas.state.width == 0 || canvas.state.height == 0) rasterize();
-  unsigned width = canvas.state.width;
-  unsigned height = canvas.state.height;
-  if(width == 0) width = widget.state.geometry.width;
-  if(height == 0) height = widget.state.geometry.height;
-
-  if(width < geometry.width) {
-    geometry.x += (geometry.width - width) / 2;
-    geometry.width = width;
-  }
-
-  if(height < geometry.height) {
-    geometry.y += (geometry.height - height) / 2;
-    geometry.height = height;
-  }
-
-  pWidget::setGeometry(geometry);
-}
-
-void pCanvas::setMode(Canvas::Mode mode) {
-  rasterize(), redraw();
-}
-
-void pCanvas::setSize(Size size) {
-  rasterize(), redraw();
-}
-
-void pCanvas::constructor() {
-  gtkWidget = gtk_drawing_area_new();
-//gtk_widget_set_double_buffered(gtkWidget, false);
-  gtk_widget_add_events(gtkWidget,
-    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
-  g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Canvas_mousePress), (gpointer)&canvas);
-  g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Canvas_mouseRelease), (gpointer)&canvas);
-  g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Canvas_drop), (gpointer)&canvas);
-  g_signal_connect(G_OBJECT(gtkWidget), "expose-event", G_CALLBACK(Canvas_expose), (gpointer)&canvas);
-  g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Canvas_mouseLeave), (gpointer)&canvas);
-  g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Canvas_mouseMove), (gpointer)&canvas);
-
-  setDroppable(canvas.state.droppable);
-  rasterize(), redraw();
-}
-
-void pCanvas::destructor() {
-  release();
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pCanvas::orphan() {
-  destructor();
-  constructor();
-}
-
-void pCanvas::onExpose(GdkEventExpose* expose) {
-  if(surface) gdk_draw_pixbuf(gtk_widget_get_window(gtkWidget), nullptr, surface, 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
-}
-
-void pCanvas::rasterize() {
-  unsigned width = canvas.state.width;
-  unsigned height = canvas.state.height;
-  if(width == 0) width = widget.state.geometry.width;
-  if(height == 0) height = widget.state.geometry.height;
-
-  if(width != surfaceWidth || height != surfaceHeight) release();
-  surfaceWidth = width;
-  surfaceHeight = height;
-
-  if(width == 0 || height == 0) return;
-
-  if(!surface) surface = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height);
-  uint32_t* buffer = (uint32_t*)gdk_pixbuf_get_pixels(surface);
-
-  if(canvas.state.mode == Canvas::Mode::Color) {
-    uint32_t color = canvas.state.color.argb();
-    for(unsigned n = 0; n < width * height; n++) buffer[n] = color;
-  }
-
-  if(canvas.state.mode == Canvas::Mode::Gradient) {
-    nall::image image;
-    image.allocate(width, height);
-    image.gradient(
-      canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb()
-    );
-    memcpy(buffer, image.data, image.size);
-  }
-
-  if(canvas.state.mode == Canvas::Mode::Image) {
-    nall::image image = canvas.state.image;
-    image.scale(width, height);
-    image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
-    memcpy(buffer, image.data, image.size);
-  }
-
-  if(canvas.state.mode == Canvas::Mode::Data) {
-    if(width == canvas.state.width && height == canvas.state.height) {
-      memcpy(buffer, canvas.state.data, width * height * sizeof(uint32_t));
-    } else {
-      memset(buffer, 0x00, width * height * sizeof(uint32_t));
-    }
-  }
-
-  //ARGB -> ABGR conversion
-  for(unsigned n = 0; n < width * height; n++) {
-    uint32_t color = *buffer;
-    color = (color & 0xff00ff00) | ((color & 0xff0000) >> 16) | ((color & 0x0000ff) << 16);
-    *buffer++ = color;
-  }
-}
-
-void pCanvas::redraw() {
-  if(gtk_widget_get_realized(gtkWidget)) {
-    gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), nullptr, true);
-  }
-}
-
-void pCanvas::release() {
-  if(surface == nullptr) return;
-  g_object_unref(surface);
-  surface = nullptr;
-  surfaceWidth = 0;
-  surfaceHeight = 0;
-}
-
-}
diff --git a/phoenix/gtk/widget/check-button.cpp b/phoenix/gtk/widget/check-button.cpp
deleted file mode 100644
index d72d91ff..00000000
--- a/phoenix/gtk/widget/check-button.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-namespace phoenix {
-
-static void CheckButton_toggle(GtkToggleButton* toggleButton, CheckButton* self) {
-  self->p.onToggle();
-}
-
-Size pCheckButton::minimumSize() {
-  Size size = pFont::size(widget.state.font, checkButton.state.text);
-
-  if(checkButton.state.orientation == Orientation::Horizontal) {
-    size.width += checkButton.state.image.width;
-    size.height = max(checkButton.state.image.height, size.height);
-  }
-
-  if(checkButton.state.orientation == Orientation::Vertical) {
-    size.width = max(checkButton.state.image.width, size.width);
-    size.height += checkButton.state.image.height;
-  }
-
-  return {size.width + 24, size.height + 12};
-}
-
-void pCheckButton::setChecked(bool checked) {
-  locked = true;
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), checked);
-  locked = false;
-}
-
-void pCheckButton::setImage(const image& image, Orientation orientation) {
-  if(image.empty() == false) {
-    GtkImage* gtkImage = CreateImage(image);
-    gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage);
-  } else {
-    gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr);
-  }
-  switch(orientation) {
-  case Orientation::Horizontal: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_LEFT); break;
-  case Orientation::Vertical:   gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_TOP);  break;
-  }
-}
-
-void pCheckButton::setText(string text) {
-  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
-  setFont(widget.state.font);
-}
-
-void pCheckButton::constructor() {
-  gtkWidget = gtk_toggle_button_new();
-  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckButton_toggle), (gpointer)&checkButton);
-
-  setChecked(checkButton.state.checked);
-  setText(checkButton.state.text);
-}
-
-void pCheckButton::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pCheckButton::orphan() {
-  destructor();
-  constructor();
-}
-
-void pCheckButton::onToggle() {
-  if(locked) return;
-  checkButton.state.checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget));
-  if(checkButton.onToggle) checkButton.onToggle();
-}
-
-}
diff --git a/phoenix/gtk/widget/check-label.cpp b/phoenix/gtk/widget/check-label.cpp
deleted file mode 100644
index 6e59fbd6..00000000
--- a/phoenix/gtk/widget/check-label.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace phoenix {
-
-static void CheckLabel_toggle(GtkToggleButton* toggleButton, CheckLabel* self) {
-  self->state.checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->p.gtkWidget));
-  if(!self->p.locked && self->onToggle) self->onToggle();
-}
-
-Size pCheckLabel::minimumSize() {
-  Size size = pFont::size(widget.state.font, checkLabel.state.text);
-  return {size.width + 28, size.height + 4};
-}
-
-void pCheckLabel::setChecked(bool checked) {
-  locked = true;
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), checked);
-  locked = false;
-}
-
-void pCheckLabel::setText(string text) {
-  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
-}
-
-void pCheckLabel::constructor() {
-  gtkWidget = gtk_check_button_new_with_label("");
-  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckLabel_toggle), (gpointer)&checkLabel);
-
-  setChecked(checkLabel.state.checked);
-  setText(checkLabel.state.text);
-}
-
-void pCheckLabel::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pCheckLabel::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/combo-button.cpp b/phoenix/gtk/widget/combo-button.cpp
deleted file mode 100644
index ed523f1a..00000000
--- a/phoenix/gtk/widget/combo-button.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-namespace phoenix {
-
-static void ComboButton_change(ComboButton* self) {
-  if(!self->p.locked) {
-    self->state.selection = gtk_combo_box_get_active(GTK_COMBO_BOX(self->p.gtkWidget));
-    if(self->onChange) self->onChange();
-  }
-}
-
-void pComboButton::append(string text) {
-  gtk_combo_box_append_text(GTK_COMBO_BOX(gtkWidget), text);
-  if(itemCounter++ == 0) comboButton.setSelection(0);
-}
-
-Size pComboButton::minimumSize() {
-  unsigned maximumWidth = 0;
-  for(auto& item : comboButton.state.text) maximumWidth = max(maximumWidth, pFont::size(widget.state.font, item).width);
-
-  Size size = pFont::size(widget.state.font, " ");
-  return {maximumWidth + 44, size.height + 12};
-}
-
-void pComboButton::remove(unsigned selection) {
-  locked = true;
-  gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), selection);
-  itemCounter--;
-  locked = false;
-
-  //when removing the actively selected item, reset the selection to the first entry
-  if(selection == comboButton.state.selection) comboButton.setSelection(0);
-}
-
-void pComboButton::reset() {
-  locked = true;
-  gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(gtkWidget))));
-  itemCounter = 0;
-  locked = false;
-}
-
-void pComboButton::setSelection(unsigned selection) {
-  locked = true;
-  gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), selection);
-  locked = false;
-}
-
-void pComboButton::setText(unsigned selection, string text) {
-  locked = true;
-  gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), selection);
-  gtk_combo_box_insert_text(GTK_COMBO_BOX(gtkWidget), selection, text);
-  gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), comboButton.state.selection);
-  locked = false;
-}
-
-void pComboButton::constructor() {
-  itemCounter = 0;
-  gtkWidget = gtk_combo_box_new_text();
-  g_signal_connect_swapped(G_OBJECT(gtkWidget), "changed", G_CALLBACK(ComboButton_change), (gpointer)&comboButton);
-
-  locked = true;
-  for(auto& text : comboButton.state.text) append(text);
-  locked = false;
-  setSelection(comboButton.state.selection);
-}
-
-void pComboButton::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pComboButton::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/frame.cpp b/phoenix/gtk/widget/frame.cpp
deleted file mode 100644
index f41e1c3c..00000000
--- a/phoenix/gtk/widget/frame.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-namespace phoenix {
-
-GtkWidget* pFrame::container(Widget& widget) {
-  return gtk_widget_get_parent(gtkWidget);
-}
-
-Position pFrame::containerOffset() {
-  return {0, 0};
-}
-
-void pFrame::setEnabled(bool enabled) {
-  if(frame.state.layout) frame.state.layout->setEnabled(frame.state.layout->enabled());
-  pWidget::setEnabled(enabled);
-}
-
-void pFrame::setGeometry(Geometry geometry) {
-  pWidget::setGeometry(geometry);
-  if(frame.state.layout == nullptr) return;
-  Size size = pFont::size(widget.state.font, frame.state.text);
-  if(frame.state.text.empty()) size.height = 8;
-  geometry.x += 2, geometry.width -= 5;
-  geometry.y += size.height - 1, geometry.height -= size.height + 2;
-  frame.state.layout->setGeometry(geometry);
-}
-
-void pFrame::setText(string text) {
-  gtk_frame_set_label(GTK_FRAME(gtkWidget), text);
-}
-
-void pFrame::setVisible(bool visible) {
-  if(frame.state.layout) frame.state.layout->setVisible(frame.state.layout->visible());
-  pWidget::setVisible(visible);
-}
-
-void pFrame::constructor() {
-  gtkWidget = gtk_frame_new("");
-}
-
-void pFrame::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pFrame::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/horizontal-scroller.cpp b/phoenix/gtk/widget/horizontal-scroller.cpp
deleted file mode 100644
index 5a46879f..00000000
--- a/phoenix/gtk/widget/horizontal-scroller.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace phoenix {
-
-static void HorizontalScroller_change(GtkRange* gtkRange, HorizontalScroller* self) {
-  unsigned position = (unsigned)gtk_range_get_value(gtkRange);
-  if(self->state.position == position) return;
-  self->state.position = position;
-  if(!self->p.locked && self->onChange) self->onChange();
-}
-
-Size pHorizontalScroller::minimumSize() {
-  return {0, 20};
-}
-
-void pHorizontalScroller::setLength(unsigned length) {
-  locked = true;
-  length += length == 0;
-  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
-  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
-  locked = false;
-}
-
-void pHorizontalScroller::setPosition(unsigned position) {
-  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
-}
-
-void pHorizontalScroller::constructor() {
-  gtkWidget = gtk_hscrollbar_new(0);
-  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalScroller_change), (gpointer)&horizontalScroller);
-
-  setLength(horizontalScroller.state.length);
-  setPosition(horizontalScroller.state.position);
-}
-
-void pHorizontalScroller::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pHorizontalScroller::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/horizontal-slider.cpp b/phoenix/gtk/widget/horizontal-slider.cpp
deleted file mode 100644
index 2ef80d93..00000000
--- a/phoenix/gtk/widget/horizontal-slider.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-namespace phoenix {
-
-static void HorizontalSlider_change(GtkRange* gtkRange, HorizontalSlider* self) {
-  unsigned position = (unsigned)gtk_range_get_value(gtkRange);
-  if(self->state.position == position) return;
-  self->state.position = position;
-  if(self->onChange) self->onChange();
-}
-
-Size pHorizontalSlider::minimumSize() {
-  return {0, 20};
-}
-
-void pHorizontalSlider::setLength(unsigned length) {
-  length += length == 0;
-  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
-  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
-}
-
-void pHorizontalSlider::setPosition(unsigned position) {
-  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
-}
-
-void pHorizontalSlider::constructor() {
-  gtkWidget = gtk_hscale_new_with_range(0, 100, 1);
-  gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
-  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)&horizontalSlider);
-
-  setLength(horizontalSlider.state.length);
-  setPosition(horizontalSlider.state.position);
-}
-
-void pHorizontalSlider::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pHorizontalSlider::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/label.cpp b/phoenix/gtk/widget/label.cpp
deleted file mode 100644
index c484587b..00000000
--- a/phoenix/gtk/widget/label.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace phoenix {
-
-Size pLabel::minimumSize() {
-  Size size = pFont::size(widget.state.font, label.state.text);
-  return {size.width, size.height};
-}
-
-void pLabel::setText(string text) {
-  gtk_label_set_text(GTK_LABEL(gtkWidget), text);
-}
-
-void pLabel::constructor() {
-  gtkWidget = gtk_label_new("");
-  gtk_misc_set_alignment(GTK_MISC(gtkWidget), 0.0, 0.5);
-
-  setText(label.state.text);
-}
-
-void pLabel::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pLabel::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/line-edit.cpp b/phoenix/gtk/widget/line-edit.cpp
deleted file mode 100644
index 642e6936..00000000
--- a/phoenix/gtk/widget/line-edit.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-namespace phoenix {
-
-static void LineEdit_activate(LineEdit* self) {
-  if(self->onActivate) self->onActivate();
-}
-
-static void LineEdit_change(LineEdit* self) {
-  self->state.text = self->text();
-  if(self->p.locked == false && self->onChange) self->onChange();
-}
-
-Size pLineEdit::minimumSize() {
-  Size size = pFont::size(widget.state.font, lineEdit.state.text);
-  return {size.width + 10, size.height + 10};
-}
-
-void pLineEdit::setBackgroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_base(gtkWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pLineEdit::setEditable(bool editable) {
-  gtk_editable_set_editable(GTK_EDITABLE(gtkWidget), editable);
-}
-
-void pLineEdit::setForegroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_text(gtkWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pLineEdit::setText(string text) {
-  locked = true;
-  gtk_entry_set_text(GTK_ENTRY(gtkWidget), text);
-  locked = false;
-}
-
-string pLineEdit::text() {
-  return gtk_entry_get_text(GTK_ENTRY(gtkWidget));
-}
-
-void pLineEdit::constructor() {
-  gtkWidget = gtk_entry_new();
-  g_signal_connect_swapped(G_OBJECT(gtkWidget), "activate", G_CALLBACK(LineEdit_activate), (gpointer)&lineEdit);
-  g_signal_connect_swapped(G_OBJECT(gtkWidget), "changed", G_CALLBACK(LineEdit_change), (gpointer)&lineEdit);
-
-  setEditable(lineEdit.state.editable);
-  setText(lineEdit.state.text);
-}
-
-void pLineEdit::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pLineEdit::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/list-view.cpp b/phoenix/gtk/widget/list-view.cpp
deleted file mode 100644
index 8fa77c6c..00000000
--- a/phoenix/gtk/widget/list-view.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-namespace phoenix {
-
-static void ListView_activate(GtkTreeView* treeView, GtkTreePath* path, GtkTreeViewColumn* column, ListView* self) {
-  char* pathname = gtk_tree_path_to_string(path);
-  unsigned selection = decimal(pathname);
-  g_free(pathname);
-  self->state.selection = selection;
-  if(self->onActivate) self->onActivate();
-}
-
-static void ListView_change(GtkTreeView* treeView, ListView* self) {
-  GtkTreeIter iter;
-  if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(treeView), 0, &iter)) return;  //should not be possible
-  char* path = gtk_tree_model_get_string_from_iter(gtk_tree_view_get_model(treeView), &iter);
-  unsigned selection = decimal(path);
-  g_free(path);
-
-  if(!self->state.selected || self->state.selection != selection) {
-    self->state.selected = true;
-    self->state.selection = selection;
-    if(self->onChange) self->onChange();
-  }
-}
-
-static void ListView_toggle(GtkCellRendererToggle* cell, gchar* path, ListView* self) {
-  unsigned selection = decimal(path);
-  self->setChecked(selection, !self->checked(selection));
-  if(self->onToggle) self->onToggle(selection);
-}
-
-void pListView::append(const lstring& text) {
-  GtkTreeIter iter;
-  gtk_list_store_append(store, &iter);
-  for(unsigned n = 0; n < text.size(); n++) {
-    gtk_list_store_set(store, &iter, 1 + n * 2 + 1, (const char*)text[n], -1);
-  }
-}
-
-void pListView::autoSizeColumns() {
-  gtk_tree_view_columns_autosize(GTK_TREE_VIEW(subWidget));
-}
-
-bool pListView::focused() {
-  return GTK_WIDGET_HAS_FOCUS(subWidget);
-}
-
-void pListView::remove(unsigned selection) {
-  GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
-  GtkTreeIter iter;
-  gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
-  gtk_list_store_remove(store, &iter);
-}
-
-void pListView::reset() {
-  listView.state.selected = false;
-  listView.state.selection = 0;
-  gtk_list_store_clear(GTK_LIST_STORE(store));
-  gtk_tree_view_set_model(GTK_TREE_VIEW(subWidget), GTK_TREE_MODEL(store));
-  //reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListView is now empty
-  gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(gtkWidget), 0);
-  gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(gtkWidget), 0);
-}
-
-void pListView::setBackgroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pListView::setCheckable(bool checkable) {
-  gtk_cell_renderer_set_visible(column(0).checkbutton, checkable);
-}
-
-void pListView::setChecked(unsigned selection, bool checked) {
-  GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
-  GtkTreeIter iter;
-  gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
-  gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1);
-}
-
-void pListView::setForegroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pListView::setHeaderText(const lstring& text) {
-  destructor();
-  constructor();
-}
-
-void pListView::setHeaderVisible(bool visible) {
-  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(subWidget), visible);
-}
-
-void pListView::setImage(unsigned selection, unsigned position, const image& image) {
-  GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
-  GtkTreeIter iter;
-  gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
-  if(image.empty() == false) {
-    GdkPixbuf* pixbuf = CreatePixbuf(image, true);
-    gtk_list_store_set(store, &iter, 1 + position * 2, pixbuf, -1);
-  } else {
-    gtk_list_store_set(store, &iter, 1 + position * 2, nullptr, -1);
-  }
-}
-
-void pListView::setSelected(bool selected) {
-  if(selected == false) {
-    GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
-    gtk_tree_selection_unselect_all(selection);
-  } else {
-    setSelection(listView.state.selection);
-  }
-}
-
-void pListView::setSelection(unsigned selection) {
-  GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
-  gtk_tree_selection_unselect_all(treeSelection);
-  GtkTreePath* path = gtk_tree_path_new_from_string(string{selection});
-  gtk_tree_selection_select_path(treeSelection, path);
-  gtk_tree_view_set_cursor(GTK_TREE_VIEW(subWidget), path, nullptr, false);
-  gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(subWidget), path, nullptr, true, 0.5, 0.0);
-  gtk_tree_path_free(path);
-}
-
-void pListView::setText(unsigned selection, unsigned position, string text) {
-  GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
-  GtkTreeIter iter;
-  gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
-  gtk_list_store_set(store, &iter, 1 + position * 2 + 1, (const char*)text, -1);
-}
-
-void pListView::constructor() {
-  gtkWidget = gtk_scrolled_window_new(0, 0);
-  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
-
-  lstring headerText = listView.state.headerText;
-  if(headerText.size() == 0) headerText.append("");  //ListView must have at least one column
-
-  column.reset();
-  vector<GType> gtype;
-  for(auto& text : headerText) {
-    GtkColumn cell;
-    cell.label = gtk_label_new(text);
-    cell.column = gtk_tree_view_column_new();
-    gtk_tree_view_column_set_resizable(cell.column, true);
-    gtk_tree_view_column_set_title(cell.column, "");
-
-    if(column.size() == 0) {  //first column checkbutton
-      cell.checkbutton = gtk_cell_renderer_toggle_new();
-      gtk_tree_view_column_pack_start(cell.column, cell.checkbutton, false);
-      gtk_tree_view_column_set_attributes(cell.column, cell.checkbutton, "active", gtype.size(), nullptr);
-      gtype.append(G_TYPE_BOOLEAN);
-      g_signal_connect(cell.checkbutton, "toggled", G_CALLBACK(ListView_toggle), (gpointer)&listView);
-    }
-
-    cell.icon = gtk_cell_renderer_pixbuf_new();
-    gtk_tree_view_column_pack_start(cell.column, cell.icon, false);
-    gtk_tree_view_column_set_attributes(cell.column, cell.icon, "pixbuf", gtype.size(), nullptr);
-    gtype.append(GDK_TYPE_PIXBUF);
-
-    cell.text = gtk_cell_renderer_text_new();
-    gtk_tree_view_column_pack_start(cell.column, cell.text, false);
-    gtk_tree_view_column_set_attributes(cell.column, cell.text, "text", gtype.size(), nullptr);
-    gtype.append(G_TYPE_STRING);
-
-    column.append(cell);
-  }
-
-  store = gtk_list_store_newv(gtype.size(), gtype.data());
-  subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
-  gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
-  g_object_unref(G_OBJECT(store));
-
-  for(auto& cell : column) {
-    gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(cell.column), cell.label);
-    gtk_tree_view_append_column(GTK_TREE_VIEW(subWidget), cell.column);
-    gtk_widget_show(cell.label);
-  }
-
-  gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(subWidget), headerText.size() >= 2);  //two or more columns + checkbutton column
-  gtk_tree_view_set_search_column(GTK_TREE_VIEW(subWidget), 2);
-
-  g_signal_connect(G_OBJECT(subWidget), "cursor-changed", G_CALLBACK(ListView_change), (gpointer)&listView);
-  g_signal_connect(G_OBJECT(subWidget), "row-activated", G_CALLBACK(ListView_activate), (gpointer)&listView);
-
-  gtk_widget_show(subWidget);
-
-  setHeaderVisible(listView.state.headerVisible);
-  setCheckable(listView.state.checkable);
-  for(auto& text : listView.state.text) append(text);
-  for(unsigned n = 0; n < listView.state.checked.size(); n++) setChecked(n, listView.state.checked[n]);
-  if(listView.state.selected) setSelection(listView.state.selection);
-  autoSizeColumns();
-}
-
-void pListView::destructor() {
-  gtk_widget_destroy(subWidget);
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pListView::orphan() {
-  destructor();
-  constructor();
-}
-
-void pListView::setFocused() {
-  gtk_widget_grab_focus(subWidget);
-}
-
-void pListView::setFont(string font) {
-  pFont::setFont(gtkWidget, font);
-  for(auto& cell : column) pFont::setFont(cell.label, font);
-}
-
-}
diff --git a/phoenix/gtk/widget/progress-bar.cpp b/phoenix/gtk/widget/progress-bar.cpp
deleted file mode 100644
index 7f4545a2..00000000
--- a/phoenix/gtk/widget/progress-bar.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace phoenix {
-
-Size pProgressBar::minimumSize() {
-  return {0, 25};
-}
-
-void pProgressBar::setPosition(unsigned position) {
-  position = position <= 100 ? position : 0;
-  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkWidget), (double)position / 100.0);
-}
-
-void pProgressBar::constructor() {
-  gtkWidget = gtk_progress_bar_new();
-
-  setPosition(progressBar.state.position);
-}
-
-void pProgressBar::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pProgressBar::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/radio-button.cpp b/phoenix/gtk/widget/radio-button.cpp
deleted file mode 100644
index ca10fc36..00000000
--- a/phoenix/gtk/widget/radio-button.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-namespace phoenix {
-
-static void RadioButton_activate(GtkToggleButton* toggleButton, RadioButton* self) {
-  self->p.onActivate();
-}
-
-Size pRadioButton::minimumSize() {
-  Size size = pFont::size(widget.state.font, radioButton.state.text);
-
-  if(radioButton.state.orientation == Orientation::Horizontal) {
-    size.width += radioButton.state.image.width;
-    size.height = max(radioButton.state.image.height, size.height);
-  }
-
-  if(radioButton.state.orientation == Orientation::Vertical) {
-    size.width = max(radioButton.state.image.width, size.width);
-    size.height += radioButton.state.image.height;
-  }
-
-  return {size.width + 24, size.height + 12};
-}
-
-void pRadioButton::setChecked() {
-  parent().locked = true;
-  for(auto& item : radioButton.state.group) {
-    bool checked = &item == &radioButton;
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item.p.gtkWidget), item.state.checked = checked);
-  }
-  parent().locked = false;
-}
-
-void pRadioButton::setGroup(const group<RadioButton>& group) {
-  parent().locked = true;
-  for(auto& item : radioButton.state.group) {
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item.p.gtkWidget), item.state.checked);
-  }
-  parent().locked = false;
-}
-
-void pRadioButton::setImage(const image& image, Orientation orientation) {
-  if(image.empty() == false) {
-    GtkImage* gtkImage = CreateImage(image);
-    gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage);
-  } else {
-    gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr);
-  }
-  switch(orientation) {
-  case Orientation::Horizontal: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_LEFT); break;
-  case Orientation::Vertical:   gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_TOP);  break;
-  }
-}
-
-void pRadioButton::setText(string text) {
-  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
-  setFont(widget.state.font);
-}
-
-void pRadioButton::constructor() {
-  gtkWidget = gtk_toggle_button_new();
-  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioButton_activate), (gpointer)&radioButton);
-
-  setGroup(radioButton.state.group);
-  setText(radioButton.state.text);
-}
-
-void pRadioButton::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pRadioButton::orphan() {
-  destructor();
-  constructor();
-}
-
-void pRadioButton::onActivate() {
-  if(parent().locked) return;
-  bool wasChecked = radioButton.state.checked;
-  setChecked();
-  if(!wasChecked) {
-    if(radioButton.onActivate) radioButton.onActivate();
-  }
-}
-
-pRadioButton& pRadioButton::parent() {
-  if(radioButton.state.group.size()) return radioButton.state.group.first().p;
-  return *this;
-}
-
-}
diff --git a/phoenix/gtk/widget/radio-label.cpp b/phoenix/gtk/widget/radio-label.cpp
deleted file mode 100644
index 5e1b224d..00000000
--- a/phoenix/gtk/widget/radio-label.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-namespace phoenix {
-
-static void RadioLabel_activate(GtkToggleButton* toggleButton, RadioLabel* self) {
-  self->p.onActivate();
-}
-
-Size pRadioLabel::minimumSize() {
-  Size size = pFont::size(widget.state.font, radioLabel.state.text);
-  return {size.width + 28, size.height + 4};
-}
-
-void pRadioLabel::setChecked() {
-  parent().locked = true;
-  for(auto& item : radioLabel.state.group) item.state.checked = false;
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), radioLabel.state.checked = true);
-  parent().locked = false;
-}
-
-void pRadioLabel::setGroup(const group<RadioLabel>& group) {
-  if(&parent() == this) return;
-  parent().locked = true;
-  gtk_radio_button_set_group(
-    GTK_RADIO_BUTTON(gtkWidget),
-    gtk_radio_button_get_group(GTK_RADIO_BUTTON(parent().gtkWidget))
-  );
-  for(auto& item : radioLabel.state.group) {
-    if(item.state.checked) {
-      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item.p.gtkWidget), true);
-      break;
-    }
-  }
-  parent().locked = false;
-}
-
-void pRadioLabel::setText(string text) {
-  gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
-}
-
-void pRadioLabel::constructor() {
-  gtkWidget = gtk_radio_button_new_with_label(nullptr, "");
-  g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioLabel_activate), (gpointer)&radioLabel);
-
-  setGroup(radioLabel.state.group);
-  setText(radioLabel.state.text);
-}
-
-void pRadioLabel::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pRadioLabel::orphan() {
-  destructor();
-  constructor();
-}
-
-void pRadioLabel::onActivate() {
-  if(parent().locked) return;
-  bool wasChecked = radioLabel.state.checked;
-  setChecked();
-  if(wasChecked) return;
-  if(radioLabel.onActivate) radioLabel.onActivate();
-}
-
-pRadioLabel& pRadioLabel::parent() {
-  if(radioLabel.state.group.size()) return radioLabel.state.group.first().p;
-  return *this;
-}
-
-}
diff --git a/phoenix/gtk/widget/tab-frame.cpp b/phoenix/gtk/widget/tab-frame.cpp
deleted file mode 100644
index a9a3c562..00000000
--- a/phoenix/gtk/widget/tab-frame.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-namespace phoenix {
-
-static void TabFrame_change(GtkNotebook* notebook, GtkWidget* page, unsigned selection, TabFrame* self) {
-  self->state.selection = selection;
-  self->p.synchronizeLayout();
-  if(!self->p.locked && self->onChange) self->onChange();
-}
-
-void pTabFrame::append(string text, const image& image) {
-  unsigned selection = tabFrame.state.text.size() - 1;
-
-  Tab tab;
-  tab.child = gtk_fixed_new();
-  tab.container = gtk_hbox_new(false, 0);
-  tab.image = gtk_image_new();
-  tab.title = gtk_label_new(text);
-  tabs.append(tab);
-
-  gtk_widget_show(tab.child);
-  gtk_widget_show(tab.container);
-  gtk_widget_show(tab.image);
-  gtk_widget_show(tab.title);
-  gtk_box_pack_start(GTK_BOX(tab.container), tab.image, false, false, 0);
-  gtk_box_pack_start(GTK_BOX(tab.container), tab.title, false, false, 2);
-
-  gtk_notebook_append_page(GTK_NOTEBOOK(gtkWidget), tab.child, tab.container);
-  setFont(widget.state.font);
-  if(!image.empty()) setImage(selection, image);
-}
-
-GtkWidget* pTabFrame::container(Widget& widget) {
-  Layout* widgetLayout = GetParentWidgetLayout(&widget);
-  unsigned selection = 0;
-  for(auto& layout : tabFrame.state.layout) {
-    if(layout == widgetLayout) return tabs[selection].child;
-    selection++;
-  }
-  return nullptr;
-}
-
-Position pTabFrame::displacement() {
-  return {6, 31};
-}
-
-void pTabFrame::remove(unsigned selection) {
-  tabs.remove(selection);
-  gtk_notebook_remove_page(GTK_NOTEBOOK(gtkWidget), selection);
-}
-
-void pTabFrame::setEnabled(bool enabled) {
-  for(auto& layout : tabFrame.state.layout) {
-    if(layout) layout->setEnabled(layout->enabled());
-  }
-  pWidget::setEnabled(enabled);
-}
-
-void pTabFrame::setGeometry(Geometry geometry) {
-  pWidget::setGeometry(geometry);
-  geometry.x += 1, geometry.width -= 5;
-  geometry.y += 26, geometry.height -= 31;
-  for(auto& layout : tabFrame.state.layout) {
-    if(layout) layout->setGeometry(geometry);
-  }
-  synchronizeLayout();
-}
-
-void pTabFrame::setImage(unsigned selection, const image& image) {
-  if(image.empty() == false) {
-    nall::image copy = image;
-    unsigned size = pFont::size(widget.state.font, " ").height;
-    copy.scale(size, size);
-    GdkPixbuf* pixbuf = CreatePixbuf(copy);
-    gtk_image_set_from_pixbuf(GTK_IMAGE(tabs[selection].image), pixbuf);
-  } else {
-    gtk_image_clear(GTK_IMAGE(tabs[selection].image));
-  }
-}
-
-void pTabFrame::setSelection(unsigned selection) {
-  locked = true;
-  gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkWidget), selection);
-  locked = false;
-}
-
-void pTabFrame::setText(unsigned selection, string text) {
-  gtk_label_set_text(GTK_LABEL(tabs[selection].title), text);
-}
-
-void pTabFrame::setVisible(bool visible) {
-  for(auto& layout : tabFrame.state.layout) {
-    if(layout) layout->setVisible(layout->visible());
-  }
-  pWidget::setVisible(visible);
-}
-
-void pTabFrame::constructor() {
-  gtkWidget = gtk_notebook_new();
-  gtk_notebook_set_show_border(GTK_NOTEBOOK(gtkWidget), false);
-  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), GTK_POS_TOP);
-  g_signal_connect(G_OBJECT(gtkWidget), "switch-page", G_CALLBACK(TabFrame_change), (gpointer)&tabFrame);
-
-  setSelection(tabFrame.state.selection);
-}
-
-void pTabFrame::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pTabFrame::orphan() {
-  destructor();
-  constructor();
-}
-
-void pTabFrame::setFont(string font) {
-  for(auto& tab : tabs) pFont::setFont(tab.title, font);
-}
-
-void pTabFrame::synchronizeLayout() {
-  unsigned selection = 0;
-  for(auto& layout : tabFrame.state.layout) {
-    if(layout) layout->setVisible(selection == tabFrame.state.selection);
-    selection++;
-  }
-}
-
-}
diff --git a/phoenix/gtk/widget/text-edit.cpp b/phoenix/gtk/widget/text-edit.cpp
deleted file mode 100644
index 49ee4df7..00000000
--- a/phoenix/gtk/widget/text-edit.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-namespace phoenix {
-
-static void TextEdit_change(TextEdit* self) {
-  self->state.text = self->text();
-  if(self->p.locked == false && self->onChange) self->onChange();
-}
-
-bool pTextEdit::focused() {
-  return GTK_WIDGET_HAS_FOCUS(subWidget);
-}
-
-void pTextEdit::setBackgroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pTextEdit::setCursorPosition(unsigned position) {
-  GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
-  GtkTextIter iter;
-  gtk_text_buffer_get_end_iter(textBuffer, &iter);
-  gtk_text_iter_set_offset(&iter, min(position, gtk_text_iter_get_offset(&iter)));
-  gtk_text_buffer_place_cursor(textBuffer, &iter);
-  gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark);
-}
-
-void pTextEdit::setEditable(bool editable) {
-  gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), editable);
-}
-
-void pTextEdit::setForegroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pTextEdit::setText(string text) {
-  locked = true;
-  gtk_text_buffer_set_text(textBuffer, text, -1);
-  locked = false;
-}
-
-void pTextEdit::setWordWrap(bool wordWrap) {
-  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
-  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget),
-    wordWrap ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS,
-    GTK_POLICY_ALWAYS);
-}
-
-string pTextEdit::text() {
-  GtkTextIter start, end;
-  gtk_text_buffer_get_start_iter(textBuffer, &start);
-  gtk_text_buffer_get_end_iter(textBuffer, &end);
-  char* temp = gtk_text_buffer_get_text(textBuffer, &start, &end, true);
-  string text = temp;
-  g_free(temp);
-  return text;
-}
-
-void pTextEdit::constructor() {
-  gtkWidget = gtk_scrolled_window_new(0, 0);
-  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
-
-  subWidget = gtk_text_view_new();
-  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_WORD_CHAR);
-  gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
-
-  textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
-
-  g_signal_connect_swapped(G_OBJECT(textBuffer), "changed", G_CALLBACK(TextEdit_change), (gpointer)&textEdit);
-
-  gtk_widget_show(subWidget);
-
-  setEditable(textEdit.state.editable);
-  setText(textEdit.state.text);
-  setWordWrap(textEdit.state.wordWrap);
-}
-
-void pTextEdit::destructor() {
-  gtk_widget_destroy(subWidget);
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pTextEdit::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/vertical-scroller.cpp b/phoenix/gtk/widget/vertical-scroller.cpp
deleted file mode 100644
index 68928ce3..00000000
--- a/phoenix/gtk/widget/vertical-scroller.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace phoenix {
-
-static void VerticalScroller_change(GtkRange* gtkRange, VerticalScroller* self) {
-  unsigned position = (unsigned)gtk_range_get_value(gtkRange);
-  if(self->state.position == position) return;
-  self->state.position = position;
-  if(!self->p.locked && self->onChange) self->onChange();
-}
-
-Size pVerticalScroller::minimumSize() {
-  return {20, 0};
-}
-
-void pVerticalScroller::setLength(unsigned length) {
-  locked = true;
-  length += length == 0;
-  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
-  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
-  locked = false;
-}
-
-void pVerticalScroller::setPosition(unsigned position) {
-  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
-}
-
-void pVerticalScroller::constructor() {
-  gtkWidget = gtk_vscrollbar_new(0);
-  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalScroller_change), (gpointer)&verticalScroller);
-
-  setLength(verticalScroller.state.length);
-  setPosition(verticalScroller.state.position);
-}
-
-void pVerticalScroller::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pVerticalScroller::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/vertical-slider.cpp b/phoenix/gtk/widget/vertical-slider.cpp
deleted file mode 100644
index 102b5c44..00000000
--- a/phoenix/gtk/widget/vertical-slider.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-namespace phoenix {
-
-static void VerticalSlider_change(GtkRange* gtkRange, VerticalSlider* self) {
-  unsigned position = (unsigned)gtk_range_get_value(gtkRange);
-  if(self->state.position == position) return;
-  self->state.position = position;
-  if(self->onChange) self->onChange();
-}
-
-Size pVerticalSlider::minimumSize() {
-  return {20, 0};
-}
-
-void pVerticalSlider::setLength(unsigned length) {
-  length += length == 0;
-  gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
-  gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
-}
-
-void pVerticalSlider::setPosition(unsigned position) {
-  gtk_range_set_value(GTK_RANGE(gtkWidget), position);
-}
-
-void pVerticalSlider::constructor() {
-  gtkWidget = gtk_vscale_new_with_range(0, 100, 1);
-  gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
-  g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)&verticalSlider);
-
-  setLength(verticalSlider.state.length);
-  setPosition(verticalSlider.state.position);
-}
-
-void pVerticalSlider::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pVerticalSlider::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/viewport.cpp b/phoenix/gtk/widget/viewport.cpp
deleted file mode 100644
index bc2474e6..00000000
--- a/phoenix/gtk/widget/viewport.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-namespace phoenix {
-
-static void Viewport_dropEvent(GtkWidget* widget, GdkDragContext* context, gint x, gint y,
-GtkSelectionData* data, guint type, guint timestamp, Viewport* viewport) {
-  if(viewport->state.droppable == false) return;
-  lstring paths = DropPaths(data);
-  if(paths.empty()) return;
-  if(viewport->onDrop) viewport->onDrop(paths);
-}
-
-static gboolean Viewport_mouseLeave(GtkWidget* widget, GdkEventButton* event, pViewport* self) {
-  if(self->viewport.onMouseLeave) self->viewport.onMouseLeave();
-  return true;
-}
-
-static gboolean Viewport_mouseMove(GtkWidget* widget, GdkEventButton* event, pViewport* self) {
-  if(self->viewport.onMouseMove) self->viewport.onMouseMove({(signed)event->x, (signed)event->y});
-  return true;
-}
-
-static gboolean Viewport_mousePress(GtkWidget* widget, GdkEventButton* event, pViewport* self) {
-  if(self->viewport.onMousePress) switch(event->button) {
-  case 1: self->viewport.onMousePress(Mouse::Button::Left); break;
-  case 2: self->viewport.onMousePress(Mouse::Button::Middle); break;
-  case 3: self->viewport.onMousePress(Mouse::Button::Right); break;
-  }
-  return true;
-}
-
-static gboolean Viewport_mouseRelease(GtkWidget* widget, GdkEventButton* event, pViewport* self) {
-  if(self->viewport.onMouseRelease) switch(event->button) {
-  case 1: self->viewport.onMouseRelease(Mouse::Button::Left); break;
-  case 2: self->viewport.onMouseRelease(Mouse::Button::Middle); break;
-  case 3: self->viewport.onMouseRelease(Mouse::Button::Right); break;
-  }
-  return true;
-}
-
-uintptr_t pViewport::handle() {
-  return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget));
-}
-
-void pViewport::setDroppable(bool droppable) {
-  gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
-  if(droppable) gtk_drag_dest_add_uri_targets(gtkWidget);
-}
-
-void pViewport::constructor() {
-  gtkWidget = gtk_drawing_area_new();
-//gtk_widget_set_double_buffered(gtkWidget, false);
-  gtk_widget_add_events(gtkWidget,
-    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
-  g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Viewport_dropEvent), (gpointer)&viewport);
-  g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Viewport_mousePress), (gpointer)this);
-  g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this);
-  g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this);
-  g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Viewport_mouseMove), (gpointer)this);
-
-  GdkColor color;
-  color.pixel = 0;
-  color.red = 0;
-  color.green = 0;
-  color.blue = 0;
-  gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, &color);
-}
-
-void pViewport::destructor() {
-  gtk_widget_destroy(gtkWidget);
-}
-
-void pViewport::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/widget/widget.cpp b/phoenix/gtk/widget/widget.cpp
deleted file mode 100644
index 1b834d32..00000000
--- a/phoenix/gtk/widget/widget.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-namespace phoenix {
-
-GtkWidget* pWidget::container(Widget& widget) {
-  return nullptr;
-}
-
-bool pWidget::focused() {
-  return GTK_WIDGET_HAS_FOCUS(gtkWidget);
-}
-
-Size pWidget::minimumSize() {
-  return {0, 0};
-}
-
-void pWidget::setEnabled(bool enabled) {
-  if(!widget.parent()) enabled = false;
-  if(widget.state.abstract) enabled = false;
-  if(!widget.enabledToAll()) enabled = false;
-  gtk_widget_set_sensitive(gtkWidget, enabled);
-}
-
-void pWidget::setFocused() {
-  gtk_widget_grab_focus(gtkWidget);
-}
-
-void pWidget::setFont(string font) {
-  pFont::setFont(gtkWidget, font);
-}
-
-void pWidget::setGeometry(Geometry geometry) {
-  Position displacement = GetDisplacement(&widget);
-  geometry.x -= displacement.x;
-  geometry.y -= displacement.y;
-
-  if(gtkParent) gtk_fixed_move(GTK_FIXED(gtkParent), gtkWidget, geometry.x, geometry.y);
-  unsigned width = (signed)geometry.width <= 0 ? 1u : geometry.width;
-  unsigned height = (signed)geometry.height <= 0 ? 1u : geometry.height;
-  gtk_widget_set_size_request(gtkWidget, width, height);
-  if(widget.onSize) widget.onSize();
-}
-
-void pWidget::setVisible(bool visible) {
-  if(!widget.parent()) visible = false;
-  if(widget.state.abstract) visible = false;
-  if(!widget.visibleToAll()) visible = false;
-  gtk_widget_set_visible(gtkWidget, visible);
-}
-
-void pWidget::constructor() {
-  if(widget.state.abstract) gtkWidget = gtk_fixed_new();
-}
-
-void pWidget::destructor() {
-  if(widget.state.abstract) gtk_widget_destroy(gtkWidget);
-}
-
-void pWidget::orphan() {
-  destructor();
-  constructor();
-}
-
-}
diff --git a/phoenix/gtk/window.cpp b/phoenix/gtk/window.cpp
deleted file mode 100644
index 573972be..00000000
--- a/phoenix/gtk/window.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-namespace phoenix {
-
-static gint Window_close(GtkWidget* widget, GdkEvent* event, Window* window) {
-  if(window->onClose) window->onClose();
-  else window->setVisible(false);
-  if(window->state.modal && !window->visible()) window->setModal(false);
-  return true;
-}
-
-static gboolean Window_expose(GtkWidget* widget, GdkEvent* event, Window* window) {
-  if(window->state.backgroundColorOverride == false) return false;
-  cairo_t* context = gdk_cairo_create(widget->window);
-
-  Color color = window->backgroundColor();
-  double red   = (double)color.red   / 255.0;
-  double green = (double)color.green / 255.0;
-  double blue  = (double)color.blue  / 255.0;
-  double alpha = (double)color.alpha / 255.0;
-
-  if(gdk_screen_is_composited(gdk_screen_get_default())
-  && gdk_screen_get_rgba_colormap(gdk_screen_get_default())
-  ) {
-    cairo_set_source_rgba(context, red, green, blue, alpha);
-  } else {
-    cairo_set_source_rgb(context, red, green, blue);
-  }
-
-  cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
-  cairo_paint(context);
-  cairo_destroy(context);
-
-  return false;
-}
-
-static gboolean Window_configure(GtkWidget* widget, GdkEvent* event, Window* window) {
-  if(gtk_widget_get_realized(window->p.widget) == false) return false;
-  if(window->visible() == false) return false;
-  GdkWindow *gdkWindow = gtk_widget_get_window(widget);
-
-  GdkRectangle border, client;
-  gdk_window_get_frame_extents(gdkWindow, &border);
-  gdk_window_get_geometry(gdkWindow, nullptr, nullptr, &client.width, &client.height, nullptr);
-  gdk_window_get_origin(gdkWindow, &client.x, &client.y);
-
-  if(window->state.fullScreen == false) {
-    //update geometry settings
-    settings->geometry.frameX = client.x - border.x;
-    settings->geometry.frameY = client.y - border.y;
-    settings->geometry.frameWidth = border.width - client.width;
-    settings->geometry.frameHeight = border.height - client.height;
-    if(window->state.backgroundColorOverride == false) {
-      GdkColor color = widget->style->bg[GTK_STATE_NORMAL];
-      settings->window.backgroundColor
-      = ((uint8_t)(color.red   >> 8) << 16)
-      + ((uint8_t)(color.green >> 8) <<  8)
-      + ((uint8_t)(color.blue  >> 8) <<  0);
-    }
-    settings->save();
-  }
-
-  Geometry geometry = {
-    client.x,
-    client.y + window->p.menuHeight(),
-    client.width,
-    client.height - window->p.menuHeight() - window->p.statusHeight()
-  };
-
-  //move
-  if(geometry.x != window->state.geometry.x || geometry.y != window->state.geometry.y) {
-    if(window->state.fullScreen == false) {
-      window->state.geometry.x = geometry.x;
-      window->state.geometry.y = geometry.y;
-    }
-    if(window->p.locked == false && window->onMove) window->onMove();
-  }
-
-  //size
-  if(geometry.width != window->state.geometry.width || geometry.height != window->state.geometry.height) {
-    window->p.onSizePending = true;
-  }
-
-  return false;
-}
-
-static void Window_drop(GtkWidget* widget, GdkDragContext* context, gint x, gint y,
-GtkSelectionData* data, guint type, guint timestamp, Window* window) {
-  if(window->state.droppable == false) return;
-  lstring paths = DropPaths(data);
-  if(paths.empty()) return;
-  if(window->onDrop) window->onDrop(paths);
-}
-
-static gboolean Window_keyPress(GtkWidget* widget, GdkEventKey* event, Window* window) {
-  Keyboard::Keycode key = Keysym(event->keyval);
-  if(key != Keyboard::Keycode::None && window->onKeyPress) window->onKeyPress(key);
-  return false;
-}
-
-static gboolean Window_keyRelease(GtkWidget* widget, GdkEventKey* event, Window* window) {
-  Keyboard::Keycode key = Keysym(event->keyval);
-  if(key != Keyboard::Keycode::None && window->onKeyRelease) window->onKeyRelease(key);
-  return false;
-}
-
-static void Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, Window* window) {
-  //size-allocate sent from gtk_fixed_move(); detect if layout unchanged and return
-  if(allocation->width  == window->p.lastAllocation.width
-  && allocation->height == window->p.lastAllocation.height) return;
-
-  window->state.geometry.width  = allocation->width;
-  window->state.geometry.height = allocation->height;
-
-  for(auto& layout : window->state.layout) {
-    Geometry geometry = window->geometry();
-    geometry.x = geometry.y = 0;
-    layout.setGeometry(geometry);
-  }
-
-  if(window->p.onSizePending && window->p.locked == false && window->onSize) {
-    window->p.onSizePending = false;
-    window->onSize();
-  }
-
-  window->p.lastAllocation = *allocation;
-}
-
-static void Window_sizeRequest(GtkWidget* widget, GtkRequisition* requisition, Window* window) {
-  requisition->width  = window->state.geometry.width;
-  requisition->height = window->state.geometry.height;
-}
-
-Window& pWindow::none() {
-  static Window* window = nullptr;
-  if(window == nullptr) window = new Window;
-  return *window;
-}
-
-void pWindow::append(Layout& layout) {
-  Geometry geometry = this->geometry();
-  geometry.x = geometry.y = 0;
-  layout.setGeometry(geometry);
-}
-
-void pWindow::append(Menu& menu) {
-  if(window.state.menuFont) menu.p.setFont(window.state.menuFont);
-  else menu.p.setFont(Font::sans(8));
-  gtk_menu_shell_append(GTK_MENU_SHELL(this->menu), menu.p.widget);
-  gtk_widget_show(menu.p.widget);
-}
-
-void pWindow::append(Widget& widget) {
-  if(widget.font().empty() && !window.state.widgetFont.empty()) {
-    widget.setFont(window.state.widgetFont);
-  }
-
-  if(GetParentWidget(&widget)) {
-    widget.p.gtkParent = GetParentWidget(&widget)->p.container(widget);
-  } else {
-    widget.p.gtkParent = formContainer;
-  }
-
-  gtk_fixed_put(GTK_FIXED(widget.p.gtkParent), widget.p.gtkWidget, 0, 0);
-  if(widget.state.font) widget.p.setFont(widget.state.font);
-  else if(window.state.widgetFont) widget.p.setFont(window.state.widgetFont);
-  else widget.p.setFont(Font::sans(8));
-  widget.setVisible(widget.visible());
-}
-
-Geometry pWindow::frameMargin() {
-  if(window.state.fullScreen) return {
-    0,
-    menuHeight(),
-    0,
-    menuHeight() + statusHeight()
-  };
-
-  return {
-    settings->geometry.frameX,
-    settings->geometry.frameY + menuHeight(),
-    settings->geometry.frameWidth,
-    settings->geometry.frameHeight + menuHeight() + statusHeight()
-  };
-}
-
-bool pWindow::focused() {
-  return gtk_window_is_active(GTK_WINDOW(widget));
-}
-
-Geometry pWindow::geometry() {
-  if(window.state.fullScreen) {
-    int x, y, width, height;
-    gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
-    gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
-    return {x, y + menuHeight(), width, height - menuHeight() - statusHeight()};
-  }
-  return window.state.geometry;
-}
-
-void pWindow::remove(Layout& layout) {
-}
-
-void pWindow::remove(Menu& menu) {
-  menu.p.orphan();
-}
-
-void pWindow::remove(Widget& widget) {
-  widget.p.orphan();
-}
-
-void pWindow::setBackgroundColor(Color color) {
-  GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
-  gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gdkColor);
-}
-
-void pWindow::setDroppable(bool droppable) {
-  gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
-  if(droppable) gtk_drag_dest_add_uri_targets(widget);
-}
-
-void pWindow::setFocused() {
-  gtk_window_present(GTK_WINDOW(widget));
-}
-
-void pWindow::setFullScreen(bool fullScreen) {
-  if(fullScreen == false) {
-    gtk_window_unfullscreen(GTK_WINDOW(widget));
-  } else {
-    gtk_window_fullscreen(GTK_WINDOW(widget));
-  /*unsigned monitor = gdk_screen_get_monitor_at_window(gdk_screen_get_default(), gtk_widget_get_window(widget));
-    GdkRectangle rectangle = {0};
-    gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor, &rectangle);
-    gtk_window_set_decorated(GTK_WINDOW(widget), false);
-    gtk_window_move(GTK_WINDOW(widget), rectangle.x, rectangle.y);
-    gtk_window_resize(GTK_WINDOW(widget), rectangle.width, rectangle.height);
-    gtk_widget_set_size_request(formContainer, rectangle.width, rectangle.height);*/
-  }
-}
-
-void pWindow::setGeometry(Geometry geometry) {
-  Geometry margin = frameMargin();
-  gtk_window_move(GTK_WINDOW(widget), geometry.x - margin.x, geometry.y - margin.y);
-
-  GdkGeometry geom;
-  geom.min_width  = window.state.resizable ? 1 : window.state.geometry.width;
-  geom.min_height = window.state.resizable ? 1 : window.state.geometry.height;
-  gtk_window_set_geometry_hints(GTK_WINDOW(widget), GTK_WIDGET(widget), &geom, GDK_HINT_MIN_SIZE);
-
-//gtk_window_set_policy(GTK_WINDOW(widget), true, true, false);
-  gtk_widget_set_size_request(formContainer, geometry.width, geometry.height);
-  gtk_window_resize(GTK_WINDOW(widget), geometry.width, geometry.height + menuHeight() + statusHeight());
-
-  for(auto& layout : window.state.layout) {
-    Geometry layoutGeometry = geometry;
-    layoutGeometry.x = layoutGeometry.y = 0;
-    layout.setGeometry(layoutGeometry);
-  }
-}
-
-void pWindow::setMenuFont(string font) {
-  for(auto& item : window.state.menu) item.p.setFont(font);
-}
-
-void pWindow::setMenuVisible(bool visible) {
-  gtk_widget_set_visible(menu, visible);
-}
-
-void pWindow::setModal(bool modal) {
-  if(modal == true) {
-    gtk_window_set_modal(GTK_WINDOW(widget), true);
-    while(window.state.modal) {
-      Application::processEvents();
-      usleep(20 * 1000);
-    }
-    gtk_window_set_modal(GTK_WINDOW(widget), false);
-  }
-}
-
-void pWindow::setResizable(bool resizable) {
-  gtk_window_set_resizable(GTK_WINDOW(widget), resizable);
-  gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), resizable);
-}
-
-void pWindow::setStatusFont(string font) {
-  pFont::setFont(status, font);
-}
-
-void pWindow::setStatusText(string text) {
-  gtk_statusbar_pop(GTK_STATUSBAR(status), 1);
-  gtk_statusbar_push(GTK_STATUSBAR(status), 1, text);
-}
-
-void pWindow::setStatusVisible(bool visible) {
-  gtk_widget_set_visible(status, visible);
-}
-
-void pWindow::setTitle(string text) {
-  gtk_window_set_title(GTK_WINDOW(widget), text);
-}
-
-void pWindow::setVisible(bool visible) {
-  gtk_widget_set_visible(widget, visible);
-  if(visible) {
-    if(gtk_widget_get_visible(menu)) {
-      GtkAllocation allocation;
-      gtk_widget_get_allocation(menu, &allocation);
-      settings->geometry.menuHeight = allocation.height;
-    }
-
-    if(gtk_widget_get_visible(status)) {
-      GtkAllocation allocation;
-      gtk_widget_get_allocation(status, &allocation);
-      settings->geometry.statusHeight = allocation.height;
-    }
-  }
-}
-
-void pWindow::setWidgetFont(string font) {
-}
-
-void pWindow::constructor() {
-  lastAllocation.width  = 0;
-  lastAllocation.height = 0;
-  onSizePending = false;
-
-  widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-
-  //if program was given a name, try and set the window taskbar icon from one of the pixmaps folders
-  if(applicationState.name.empty() == false) {
-    string filename = {"/usr/share/pixmaps/", applicationState.name, ".png"};
-    if(!file::exists(filename)) filename = {"/usr/local/share/pixmaps/", applicationState.name, ".png"};
-    if(file::exists(filename)) {
-      //maximum image size supported by GTK+ is 256x256; so we must scale larger images ourselves
-      nall::image icon(filename);
-      icon.scale(min(256u, icon.width), min(256u, icon.height), true);
-      GdkPixbuf* pixbuf = CreatePixbuf(icon);
-      gtk_window_set_icon(GTK_WINDOW(widget), pixbuf);
-      g_object_unref(G_OBJECT(pixbuf));
-    }
-  }
-
-  GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
-  if(!colormap) colormap = gdk_screen_get_rgb_colormap(gdk_screen_get_default());
-  if(colormap) gtk_widget_set_colormap(widget, colormap);
-
-  gtk_window_set_resizable(GTK_WINDOW(widget), true);
-  #if GTK_MAJOR_VERSION >= 3
-  gtk_window_set_has_resize_grip(GTK_WINDOW(widget), false);
-  #endif
-
-  gtk_widget_set_app_paintable(widget, true);
-  gtk_widget_add_events(widget, GDK_CONFIGURE);
-
-  menuContainer = gtk_vbox_new(false, 0);
-  gtk_container_add(GTK_CONTAINER(widget), menuContainer);
-  gtk_widget_show(menuContainer);
-
-  menu = gtk_menu_bar_new();
-  gtk_box_pack_start(GTK_BOX(menuContainer), menu, false, false, 0);
-
-  formContainer = gtk_fixed_new();
-  gtk_box_pack_start(GTK_BOX(menuContainer), formContainer, true, true, 0);
-  gtk_widget_show(formContainer);
-
-  statusContainer = gtk_event_box_new();
-  status = gtk_statusbar_new();
-  gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), true);
-  gtk_container_add(GTK_CONTAINER(statusContainer), status);
-  gtk_box_pack_start(GTK_BOX(menuContainer), statusContainer, false, false, 0);
-  gtk_widget_show(statusContainer);
-
-  setTitle("");
-  setResizable(window.state.resizable);
-  setGeometry(window.state.geometry);
-  setMenuFont(Font::sans(8));
-  setStatusFont(Font::sans(8));
-
-  g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
-  g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)&window);
-  g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
-  g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_drop), (gpointer)&window);
-  g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPress), (gpointer)&window);
-  g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyPress), (gpointer)&window);
-
-  g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)&window);
-  g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)&window);
-
-  window.state.backgroundColor = Color(
-    (uint8_t)(settings->window.backgroundColor >> 16),
-    (uint8_t)(settings->window.backgroundColor >>  8),
-    (uint8_t)(settings->window.backgroundColor >>  0)
-  );
-}
-
-unsigned pWindow::menuHeight() {
-  return window.state.menuVisible ? settings->geometry.menuHeight : 0;
-}
-
-unsigned pWindow::statusHeight() {
-  return window.state.statusVisible ? settings->geometry.statusHeight : 0;
-}
-
-}
diff --git a/phoenix/phoenix.cpp b/phoenix/phoenix.cpp
deleted file mode 100644
index 63041289..00000000
--- a/phoenix/phoenix.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef PHOENIX_CPP
-#define PHOENIX_CPP
-
-#include "core/core.cpp"
-
-#endif
diff --git a/phoenix/phoenix.hpp b/phoenix/phoenix.hpp
deleted file mode 100644
index 21e85af6..00000000
--- a/phoenix/phoenix.hpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef PHOENIX_HPP
-#define PHOENIX_HPP
-
-#include "core/core.hpp"
-
-#endif
diff --git a/phoenix/qt/keyboard.cpp b/phoenix/qt/keyboard.cpp
deleted file mode 100644
index 17821076..00000000
--- a/phoenix/qt/keyboard.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-namespace phoenix {
-
-void pKeyboard::initialize() {
-  auto append = [](Keyboard::Scancode scancode, unsigned keysym) {
-    settings->keymap.insert(scancode, XKeysymToKeycode(pApplication::display, keysym));
-  };
-
-  append(Keyboard::Scancode::Escape, XK_Escape);
-  append(Keyboard::Scancode::F1, XK_F1);
-  append(Keyboard::Scancode::F2, XK_F2);
-  append(Keyboard::Scancode::F3, XK_F3);
-  append(Keyboard::Scancode::F4, XK_F4);
-  append(Keyboard::Scancode::F5, XK_F5);
-  append(Keyboard::Scancode::F6, XK_F6);
-  append(Keyboard::Scancode::F7, XK_F7);
-  append(Keyboard::Scancode::F8, XK_F8);
-  append(Keyboard::Scancode::F9, XK_F9);
-  append(Keyboard::Scancode::F10, XK_F10);
-  append(Keyboard::Scancode::F11, XK_F11);
-  append(Keyboard::Scancode::F12, XK_F12);
-
-  append(Keyboard::Scancode::PrintScreen, XK_Print);
-  append(Keyboard::Scancode::ScrollLock, XK_Scroll_Lock);
-  append(Keyboard::Scancode::Pause, XK_Pause);
-
-  append(Keyboard::Scancode::Insert, XK_Insert);
-  append(Keyboard::Scancode::Delete, XK_Delete);
-  append(Keyboard::Scancode::Home, XK_Home);
-  append(Keyboard::Scancode::End, XK_End);
-  append(Keyboard::Scancode::PageUp, XK_Prior);
-  append(Keyboard::Scancode::PageDown, XK_Next);
-
-  append(Keyboard::Scancode::Up, XK_Up);
-  append(Keyboard::Scancode::Down, XK_Down);
-  append(Keyboard::Scancode::Left, XK_Left);
-  append(Keyboard::Scancode::Right, XK_Right);
-
-  append(Keyboard::Scancode::Grave, XK_asciitilde);
-  append(Keyboard::Scancode::Number1, XK_1);
-  append(Keyboard::Scancode::Number2, XK_2);
-  append(Keyboard::Scancode::Number3, XK_3);
-  append(Keyboard::Scancode::Number4, XK_4);
-  append(Keyboard::Scancode::Number5, XK_5);
-  append(Keyboard::Scancode::Number6, XK_6);
-  append(Keyboard::Scancode::Number7, XK_7);
-  append(Keyboard::Scancode::Number8, XK_8);
-  append(Keyboard::Scancode::Number9, XK_9);
-  append(Keyboard::Scancode::Number0, XK_0);
-  append(Keyboard::Scancode::Minus, XK_minus);
-  append(Keyboard::Scancode::Equal, XK_equal);
-  append(Keyboard::Scancode::Backspace, XK_BackSpace);
-
-  append(Keyboard::Scancode::BracketLeft, XK_bracketleft);
-  append(Keyboard::Scancode::BracketRight, XK_bracketright);
-  append(Keyboard::Scancode::Backslash, XK_backslash);
-  append(Keyboard::Scancode::Semicolon, XK_semicolon);
-  append(Keyboard::Scancode::Apostrophe, XK_apostrophe);
-  append(Keyboard::Scancode::Comma, XK_comma);
-  append(Keyboard::Scancode::Period, XK_period);
-  append(Keyboard::Scancode::Slash, XK_slash);
-
-  append(Keyboard::Scancode::Tab, XK_Tab);
-  append(Keyboard::Scancode::CapsLock, XK_Caps_Lock);
-  append(Keyboard::Scancode::Return, XK_Return);
-  append(Keyboard::Scancode::ShiftLeft, XK_Shift_L);
-  append(Keyboard::Scancode::ShiftRight, XK_Shift_R);
-  append(Keyboard::Scancode::ControlLeft, XK_Control_L);
-  append(Keyboard::Scancode::ControlRight, XK_Control_R);
-  append(Keyboard::Scancode::SuperLeft, XK_Super_L);
-  append(Keyboard::Scancode::SuperRight, XK_Super_R);
-  append(Keyboard::Scancode::AltLeft, XK_Alt_L);
-  append(Keyboard::Scancode::AltRight, XK_Alt_R);
-  append(Keyboard::Scancode::Space, XK_space);
-  append(Keyboard::Scancode::Menu, XK_Menu);
-
-  append(Keyboard::Scancode::A, XK_A);
-  append(Keyboard::Scancode::B, XK_B);
-  append(Keyboard::Scancode::C, XK_C);
-  append(Keyboard::Scancode::D, XK_D);
-  append(Keyboard::Scancode::E, XK_E);
-  append(Keyboard::Scancode::F, XK_F);
-  append(Keyboard::Scancode::G, XK_G);
-  append(Keyboard::Scancode::H, XK_H);
-  append(Keyboard::Scancode::I, XK_I);
-  append(Keyboard::Scancode::J, XK_J);
-  append(Keyboard::Scancode::K, XK_K);
-  append(Keyboard::Scancode::L, XK_L);
-  append(Keyboard::Scancode::M, XK_M);
-  append(Keyboard::Scancode::N, XK_N);
-  append(Keyboard::Scancode::O, XK_O);
-  append(Keyboard::Scancode::P, XK_P);
-  append(Keyboard::Scancode::Q, XK_Q);
-  append(Keyboard::Scancode::R, XK_R);
-  append(Keyboard::Scancode::S, XK_S);
-  append(Keyboard::Scancode::T, XK_T);
-  append(Keyboard::Scancode::U, XK_U);
-  append(Keyboard::Scancode::V, XK_V);
-  append(Keyboard::Scancode::W, XK_W);
-  append(Keyboard::Scancode::X, XK_X);
-  append(Keyboard::Scancode::Y, XK_Y);
-  append(Keyboard::Scancode::Z, XK_Z);
-
-  append(Keyboard::Scancode::NumLock, XK_Num_Lock);
-  append(Keyboard::Scancode::Divide, XK_KP_Divide);
-  append(Keyboard::Scancode::Multiply, XK_KP_Multiply);
-  append(Keyboard::Scancode::Subtract, XK_KP_Subtract);
-  append(Keyboard::Scancode::Add, XK_KP_Add);
-  append(Keyboard::Scancode::Enter, XK_KP_Enter);
-  append(Keyboard::Scancode::Point, XK_KP_Decimal);
-
-  append(Keyboard::Scancode::Keypad1, XK_KP_1);
-  append(Keyboard::Scancode::Keypad2, XK_KP_2);
-  append(Keyboard::Scancode::Keypad3, XK_KP_3);
-  append(Keyboard::Scancode::Keypad4, XK_KP_4);
-  append(Keyboard::Scancode::Keypad5, XK_KP_5);
-  append(Keyboard::Scancode::Keypad6, XK_KP_6);
-  append(Keyboard::Scancode::Keypad7, XK_KP_7);
-  append(Keyboard::Scancode::Keypad8, XK_KP_8);
-  append(Keyboard::Scancode::Keypad9, XK_KP_9);
-  append(Keyboard::Scancode::Keypad0, XK_KP_0);
-}
-
-bool pKeyboard::pressed(Keyboard::Scancode scancode) {
-  char state[256];
-  XQueryKeymap(pApplication::display, state);
-  if(auto result = settings->keymap.find(scancode)) {
-    unsigned id = result();
-    return state[id >> 3] & (1 << (id & 7));
-  }
-  return false;
-}
-
-vector<bool> pKeyboard::state() {
-  vector<bool> output;
-  output.resize((unsigned)Keyboard::Scancode::Limit);
-  for(auto& n : output) n = false;
-
-  char state[256];
-  XQueryKeymap(pApplication::display, state);
-  for(auto node : settings->keymap) {
-    if(state[node.value >> 3] & (1 << (node.value & 7))) {
-      output[(unsigned)node.key] = true;
-    }
-  }
-
-  return output;
-}
-
-}
diff --git a/phoenix/qt/widget/list-view.cpp b/phoenix/qt/widget/list-view.cpp
deleted file mode 100644
index 7d5fd5dc..00000000
--- a/phoenix/qt/widget/list-view.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-namespace phoenix {
-
-void pListView::append(const lstring& text) {
-  locked = true;
-  auto items = qtListView->findItems("", Qt::MatchContains);
-  QTreeWidgetItem* item = new QTreeWidgetItem(qtListView);
-
-  item->setData(0, Qt::UserRole, (unsigned)items.size());
-  if(listView.state.checkable) item->setCheckState(0, Qt::Unchecked);
-  for(unsigned position = 0; position < text.size(); position++) {
-    item->setText(position, QString::fromUtf8(text[position]));
-  }
-  locked = false;
-}
-
-void pListView::autoSizeColumns() {
-  for(unsigned n = 0; n < listView.state.headerText.size(); n++) qtListView->resizeColumnToContents(n);
-}
-
-void pListView::remove(unsigned selection) {
-  locked = true;
-  QTreeWidgetItem* item = qtListView->topLevelItem(selection);
-  if(item == nullptr) return;
-  delete item;
-  locked = false;
-}
-
-void pListView::reset() {
-  qtListView->clear();
-}
-
-void pListView::setBackgroundColor(Color color) {
-}
-
-void pListView::setCheckable(bool checkable) {
-  if(checkable) {
-    auto items = qtListView->findItems("", Qt::MatchContains);
-    for(unsigned n = 0; n < items.size(); n++) items[n]->setCheckState(0, Qt::Unchecked);
-  }
-}
-
-void pListView::setChecked(unsigned selection, bool checked) {
-  locked = true;
-  QTreeWidgetItem* item = qtListView->topLevelItem(selection);
-  if(item) item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked);
-  locked = false;
-}
-
-void pListView::setForegroundColor(Color color) {
-}
-
-void pListView::setHeaderText(const lstring& text) {
-  QStringList labels;
-  for(auto& column : text) labels << QString::fromUtf8(column);
-
-  qtListView->setColumnCount(text.size());
-  qtListView->setAlternatingRowColors(text.size() >= 2);
-  qtListView->setHeaderLabels(labels);
-  autoSizeColumns();
-}
-
-void pListView::setHeaderVisible(bool visible) {
-  qtListView->setHeaderHidden(!visible);
-  autoSizeColumns();
-}
-
-void pListView::setImage(unsigned selection, unsigned position, const nall::image& image) {
-  QTreeWidgetItem* item = qtListView->topLevelItem(selection);
-  if(item) {
-    if(image.empty() == 0) item->setIcon(position, CreateIcon(image));
-    if(image.empty() == 1) item->setIcon(position, QIcon());
-  }
-}
-
-void pListView::setSelected(bool selected) {
-  QTreeWidgetItem* item = qtListView->currentItem();
-  if(item) item->setSelected(selected);
-}
-
-void pListView::setSelection(unsigned selection) {
-  locked = true;
-  QTreeWidgetItem* item = qtListView->currentItem();
-  if(item) item->setSelected(false);
-  item = qtListView->topLevelItem(selection);
-  if(item) qtListView->setCurrentItem(item);
-  locked = false;
-}
-
-void pListView::setText(unsigned selection, unsigned position, string text) {
-  locked = true;
-  QTreeWidgetItem* item = qtListView->topLevelItem(selection);
-  if(item) item->setText(position, QString::fromUtf8(text));
-  locked = false;
-}
-
-void pListView::constructor() {
-  qtWidget = qtListView = new QTreeWidget;
-  qtListView->setAllColumnsShowFocus(true);
-  qtListView->setRootIsDecorated(false);
-
-  connect(qtListView, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate()));
-  connect(qtListView, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), SLOT(onChange(QTreeWidgetItem*)));
-  connect(qtListView, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(onToggle(QTreeWidgetItem*)));
-
-  pWidget::synchronizeState();
-  setCheckable(listView.state.checkable);
-  setHeaderText(listView.state.headerText.size() ? listView.state.headerText : lstring{ " " });
-  setHeaderVisible(listView.state.headerVisible);
-  for(auto& row : listView.state.text) append(row);
-  if(listView.state.checkable) {
-    for(unsigned n = 0; n < listView.state.checked.size(); n++) {
-      setChecked(n, listView.state.checked[n]);
-    }
-  }
-  setSelected(listView.state.selected);
-  if(listView.state.selected) setSelection(listView.state.selection);
-  autoSizeColumns();
-}
-
-void pListView::destructor() {
-  delete qtListView;
-  qtWidget = qtListView = nullptr;
-}
-
-void pListView::orphan() {
-  destructor();
-  constructor();
-}
-
-void pListView::onActivate() {
-  if(locked == false && listView.onActivate) listView.onActivate();
-}
-
-void pListView::onChange(QTreeWidgetItem* item) {
-  bool selected = listView.state.selected;
-  unsigned selection = listView.state.selection;
-  if(item) {
-    item->setSelected(true);  //Qt bug workaround: clicking items with mouse does not mark items as selected
-    listView.state.selected = true;
-    listView.state.selection = item->data(0, Qt::UserRole).toUInt();
-  } else {
-    listView.state.selected = false;
-    listView.state.selection = 0;
-  }
-  if(selected != listView.state.selected || selection != listView.state.selection) {
-    if(!locked && listView.onChange) listView.onChange();
-  }
-}
-
-void pListView::onToggle(QTreeWidgetItem* item) {
-  unsigned selection = item->data(0, Qt::UserRole).toUInt();
-  listView.state.checked[selection] = (item->checkState(0) == Qt::Checked);
-  if(!locked && listView.onToggle) listView.onToggle(selection);
-}
-
-}
diff --git a/processor/Makefile b/processor/GNUmakefile
similarity index 100%
rename from processor/Makefile
rename to processor/GNUmakefile
diff --git a/processor/arm/disassembler.cpp b/processor/arm/disassembler.cpp
index eaf9a368..03e90679 100644
--- a/processor/arm/disassembler.cpp
+++ b/processor/arm/disassembler.cpp
@@ -408,7 +408,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
     output.append(load ? "ldm" : "stm", conditions[condition], indices[index], " ");
     output.append(registers[rn], writeback ? "!" : "", ",{");
     for(unsigned n = 0; n < 16; n++) if(list & (1 << n)) output.append(registers[n], ",");
-    output.rtrim<1>(",");
+    output.rtrim(",");
     output.append("}", s ? "^" : "");
 
     return output;
@@ -681,7 +681,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
       if(list & (1 << l)) output.append(registers[l], ",");
     }
     if(branch) output.append(load == 0 ? "lr," : "pc,");
-    output.rtrim<1>(",");
+    output.rtrim(",");
     output.append("}");
 
     return output;
@@ -698,7 +698,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
     for(unsigned l = 0; l < 8; l++) {
       if(list & (1 << l)) output.append(registers[l], ",");
     }
-    output.rtrim<1>(",");
+    output.rtrim(",");
     output.append("}");
 
     return output;
diff --git a/processor/lr35902/disassembler.cpp b/processor/lr35902/disassembler.cpp
index 8929cf9a..9cf10036 100644
--- a/processor/lr35902/disassembler.cpp
+++ b/processor/lr35902/disassembler.cpp
@@ -12,9 +12,9 @@ string LR35902::disassemble(uint16 pc) {
     " SP:", hex<4>(r[SP])
   };
 
-  memcpy(output +   0, hex<4>(pc), 4);
-  memcpy(output +   6, opcode, opcode.length());
-  memcpy(output +  23, registers, registers.length());
+  memcpy(output +   0, hex<4>(pc).data(), 4);
+  memcpy(output +   6, opcode.data(), opcode.length());
+  memcpy(output +  23, registers.data(), registers.length());
   output[63] = 0;
   return output;
 }
diff --git a/processor/r6502/disassembler.cpp b/processor/r6502/disassembler.cpp
index 2537e063..40c7755e 100644
--- a/processor/r6502/disassembler.cpp
+++ b/processor/r6502/disassembler.cpp
@@ -182,7 +182,7 @@ string R6502::disassemble() {
   #undef op
 
   output.append("                ");
-  output[20] = 0;
+  output.resize(20);
 
   output.append(
     "A:", hex<2>(regs.a), " X:", hex<2>(regs.x), " Y:", hex<2>(regs.y), " S:", hex<2>(regs.s), " ",
diff --git a/ruby/Makefile b/ruby/GNUmakefile
similarity index 100%
rename from ruby/Makefile
rename to ruby/GNUmakefile
diff --git a/ruby/ruby.cpp b/ruby/ruby.cpp
index bdf091c9..5e03ebfb 100644
--- a/ruby/ruby.cpp
+++ b/ruby/ruby.cpp
@@ -1,5 +1,6 @@
 #include <ruby/ruby.hpp>
 
+#undef deprecated
 #undef mkdir
 #undef usleep
 #include <ruby/implementation.cpp>
diff --git a/ruby/ruby.hpp b/ruby/ruby.hpp
index ab421564..692cfe5c 100644
--- a/ruby/ruby.hpp
+++ b/ruby/ruby.hpp
@@ -1,8 +1,11 @@
-/*
-  ruby
-  version: 0.11 (2013-12-19)
-  license: public domain
-*/
+/* ruby
+ * author: byuu
+ * license: ISC
+ * version: 0.11 (2013-12-19)
+ *
+ * ruby is a cross-platform hardware abstraction layer
+ * it provides a common interface to video, audio and input devices
+ */
 
 #ifndef RUBY_H
 #define RUBY_H
diff --git a/ruby/video/opengl/main.hpp b/ruby/video/opengl/main.hpp
index 829fda6a..8b90ceff 100644
--- a/ruby/video/opengl/main.hpp
+++ b/ruby/video/opengl/main.hpp
@@ -28,11 +28,11 @@ void OpenGL::shader(const char* pathname) {
     for(auto& node : document["output"]) {
       string text = node.text();
       if(node.name == "width") {
-        if(text.endsWith("%")) relativeWidth = real(text.rtrim<1>("%")) / 100.0;
+        if(text.endsWith("%")) relativeWidth = real(text.rtrim("%")) / 100.0;
         else absoluteWidth = decimal(text);
       }
       if(node.name == "height") {
-        if(text.endsWith("%")) relativeHeight = real(text.rtrim<1>("%")) / 100.0;
+        if(text.endsWith("%")) relativeHeight = real(text.rtrim("%")) / 100.0;
         else absoluteHeight = decimal(text);
       }
     }
diff --git a/ruby/video/opengl/program.hpp b/ruby/video/opengl/program.hpp
index 21950846..de127ccb 100644
--- a/ruby/video/opengl/program.hpp
+++ b/ruby/video/opengl/program.hpp
@@ -4,9 +4,9 @@ void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin
   modulo = glrModulo(node["modulo"].integer());
 
   string w = node["width"].text(), h = node["height"].text();
-  if(w.endsWith("%")) relativeWidth = real(w.rtrim<1>("%")) / 100.0;
+  if(w.endsWith("%")) relativeWidth = real(w.rtrim("%")) / 100.0;
   else absoluteWidth = decimal(w);
-  if(h.endsWith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0;
+  if(h.endsWith("%")) relativeHeight = real(h.rtrim("%")) / 100.0;
   else absoluteHeight = decimal(h);
 
   format = glrFormat(node["format"].text());
@@ -78,7 +78,7 @@ void OpenGLProgram::parse(OpenGL* instance, string& source) {
     if(auto position = s.find("//")) s.resize(position());  //strip comments
     s.strip();  //remove extraneous whitespace
     if(s.match("#in ?*")) {
-      s.ltrim<1>("#in ").strip();
+      s.ltrim("#in ").strip();
       if(auto setting = instance->settings.find({s})) {
         line = {"#define ", setting().name, " ", setting().value};
       } else {
diff --git a/ruby/video/sdl.cpp b/ruby/video/sdl.cpp
index 0079f87d..4c63d237 100644
--- a/ruby/video/sdl.cpp
+++ b/ruby/video/sdl.cpp
@@ -109,6 +109,7 @@ struct pVideoSDL {
   bool init() {
     display = XOpenDisplay(0);
 
+    //todo: this causes a segfault inside SDL_SetVideoMode on FreeBSD (works under Linux)
     char env[512];
     sprintf(env, "SDL_WINDOWID=%ld", (long int)settings.handle);
     putenv(env);
diff --git a/ruby/video/xshm.cpp b/ruby/video/xshm.cpp
index ef1552a8..f1592218 100644
--- a/ruby/video/xshm.cpp
+++ b/ruby/video/xshm.cpp
@@ -1,3 +1,10 @@
+//XShm driver for Xorg
+
+//Note that on composited displays, the alpha bits will allow translucency underneath the active window
+//As this is not a feature of ruby, this driver must always set the alpha bits on clear() and refresh()
+
+//Linear interpolation is only applied horizontally for performance reasons, although Nearest is still much faster
+
 #include <sys/shm.h>
 #include <X11/extensions/XShm.h>
 
@@ -18,30 +25,22 @@ struct pVideoXShm {
   } device;
 
   struct Settings {
-    uintptr_t handle;
-    unsigned depth = 24;
+    uintptr_t handle = 0;
+    unsigned filter = Video::FilterLinear;
 
     uint32_t* buffer = nullptr;
     unsigned width, height;
   } settings;
 
-  struct Color {
-    unsigned depth;
-    unsigned shift;
-
-    unsigned idepth;
-    unsigned ishift;
-  } red, green, blue;
-
   bool cap(const string& name) {
     if(name == Video::Handle) return true;
-    if(name == Video::Depth) return true;
+    if(name == Video::Filter) return true;
     return false;
   }
 
   any get(const string& name) {
     if(name == Video::Handle) return settings.handle;
-    if(name == Video::Depth) return settings.depth;
+    if(name == Video::Filter) return settings.filter;
     return false;
   }
 
@@ -50,8 +49,9 @@ struct pVideoXShm {
       settings.handle = any_cast<uintptr_t>(value);
       return true;
     }
-    if(name == Video::Depth) {
-      return setDepth(any_cast<unsigned>(value));
+    if(name == Video::Filter) {
+      settings.filter = any_cast<unsigned>(value);
+      return true;
     }
     return false;
   }
@@ -60,7 +60,7 @@ struct pVideoXShm {
     if(settings.buffer == nullptr || settings.width != width || settings.height != height) {
       if(settings.buffer) delete[] settings.buffer;
       settings.width = width, settings.height = height;
-      settings.buffer = new uint32_t[width * height]();
+      settings.buffer = new uint32_t[width * height + 16];  //+16 is padding for linear interpolation
     }
 
     data = settings.buffer;
@@ -73,7 +73,9 @@ struct pVideoXShm {
 
   void clear() {
     if(settings.buffer == nullptr) return;
-    memset(settings.buffer, 0, settings.width * settings.height * sizeof(uint32_t));
+    uint32_t* dp = settings.buffer;
+    unsigned length = settings.width * settings.height;
+    while(length--) *dp++ = (255u << 24);
     refresh();
   }
 
@@ -81,23 +83,27 @@ struct pVideoXShm {
     if(settings.buffer == nullptr) return;
     size();
 
-    float xRatio = (float)settings.width  / (float)device.width;
-    float yRatio = (float)settings.height / (float)device.height;
-    float yStep = 0;
+    float xratio = (float)settings.width  / (float)device.width;
+    float yratio = (float)settings.height / (float)device.height;
+
+    #pragma omp parallel for
     for(unsigned y = 0; y < device.height; y++) {
-      uint32_t* sp = settings.buffer + (unsigned)yStep * settings.width;
+      float ystep = y * yratio;
+      float xstep = 0;
+
+      uint32_t* sp = settings.buffer + (unsigned)ystep * settings.width;
       uint32_t* dp = device.buffer + y * device.width;
-      yStep += yRatio;
-      float xStep = 0;
-      for(unsigned x = 0; x < device.width; x++) {
-        uint32_t color = sp[(unsigned)xStep];
-        xStep += xRatio;
-        unsigned r = (color >> red.ishift  ) & ((1 << red.idepth  ) - 1);
-        unsigned g = (color >> green.ishift) & ((1 << green.idepth) - 1);
-        unsigned b = (color >> blue.ishift ) & ((1 << blue.idepth ) - 1);
-        *dp++ = image::normalize(r, red.idepth,   red.depth  ) << red.shift
-              | image::normalize(g, green.idepth, green.depth) << green.shift
-              | image::normalize(b, blue.idepth,  blue.depth ) << blue.shift;
+
+      if(settings.filter == Video::FilterNearest) {
+        for(unsigned x = 0; x < device.width; x++) {
+          *dp++ = (255u << 24) | sp[(unsigned)xstep];
+          xstep += xratio;
+        }
+      } else {  //settings.filter == Video::FilterLinear
+        for(unsigned x = 0; x < device.width; x++) {
+          *dp++ = (255u << 24) | interpolate(xstep - (unsigned)xstep, sp[(unsigned)xstep], sp[(unsigned)xstep + 1]);
+          xstep += xratio;
+        }
       }
     }
 
@@ -118,24 +124,12 @@ struct pVideoXShm {
     XGetWindowAttributes(device.display, (Window)settings.handle, &getAttributes);
     device.depth = getAttributes.depth;
     device.visual = getAttributes.visual;
-    unsigned visualID = XVisualIDFromVisual(device.visual);
-
-    XVisualInfo visualTemplate = {0};
-    visualTemplate.screen = device.screen;
-    visualTemplate.depth = device.depth;
-    int visualsMatched = 0;
-    XVisualInfo* visualList = XGetVisualInfo(device.display, VisualScreenMask | VisualDepthMask, &visualTemplate, &visualsMatched);
-    for(unsigned n = 0; n < visualsMatched; n++) {
-      auto& v = visualList[n];
-      if(v.visualid == visualID) {
-        red.depth   = bit::count(v.red_mask),   red.shift   = bit::first(v.red_mask);
-        green.depth = bit::count(v.green_mask), green.shift = bit::first(v.green_mask);
-        blue.depth  = bit::count(v.blue_mask),  blue.shift  = bit::first(v.blue_mask);
-        break;
-      }
+    //driver only supports 32-bit pixels
+    //note that even on 15-bit and 16-bit displays, the window visual's depth should be 32
+    if(device.depth < 24 || device.depth > 32) {
+      free();
+      return false;
     }
-    XFree(visualList);
-    setDepth(settings.depth);
 
     XSetWindowAttributes setAttributes = {0};
     setAttributes.border_pixel = 0;
@@ -168,26 +162,6 @@ struct pVideoXShm {
   }
 
 //internal:
-  bool setDepth(unsigned depth) {
-    if(depth == 24) {
-      settings.depth = 24;
-      red.idepth   = 8, red.ishift   = 16;
-      green.idepth = 8, green.ishift =  8;
-      blue.idepth  = 8, blue.ishift  =  0;
-      return true;
-    }
-
-    if(depth == 30) {
-      settings.depth = 30;
-      red.idepth   = 10, red.ishift   = 20;
-      green.idepth = 10, green.ishift = 10;
-      blue.idepth  = 10, blue.ishift  =  0;
-      return true;
-    }
-
-    return false;
-  }
-
   bool size() {
     XWindowAttributes windowAttributes;
     XGetWindowAttributes(device.display, settings.handle, &windowAttributes);
@@ -197,7 +171,6 @@ struct pVideoXShm {
     XResizeWindow(device.display, device.window, device.width, device.height);
     free();
 
-    //create
     device.shmInfo.shmid = shmget(IPC_PRIVATE, device.width * device.height * sizeof(uint32_t), IPC_CREAT | 0777);
     if(device.shmInfo.shmid < 0) return false;
 
@@ -220,6 +193,15 @@ struct pVideoXShm {
     shmdt(device.shmInfo.shmaddr);
     shmctl(device.shmInfo.shmid, IPC_RMID, 0);
   }
+
+  alwaysinline uint32_t interpolate(float mu, uint32_t a, uint32_t b) {
+    uint8_t ar = (a >> 16), ag = (a >> 8), ab = (a >> 0);
+    uint8_t br = (b >> 16), bg = (b >> 8), bb = (b >> 0);
+    uint8_t cr = ar * (1.0 - mu) + br * mu;
+    uint8_t cg = ag * (1.0 - mu) + bg * mu;
+    uint8_t cb = ab * (1.0 - mu) + bb * mu;
+    return (cr << 16) | (cg << 8) | (cb << 0);
+  }
 };
 
 DeclareVideo(XShm)
diff --git a/sfc/Makefile b/sfc/GNUmakefile
similarity index 100%
rename from sfc/Makefile
rename to sfc/GNUmakefile
diff --git a/sfc/cartridge/cartridge.cpp b/sfc/cartridge/cartridge.cpp
index 4a599991..c9bd946e 100644
--- a/sfc/cartridge/cartridge.cpp
+++ b/sfc/cartridge/cartridge.cpp
@@ -66,56 +66,44 @@ void Cartridge::load() {
 
   //Super Game Boy
   if(cartridge.has_gb_slot()) {
-    sha256 = nall::sha256(GameBoy::cartridge.romdata, GameBoy::cartridge.romsize);
+    sha256 = Hash::SHA256(GameBoy::cartridge.romdata, GameBoy::cartridge.romsize).digest();
   }
 
   //Broadcast Satellaview
   else if(cartridge.has_bs_cart() && cartridge.has_bs_slot()) {
-    sha256 = nall::sha256(satellaviewcartridge.memory.data(), satellaviewcartridge.memory.size());
+    sha256 = Hash::SHA256(satellaviewcartridge.memory.data(), satellaviewcartridge.memory.size()).digest();
   }
 
   //Sufami Turbo
   else if(cartridge.has_st_slots()) {
-    sha256_ctx sha;
-    uint8_t hash[32];
-    sha256_init(&sha);
-    sha256_chunk(&sha, sufamiturboA.rom.data(), sufamiturboA.rom.size());
-    sha256_chunk(&sha, sufamiturboB.rom.data(), sufamiturboB.rom.size());
-    sha256_final(&sha);
-    sha256_hash(&sha, hash);
-    string result;
-    for(auto& byte : hash) result.append(hex<2>(byte));
-    sha256 = result;
+    Hash::SHA256 sha;
+    sha.data(sufamiturboA.rom.data(), sufamiturboA.rom.size());
+    sha.data(sufamiturboB.rom.data(), sufamiturboB.rom.size());
+    sha256 = sha.digest();
   }
 
   //Super Famicom
   else {
-    sha256_ctx sha;
-    uint8_t hash[32];
-    vector<uint8_t> buffer;
-    sha256_init(&sha);
+    Hash::SHA256 sha;
     //hash each ROM image that exists; any with size() == 0 is ignored by sha256_chunk()
-    sha256_chunk(&sha, rom.data(), rom.size());
-    sha256_chunk(&sha, bsxcartridge.rom.data(), bsxcartridge.rom.size());
-    sha256_chunk(&sha, sa1.rom.data(), sa1.rom.size());
-    sha256_chunk(&sha, superfx.rom.data(), superfx.rom.size());
-    sha256_chunk(&sha, hitachidsp.rom.data(), hitachidsp.rom.size());
-    sha256_chunk(&sha, spc7110.prom.data(), spc7110.prom.size());
-    sha256_chunk(&sha, spc7110.drom.data(), spc7110.drom.size());
-    sha256_chunk(&sha, sdd1.rom.data(), sdd1.rom.size());
+    sha.data(rom.data(), rom.size());
+    sha.data(bsxcartridge.rom.data(), bsxcartridge.rom.size());
+    sha.data(sa1.rom.data(), sa1.rom.size());
+    sha.data(superfx.rom.data(), superfx.rom.size());
+    sha.data(hitachidsp.rom.data(), hitachidsp.rom.size());
+    sha.data(spc7110.prom.data(), spc7110.prom.size());
+    sha.data(spc7110.drom.data(), spc7110.drom.size());
+    sha.data(sdd1.rom.data(), sdd1.rom.size());
     //hash all firmware that exists
+    vector<uint8> buffer;
     buffer = armdsp.firmware();
-    sha256_chunk(&sha, buffer.data(), buffer.size());
+    sha.data(buffer.data(), buffer.size());
     buffer = hitachidsp.firmware();
-    sha256_chunk(&sha, buffer.data(), buffer.size());
+    sha.data(buffer.data(), buffer.size());
     buffer = necdsp.firmware();
-    sha256_chunk(&sha, buffer.data(), buffer.size());
+    sha.data(buffer.data(), buffer.size());
     //finalize hash
-    sha256_final(&sha);
-    sha256_hash(&sha, hash);
-    string result;
-    for(auto& byte : hash) result.append(hex<2>(byte));
-    sha256 = result;
+    sha256 = sha.digest();
   }
 
   rom.write_protect(true);
diff --git a/sfc/chip/event/event.cpp b/sfc/chip/event/event.cpp
index b7276295..5ffae110 100644
--- a/sfc/chip/event/event.cpp
+++ b/sfc/chip/event/event.cpp
@@ -63,7 +63,7 @@ void Event::submitScore() {
   lstring side = interface->server().split<1>("@");
   string username = side(0).split<1>(":")(0);
   string password = side(0).split<1>(":")(1);
-  side(1).ltrim<1>("http://");
+  side(1).ltrim("http://");
   string hostname = side(1).split<1>("/")(0);
   string hostpath = side(1).split<1>("/")(1);
   side = hostname.split<1>(":");
@@ -71,7 +71,7 @@ void Event::submitScore() {
   string hostport = side(1);
   if(hostport.empty()) hostport = "80";
 
-  http server;
+/*http server;
   if(server.connect(hostname, decimal(hostport))) {
     string content = {
       "username:", username, "\n",
@@ -92,7 +92,7 @@ void Event::submitScore() {
     };
     server.send(packet);
     server.disconnect();
-  }
+  }*/
 }
 
 void Event::init() {
diff --git a/sfc/chip/hsu1/hsu1.cpp b/sfc/chip/hsu1/hsu1.cpp
index 5feaebf3..6cf06d63 100644
--- a/sfc/chip/hsu1/hsu1.cpp
+++ b/sfc/chip/hsu1/hsu1.cpp
@@ -55,7 +55,7 @@ void HSU1::write(unsigned addr, uint8 data) {
       lstring side = interface->server().split<1>("@");
       string username = side(0).split<1>(":")(0);
       string password = side(0).split<1>(":")(1);
-      side(1).ltrim<1>("http://");
+      side(1).ltrim("http://");
       string hostname = side(1).split<1>("/")(0);
       string hostpath = side(1).split<1>("/")(1);
       side = hostname.split<1>(":");
@@ -63,7 +63,7 @@ void HSU1::write(unsigned addr, uint8 data) {
       string hostport = side(1);
       if(hostport.empty()) hostport = "80";
 
-      http server;
+    /*http server;
       if(server.connect(hostname, decimal(hostport))) {
         string header {
           "username:", username, "\n",
@@ -97,7 +97,7 @@ void HSU1::write(unsigned addr, uint8 data) {
         free(data);
 
         server.disconnect();
-      }
+      }*/
     }
     txlatch = latch;
   }
diff --git a/sfc/system/serialization.cpp b/sfc/system/serialization.cpp
index a29a9eeb..3c68e902 100644
--- a/sfc/system/serialization.cpp
+++ b/sfc/system/serialization.cpp
@@ -8,7 +8,7 @@ serializer System::serialize() {
   memcpy(&hash, (const char*)cartridge.sha256(), 64);
   memset(&description, 0, sizeof description);
   memset(&profile, 0, sizeof profile);
-  strmcpy(profile, Emulator::Profile, sizeof profile);
+  strcpy(profile, Emulator::Profile);
 
   s.integer(signature);
   s.integer(version);
diff --git a/target-higan/GNUmakefile b/target-higan/GNUmakefile
new file mode 100644
index 00000000..2f741edc
--- /dev/null
+++ b/target-higan/GNUmakefile
@@ -0,0 +1,53 @@
+name := higan
+
+processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050
+include processor/GNUmakefile
+include fc/GNUmakefile
+include sfc/GNUmakefile
+include gb/GNUmakefile
+include gba/GNUmakefile
+
+ui_objects := ui-higan
+ui_objects += ruby hiro
+
+# platform
+ifeq ($(platform),windows)
+else ifeq ($(platform),macosx)
+else ifeq ($(platform),linux)
+else ifeq ($(platform),bsd)
+  ruby := video.glx video.xshm
+  ruby += audio.openal audio.oss
+  ruby += input.xlib
+endif
+
+# ruby
+include ruby/GNUmakefile
+link += $(rubylink)
+
+# hiro
+include hiro/GNUmakefile
+link += $(hirolink)
+
+# rules
+objects := $(ui_objects) $(objects)
+objects := $(patsubst %,obj/%.o,$(objects))
+
+obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/)
+	$(compiler) $(rubyflags) -c $< -o $@
+
+obj/hiro.o: hiro/hiro.cpp $(call rwildcard,hiro/)
+	$(compiler) $(hiroflags) -c $< -o $@
+
+obj/ui-higan.o: $(ui)/higan.cpp $(call rwildcard,$(ui)/)
+
+# build
+build: $(objects)
+	$(strip $(compiler) -o out/$(name) $(objects) $(link))
+
+install:
+	cp out/$(name) $(prefix)/bin/$(name)
+	cp data/higan.png $(prefix)/share/icons/higan.png
+
+uninstall:
+	if [ -f $(prefix)/bin/$(name) ]; then rm $(prefix)/bin/$(name); fi
+	if [ -f $(prefix)/share/icons/higan.png ]; then rm $(prefix)/share/icons/higan.png
diff --git a/target-higan/Makefile b/target-higan/Makefile
deleted file mode 100644
index 6e726ce5..00000000
--- a/target-higan/Makefile
+++ /dev/null
@@ -1,117 +0,0 @@
-name := higan
-
-processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050
-include processor/Makefile
-
-include fc/Makefile
-include sfc/Makefile
-include gb/Makefile
-include gba/Makefile
-
-ui_objects := ui-higan ui-configuration ui-interface ui-utility
-ui_objects += ui-input ui-window ui-general ui-settings ui-tools
-ui_objects += phoenix ruby
-ui_objects += $(if $(call streq,$(platform),windows),resource)
-
-# platform
-ifeq ($(platform),windows)
-  ruby := video.direct3d video.wgl video.directdraw video.gdi
-  ruby += audio.directsound audio.xaudio2
-  ruby += input.windows
-else ifeq ($(platform),macosx)
-  ruby := video.cgl
-  ruby += audio.openal
-  ruby += input.carbon
-else ifeq ($(platform),linux)
-  ruby := video.glx video.xv video.xshm video.sdl
-  ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
-  ruby += input.udev input.sdl input.xlib
-else ifeq ($(platform),bsd)
-  ruby := video.glx
-  ruby += audio.openal audio.oss
-  ruby += input.xlib
-endif
-
-# phoenix
-include phoenix/Makefile
-link += $(phoenixlink)
-
-# ruby
-include ruby/Makefile
-link += $(rubylink)
-
-# rules
-objects := $(ui_objects) $(objects)
-objects := $(patsubst %,obj/%.o,$(objects))
-
-obj/ui-higan.o: $(ui)/higan.cpp $(call rwildcard,$(ui)/)
-obj/ui-configuration.o: $(ui)/configuration/configuration.cpp $(call rwildcard,$(ui)/)
-obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
-obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
-obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
-obj/ui-window.o: $(ui)/window/window.cpp $(call rwildcard,$(ui)/)
-obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/)
-obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
-obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/)
-
-obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/)
-	$(compiler) $(rubyflags) -c $< -o $@
-
-obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/)
-	$(compiler) $(phoenixflags) -c $< -o $@
-
-obj/resource.o: $(ui)/resource.rc
-ifeq ($(arch),win32)
-	windres --target=pe-i386 $(ui)/resource.rc obj/resource.o
-else
-	windres $(ui)/resource.rc obj/resource.o
-endif
-
-# targets
-build: $(objects)
-ifeq ($(platform),windows)
-	$(strip $(compiler) -shared -o out/phoenix.dll obj/phoenix.o $(phoenixlink))
-	$(strip $(compiler) -o out/$(name) $(subst obj/phoenix.o,,$(objects)) $(link) -Lout -lphoenix)
-else ifeq ($(platform),macosx)
-	if [ -d out/$(name).app ]; then rm -r out/$(name).app; fi
-	mkdir out/$(name).app
-	mkdir out/$(name).app/Contents
-	mkdir out/$(name).app/Contents/MacOS
-	mkdir out/$(name).app/Contents/Resources
-	cp data/Info.plist out/$(name).app/Contents/Info.plist
-	sips -s format icns data/higan.png --out out/$(name).app/Contents/Resources/higan.icns
-	$(strip $(compiler) -o out/$(name).app/Contents/MacOS/$(name) $(objects) $(link))
-else
-	$(strip $(compiler) -o out/$(name) $(objects) $(link))
-endif
-
-resource:
-	sourcery $(ui)/resource/resource.bml $(ui)/resource/resource.cpp $(ui)/resource/resource.hpp
-
-install:
-ifneq ($(shell id -un),root)
-	$(error "make install must be run as root")
-else ifeq ($(platform),windows)
-else ifeq ($(platform),macosx)
-	mkdir -p /Library/Application\ Support/$(name)
-	cp -R profile/* /Library/Application\ Support/$(name)
-	cp data/cheats.bml /Library/Application\ Support/$(name)/cheats.bml
-	chmod -R 777 /Library/Application\ Support/$(name)
-else
-	cp out/$(name) $(prefix)/bin/$(name)
-	cp data/$(name).png $(prefix)/share/pixmaps/$(name).png
-	cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
-	mkdir -p /usr/share/$(name)
-	cp -R profile/* /usr/share/$(name)
-	cp data/cheats.bml /usr/share/$(name)/cheats.bml
-	chmod -R 777 /usr/share/$(name)
-endif
-
-uninstall:
-ifneq ($(shell id -un),root)
-	$(error "make uninstall must be run as root")
-else ifeq ($(platform),windows)
-else ifeq ($(platform),macosx)
-else
-	rm $(prefix)/bin/$(name)
-endif
diff --git a/target-higan/bootstrap.cpp b/target-higan/bootstrap.cpp
deleted file mode 100644
index 5b14e25e..00000000
--- a/target-higan/bootstrap.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <fc/interface/interface.hpp>
-#include <sfc/interface/interface.hpp>
-#include <gb/interface/interface.hpp>
-#include <gba/interface/interface.hpp>
-
-void Program::bootstrap() {
-  interface = new Interface;
-
-  emulator.append(new Famicom::Interface);
-  emulator.append(new SuperFamicom::Interface);
-  emulator.append(new GameBoy::Interface);
-  emulator.append(new GameBoyAdvance::Interface);
-
-  for(auto& system : emulator) system->bind = interface;
-}
diff --git a/target-higan/configuration/configuration.cpp b/target-higan/configuration/configuration.cpp
deleted file mode 100644
index 612f1bfc..00000000
--- a/target-higan/configuration/configuration.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "../higan.hpp"
-ConfigurationSettings* config = nullptr;
-
-ConfigurationSettings::ConfigurationSettings() {
-  video.append(video.driver = ruby::video.optimalDriver(), "Driver");
-  video.append(video.synchronize = false, "Synchronize");
-  video.append(video.shader = "Blur", "Shader");
-  video.append(video.scaleMode = 0, "ScaleMode");
-  video.append(video.aspectCorrection = true, "AspectCorrection");
-  video.append(video.colorEmulation = true, "ColorEmulation");
-  video.maskOverscan.assign(video.maskOverscan.enable = false);
-  video.maskOverscan.append(video.maskOverscan.horizontal = 8, "Horizontal");
-  video.maskOverscan.append(video.maskOverscan.vertical = 8, "Vertical");
-  video.append(video.maskOverscan, "MaskOverscan");
-  video.append(video.saturation = 100, "Saturation");
-  video.append(video.gamma = 100, "Gamma");
-  video.append(video.luminance = 100, "Luminance");
-  video.append(video.startFullScreen = false, "StartFullScreen");
-  append(video, "Video");
-
-  audio.append(audio.driver = ruby::audio.optimalDriver(), "Driver");
-  audio.append(audio.synchronize = true, "Synchronize");
-  audio.append(audio.frequency = 48000, "Frequency");
-  audio.append(audio.latency = 60, "Latency");
-  audio.append(audio.resampler = 2, "Resampler");
-  audio.append(audio.volume = 100, "Volume");
-  audio.append(audio.mute = false, "Mute");
-  append(audio, "Audio");
-
-  input.append(input.driver = ruby::input.optimalDriver(), "Driver");
-  input.focus.append(input.focus.pause = false, "Pause");
-  input.focus.append(input.focus.allow = false, "AllowInput");
-  input.append(input.focus, "Focus");
-  append(input, "Input");
-
-  timing.append(timing.video = 60.0, "Video");
-  timing.append(timing.audio = 48000.0, "Audio");
-  append(timing, "Timing");
-
-  server.append(server.hostname = "", "Hostname");
-  server.append(server.username = "", "Username");
-  server.append(server.password = "", "Password");
-  append(server, "Server");
-
-  library.append(library.selection = -1, "Selection");
-  library.append(library.mediaMode = 0, "MediaMode");
-  library.append(library.showOnStartup = true, "ShowOnStartup");
-  append(library, "Library");
-
-  load();
-}
-
-void ConfigurationSettings::load() {
-  Configuration::Document::load(program->path("settings.bml"));
-  save();  //creates file if it does not exist
-}
-
-void ConfigurationSettings::save() {
-  Configuration::Document::save(program->path("settings.bml"));
-}
diff --git a/target-higan/configuration/configuration.hpp b/target-higan/configuration/configuration.hpp
deleted file mode 100644
index 9591669c..00000000
--- a/target-higan/configuration/configuration.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-struct ConfigurationSettings : Configuration::Document {
-  struct Video : Configuration::Node {
-    string driver;
-    bool synchronize;
-    string shader;
-    unsigned scaleMode;
-    bool aspectCorrection;
-    bool colorEmulation;
-    struct MaskOverscan : Configuration::Node {
-      bool enable;
-      unsigned horizontal;
-      unsigned vertical;
-    } maskOverscan;
-    unsigned saturation;
-    unsigned gamma;
-    unsigned luminance;
-    bool startFullScreen;
-  } video;
-
-  struct Audio : Configuration::Node {
-    string driver;
-    bool synchronize;
-    unsigned frequency;
-    unsigned latency;
-    unsigned resampler;
-    unsigned volume;
-    bool mute;
-  } audio;
-
-  struct Input : Configuration::Node {
-    string driver;
-    struct Focus : Configuration::Node {
-      bool pause;
-      bool allow;
-    } focus;
-  } input;
-
-  struct Timing : Configuration::Node {
-    double video;
-    double audio;
-  } timing;
-
-  struct Server : Configuration::Node {
-    string hostname;
-    string username;
-    string password;
-  } server;
-
-  struct Library : Configuration::Node {
-    signed selection;
-    unsigned mediaMode;
-    bool showOnStartup;
-  } library;
-
-  void load();
-  void save();
-  ConfigurationSettings();
-};
-
-extern ConfigurationSettings* config;
diff --git a/target-higan/general/dip-switches.cpp b/target-higan/general/dip-switches.cpp
deleted file mode 100644
index 2734daaa..00000000
--- a/target-higan/general/dip-switches.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-DipSwitches* dipSwitches = nullptr;
-
-DipSwitch::DipSwitch() {
-  append(name, {100, 0}, 5);
-  append(value, {~0, 0});
-}
-
-DipSwitches::DipSwitches() {
-  setTitle("DIP Switches");
-  layout.setMargin(5);
-  accept.setText("Accept");
-
-  append(layout);
-  for(auto& dipItem : dip) layout.append(dipItem, {~0, 0}, 5);
-  layout.append(controlLayout, {~0, 0});
-    controlLayout.append(spacer, {~0, 0});
-    controlLayout.append(accept, {80, 0});
-
-  setGeometry({128, 128, 250, layout.minimumSize().height});
-  windowManager->append(this, "DipSwitches");
-
-  onClose = accept.onActivate = [&] {
-    setModal(false);
-    setVisible(false);
-  };
-}
-
-unsigned DipSwitches::run(const Markup::Node& node) {
-  for(auto& dipItem : dip) {
-    dipItem.name.setEnabled(false);
-    dipItem.name.setText("(empty)");
-    dipItem.value.setEnabled(false);
-    dipItem.value.reset();
-    dipItem.values.reset();
-  }
-
-  unsigned index = 0;
-  for(auto& setting : node) {
-    if(setting.name != "setting") continue;
-    dip[index].name.setEnabled();
-    dip[index].name.setText(setting["name"].data);
-    dip[index].value.setEnabled();
-    for(auto& option : setting) {
-      if(option.name != "option") continue;
-      if(auto result = Eval::integer(option["value"].data)) {
-        dip[index].value.append(option["name"].data);
-        dip[index].values.append(result());
-      }
-    }
-
-    if(++index >= Slots) break;
-  }
-
-  setVisible();
-  accept.setFocused();
-
-  audio.clear();
-  setModal();
-
-  unsigned result = 0;
-  for(auto& dipItem : dip) {
-    if(dipItem.value.enabled() == false) continue;
-    result |= dipItem.values[dipItem.value.selection()];
-  }
-  return result;
-}
diff --git a/target-higan/general/dip-switches.hpp b/target-higan/general/dip-switches.hpp
deleted file mode 100644
index 0b25b73d..00000000
--- a/target-higan/general/dip-switches.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-struct DipSwitch : HorizontalLayout {
-  Label name;
-  ComboButton value;
-  vector<unsigned> values;
-
-  DipSwitch();
-};
-
-struct DipSwitches : Window {
-  enum : unsigned { Slots = 8 };
-
-  VerticalLayout layout;
-  DipSwitch dip[Slots];
-  HorizontalLayout controlLayout;
-    Widget spacer;
-    Button accept;
-
-  unsigned run(const Markup::Node& node);
-  DipSwitches();
-};
-
-extern DipSwitches* dipSwitches;
diff --git a/target-higan/general/general.cpp b/target-higan/general/general.cpp
deleted file mode 100644
index fd34b8fa..00000000
--- a/target-higan/general/general.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "../higan.hpp"
-#include "library.cpp"
-#include "presentation.cpp"
-#include "dip-switches.cpp"
diff --git a/target-higan/general/general.hpp b/target-higan/general/general.hpp
deleted file mode 100644
index 4e612af2..00000000
--- a/target-higan/general/general.hpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "library.hpp"
-#include "presentation.hpp"
-#include "dip-switches.hpp"
diff --git a/target-higan/general/library.cpp b/target-higan/general/library.cpp
deleted file mode 100644
index 3ce67235..00000000
--- a/target-higan/general/library.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-LibraryManager* libraryManager = nullptr;
-
-LibraryBrowser::LibraryBrowser(Emulator::Interface& emulator) : emulator(emulator) {
-  setMargin(5);
-
-  informationType.setText({
-    "Title:\n",
-    "Serial:"
-  });
-
-  for(auto& media : emulator.media) {
-    mediaMode.append(media.name);
-  }
-
-  unsigned height = Font::size(program->normalFont, " ").height;
-
-  append(folders, {~0, ~0}, 5);
-  append(informationLayout, {~0, 0});
-  informationLayout.append(informationType, {0, height * 2}, 5);
-  informationLayout.append(information, {~0, height * 2}, 5);
-  informationLayout.append(mediaMode, {0, 0});
-
-  folders.onActivate = {&LibraryBrowser::onActivate, this};
-  folders.onChange = {&LibraryBrowser::onChange, this};
-  mediaMode.onChange = {&LibraryBrowser::setMode, this};
-}
-
-void LibraryBrowser::onActivate() {
-  if(folders.selected() == false) return;
-  if(libraryManager->loadButton.enabled() == false) return;
-
-  unsigned selection = folders.selection();
-  string pathname = {this->pathname, folders.text(selection, 0), typeSuffix};
-
-  libraryManager->loaded.append(folders.text(selection, 0));
-  libraryManager->setInformation(false);
-
-  if(libraryManager->slotLoad == false) {
-    utility->loadMedia(pathname);
-  } else {
-    libraryManager->loadPathname = pathname;
-    libraryManager->setModal(false);
-  }
-}
-
-void LibraryBrowser::onChange() {
-  if(folders.selected() == false) return information.setText("");
-
-  string manifest = {pathname, folders.text(folders.selection(), 0), typeSuffix, "manifest.bml"};
-  auto document = Markup::Document(file::read(manifest));
-
-  information.setText({
-    document["information/title"].text(), "\n",
-    document["information/serial"].text()
-  });
-}
-
-void LibraryBrowser::refresh() {
-  folders.reset();
-  lstring pathnames = directory::ifolders(pathname, typeMask);
-  unsigned selection = 0;
-  for(auto& pathname : pathnames) {
-    folders.append(string{pathname}.rtrim<1>(typeSuffix));
-    folders.setImage(selection++, 0, {resource::game, sizeof resource::game});
-  }
-  folders.setSelection(0);
-  onChange();
-}
-
-void LibraryBrowser::setMode() {
-  config->library.mediaMode = mediaMode.selection();
-  auto& media = emulator.media[mediaMode.selection()];
-
-  pathname = {utility->libraryPath(), media.name, "/"};
-  type = media.type;
-  typeMask = {"*.", type};
-  typeSuffix = {".", type, "/"};
-
-  refresh();
-  folders.setFocused();
-  libraryManager->synchronize();
-}
-
-LibraryImport::LibraryImport() {
-  setMargin(5);
-  information.setText({
-    "higan manages games in a library. To play a game, you must first import the game.\n"
-    "After doing so, the game will appear inside your library, and can then be loaded and played."
-  });
-  importButton.setText("Import Game ...");
-  append(information, {~0, 0}, 5);
-  append(importButton, {0, 0});
-
-  importButton.onActivate = {&LibraryImport::onImportActivate, this};
-}
-
-void LibraryImport::onImportActivate() {
-  if(program->ananke.open() == false) {
-    MessageWindow().setText("ananke must be installed to use this feature").warning();
-    return;
-  }
-  function<string ()> browse = program->ananke.sym("ananke_browse");
-  if(!browse) return;
-  audio.clear();  //ananke's browser is modal
-  string pathname = browse();
-  pathname.rtrim<1>("/");
-  if(pathname.empty()) return;
-
-  //after importing game, take user to the relevant game list to show the newly imported title
-  string type = extension(pathname);
-  for(signed bootable = 1; bootable >= 0; bootable--) {
-    unsigned selection = 0;
-    for(auto& browser : libraryManager->browsers) {
-      unsigned mode = 0;
-      for(auto& media : browser->emulator.media) {
-        if(type == media.type && media.bootable == bootable) {
-          browser->mediaMode.setSelection(mode);
-          libraryManager->libraryFrame.setSelection(selection);
-          libraryManager->onChange();
-
-          //find game in list and select it
-          string name = notdir(nall::basename(pathname));
-          for(unsigned n = 0; n < browser->folders.rows(); n++) {
-            if(browser->folders.text(n, 0) == name) {
-              browser->folders.setSelection(n);
-              browser->onChange();
-              break;
-            }
-          }
-
-          return;
-        }
-        mode++;
-      }
-      selection++;
-    }
-  }
-}
-
-LibraryManager::LibraryManager() {
-  libraryManager = this;
-
-  setTitle("Game Library");
-  setGeometry({128, 128, 640, 680});
-  windowManager->append(this, "LibraryManager");
-
-  layout.setMargin(5);
-  bootstrap();
-  libraryFrame.append("Import");
-  libraryFrame.setLayout(browsers.size(), libraryImport);
-  loadButton.setText("Load");
-
-  unsigned height = Font::size(program->normalFont, " ").height;
-
-  append(layout);
-  layout.append(libraryFrame, {~0, ~0}, 5);
-  layout.append(informationLayout, {~0, 0});
-  informationLayout.append(information, {~0, height * 3}, 5);
-  informationLayout.append(skipButton, {80, 0}, 5);
-  informationLayout.append(loadButton, {80, 0});
-
-  onClose = skipButton.onActivate = [&] {
-    setModal(false);
-    setVisible(false);
-  };
-
-  libraryFrame.onChange = {&LibraryManager::onChange, this};
-  loadButton.onActivate = {&LibraryManager::onLoad, this};
-
-  //initial config value of -1 defaults to import tab on first launch of higan
-  if(config->library.selection < 0) config->library.selection = browsers.size();
-  libraryFrame.setSelection(config->library.selection);
-
-  if(libraryFrame.selection() < browsers.size()) {
-    browsers[libraryFrame.selection()]->mediaMode.setSelection(config->library.mediaMode);
-    browsers[libraryFrame.selection()]->setMode();
-  }
-}
-
-void LibraryManager::bootstrap() {
-  unsigned selection = 0;
-  for(auto& emulator : program->emulator) {
-    LibraryBrowser* browser = new LibraryBrowser(*emulator);
-    libraryFrame.append(emulator->information.name);
-    libraryFrame.setLayout(selection++, *browser);
-    browsers.append(browser);
-  }
-}
-
-string LibraryManager::load(const string& type) {
-  requestedLoadType = type;
-  unsigned selection = 0;
-  for(auto& browser : browsers) {
-    unsigned mode = 0;
-    for(auto& media : browser->emulator.media) {
-      if(type == media.type && media.bootable == false) {
-        libraryFrame.setSelection(selection);
-        browser->mediaMode.setSelection(mode);
-        browser->setMode();
-
-        slotLoad = true;
-        loadPathname = "";
-        show();
-        setModal();
-        slotLoad = false;
-        browser->mediaMode.setSelection(config->library.mediaMode = 0);
-        return loadPathname;
-      }
-      mode++;
-    }
-    selection++;
-  }
-  return "";  //should never occur
-}
-
-void LibraryManager::onChange() {
-  unsigned selection = libraryFrame.selection();
-  config->library.selection = selection;
-  if(selection < browsers.size()) {
-    browsers[selection]->setMode();
-  } else {
-    loadButton.setEnabled(false);
-  }
-}
-
-void LibraryManager::onLoad() {
-  unsigned selection = libraryFrame.selection();
-  if(selection < browsers.size()) browsers[selection]->onActivate();
-}
-
-void LibraryManager::setInformation(bool load) {
-  string text;
-  if(loaded.size() == 0) {
-    text = {" \nPlease select a game to load ...\n "};
-  } else if(loaded.size() == 1 && load == false) {
-    text = {" \n", loaded[0], "\n "};
-  } else if(loaded.size() == 1 && load == true) {
-    text = {loaded[0], " \nPlease select a slot game to load ...\n "};
-  } else if(loaded.size() == 2 && load == false) {
-    text = {loaded[0], "\n", loaded[1], "\n "};
-  } else if(loaded.size() == 2 && load == true) {
-    text = {loaded[0], "\n", loaded[1], "\nPlease select a slot game to load ..."};
-  } else if(loaded.size() == 3) {
-    text = {loaded[0], "\n", loaded[1], "\n", loaded[2]};
-  }
-  information.setText(text);
-}
-
-void LibraryManager::show() {
-  if(slotLoad == false) {
-    loaded.reset();
-    requestedLoadType.reset();
-    skipButton.setText("Cancel");
-  } else {
-    skipButton.setText("Skip");
-  }
-
-  setInformation(true);
-  setVisible();
-  setFocused();
-  onChange();
-}
-
-//set library to show a specific media type, and then show the library
-void LibraryManager::show(const string& type) {
-  unsigned selection = 0;
-  for(auto& browser : browsers) {
-    unsigned mode = 0;
-    for(auto& media : browser->emulator.media) {
-      if(media.bootable && media.type == type) {
-        libraryFrame.setSelection(selection);
-        browser->mediaMode.setSelection(mode);
-        browser->setMode();
-        return show();
-      }
-      mode++;
-    }
-    selection++;
-  }
-}
-
-void LibraryManager::synchronize() {
-  if(libraryFrame.selection() < browsers.size()) {
-    auto& emulator = browsers[libraryFrame.selection()]->emulator;
-    auto& media = emulator.media[browsers[libraryFrame.selection()]->mediaMode.selection()];
-
-    if(requestedLoadType.empty()) {
-      loadButton.setEnabled(media.bootable);
-    } else {
-      bool enabled = (requestedLoadType == media.type);
-      //allow Super Game Boy to load Game Boy Color games
-      if(requestedLoadType == "gb" && loaded.size() == 1 && media.type == "gbc") enabled = true;
-      loadButton.setEnabled(enabled);
-    }
-  } else {
-    loadButton.setEnabled(false);
-  }
-}
diff --git a/target-higan/general/library.hpp b/target-higan/general/library.hpp
deleted file mode 100644
index 7d93f4c9..00000000
--- a/target-higan/general/library.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-struct LibraryBrowser : VerticalLayout {
-  ListView folders;
-  HorizontalLayout informationLayout;
-    Label informationType;
-    Label information;
-    ComboButton mediaMode;
-
-  LibraryBrowser(Emulator::Interface& emulator);
-  void onActivate();
-  void onChange();
-  void refresh();
-  void setMode();
-
-  Emulator::Interface& emulator;
-  string pathname;
-  string type;
-  string typeMask;
-  string typeSuffix;
-};
-
-struct LibraryImport : VerticalLayout {
-  Label information;
-  Button importButton;
-
-  LibraryImport();
-  void onImportActivate();
-};
-
-struct LibraryManager : Window {
-  VerticalLayout layout;
-  TabFrame libraryFrame;
-    vector<LibraryBrowser*> browsers;
-    LibraryImport libraryImport;
-  HorizontalLayout informationLayout;
-    Label information;
-    Button skipButton;
-    Button loadButton;
-
-  LibraryManager();
-  void bootstrap();
-  string load(const string& type);
-  void onChange();
-  void onLoad();
-  void setInformation(bool load);
-  void show();
-  void show(const string& type);
-  void synchronize();
-
-  lstring loaded;
-  string requestedLoadType;
-  bool slotLoad = false;
-  string loadPathname;
-};
-
-extern LibraryManager* libraryManager;
diff --git a/target-higan/general/presentation.cpp b/target-higan/general/presentation.cpp
deleted file mode 100644
index 651a11ac..00000000
--- a/target-higan/general/presentation.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-Presentation* presentation = nullptr;
-
-void Presentation::synchronize() {
-  for(auto& emulator : emulatorList) emulator->menu.setVisible(false);
-  for(auto& emulator : emulatorList) {
-    if(emulator->interface == program->active) {
-      active = emulator;
-      emulator->menu.setVisible(true);
-    }
-  }
-
-  shaderNone.setChecked();
-  if(config->video.shader == "None") shaderNone.setChecked();
-  if(config->video.shader == "Blur") shaderBlur.setChecked();
-  if(config->video.shader == "Display Emulation") shaderEmulation.setChecked();
-  for(auto& shader : shaderList) {
-    string name = notdir(config->video.shader.split<1>(".shader/")(0));
-    if(name == shader->text()) shader->setChecked();
-  }
-
-  switch(config->video.scaleMode) {
-  case 0: centerVideo.setChecked(); break;
-  case 1: scaleVideo.setChecked(); break;
-  case 2: stretchVideo.setChecked(); break;
-  }
-  aspectCorrection.setChecked(config->video.aspectCorrection);
-  maskOverscan.setChecked(config->video.maskOverscan.enable);
-  synchronizeVideo.setChecked(config->video.synchronize);
-  synchronizeAudio.setChecked(config->audio.synchronize);
-  muteAudio.setChecked(config->audio.mute);
-
-  if(program->active == nullptr) {
-    toolsMenu.setVisible(false);
-  } else {
-    toolsMenu.setVisible(true);
-    saveStateMenu.setVisible(system().information.capability.states);
-    loadStateMenu.setVisible(system().information.capability.states);
-    stateMenuSeparator.setVisible(system().information.capability.states);
-    resizeWindow.setVisible(config->video.scaleMode != 2);
-    stateManager.setVisible(system().information.capability.states);
-    cheatEditor.setVisible(system().information.capability.cheats);
-    synchronizeTime.setVisible(system().rtc());
-  }
-}
-
-void Presentation::setSystemName(string name) {
-  if(active) active->menu.setText(systemName = name);
-}
-
-Presentation::Presentation() {
-  bootstrap();
-  loadShaders();
-  setGeometry({256, 256, 720, 480});
-  windowManager->append(this, "Presentation");
-
-  setTitle({::Emulator::Name, " v", ::Emulator::Version});
-  setBackgroundColor({0, 0, 0});
-  setMenuVisible();
-  setStatusVisible();
-
-  setDroppable();
-  viewport.setDroppable();
-
-  loadMenu.setText("Library");
-  settingsMenu.setText("Settings");
-    videoMenu.setText("Video");
-      centerVideo.setText("Center");
-      scaleVideo.setText("Scale");
-      stretchVideo.setText("Stretch");
-      RadioItem::group(centerVideo, scaleVideo, stretchVideo);
-      aspectCorrection.setText("Aspect Correction");
-      maskOverscan.setText("Mask Overscan");
-    shaderMenu.setText("Shader");
-      shaderNone.setText("None");
-      shaderBlur.setText("Blur");
-      shaderEmulation.setText("Display Emulation");
-    synchronizeVideo.setText("Synchronize Video");
-    synchronizeAudio.setText("Synchronize Audio");
-    muteAudio.setText("Mute Audio");
-    configurationSettings.setText("Configuration ...");
-  toolsMenu.setText("Tools");
-    saveStateMenu.setText("Save State");
-      for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n});
-    loadStateMenu.setText("Load State");
-      for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n});
-    resizeWindow.setText("Resize Window");
-    stateManager.setText("State Manager");
-    cheatEditor.setText("Cheat Editor");
-    synchronizeTime.setText("Synchronize Time");
-
-  append(loadMenu);
-    for(auto& item : loadBootableMedia) loadMenu.append(*item);
-  for(auto& systemItem : emulatorList) append(systemItem->menu);
-  append(settingsMenu);
-    settingsMenu.append(videoMenu);
-      videoMenu.append(centerVideo);
-      videoMenu.append(scaleVideo);
-      videoMenu.append(stretchVideo);
-      videoMenu.append(*new Separator);
-      videoMenu.append(aspectCorrection);
-      videoMenu.append(maskOverscan);
-    settingsMenu.append(shaderMenu);
-      shaderMenu.append(shaderNone);
-      shaderMenu.append(shaderBlur);
-      if(config->video.driver == "OpenGL") shaderMenu.append(shaderEmulation);
-      if(shaderList.size() > 0) {
-        shaderMenu.append(*new Separator);
-        for(auto& shader : shaderList) shaderMenu.append(*shader);
-      }
-    settingsMenu.append(*new Separator);
-    settingsMenu.append(synchronizeVideo);
-    settingsMenu.append(synchronizeAudio);
-    settingsMenu.append(muteAudio);
-    if(Intrinsics::platform() != Intrinsics::Platform::MacOSX) {
-      settingsMenu.append(*new Separator);
-      settingsMenu.append(configurationSettings);
-    }
-  append(toolsMenu);
-    toolsMenu.append(saveStateMenu);
-      for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]);
-    toolsMenu.append(loadStateMenu);
-      for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]);
-    toolsMenu.append(stateMenuSeparator);
-    toolsMenu.append(resizeWindow);
-    toolsMenu.append(stateManager);
-    toolsMenu.append(cheatEditor);
-    toolsMenu.append(synchronizeTime);
-
-  append(layout);
-  layout.append(viewport, {0, 0, 1, 1});
-
-  onDrop = viewport.onDrop = [&](lstring paths) {
-    if(paths.size() && directory::exists(paths[0])) {
-      utility->loadMedia(paths[0]);
-      setFocused();
-    }
-  };
-
-  onSize = [&] {
-    utility->resize();
-  };
-
-  onClose = [&] {
-    setVisible(false);
-    if(Intrinsics::platform() == Intrinsics::Platform::MacOSX) {
-      utility->unload();
-    } else {
-      Application::quit();
-    }
-  };
-
-  shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); };
-  shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); };
-  shaderEmulation.onActivate = [&] { config->video.shader = "Display Emulation"; utility->updateShader(); };
-  centerVideo.onActivate  = [&] { config->video.scaleMode = 0; utility->resize(); };
-  scaleVideo.onActivate   = [&] { config->video.scaleMode = 1; utility->resize(); };
-  stretchVideo.onActivate = [&] { config->video.scaleMode = 2; utility->resize(); };
-  aspectCorrection.onToggle = [&] { config->video.aspectCorrection = aspectCorrection.checked(); utility->resize(); };
-  maskOverscan.onToggle = [&] { config->video.maskOverscan.enable = maskOverscan.checked(); };
-  synchronizeVideo.onToggle = [&] { config->video.synchronize = synchronizeVideo.checked(); utility->synchronizeRuby(); };
-  synchronizeAudio.onToggle = [&] { config->audio.synchronize = synchronizeAudio.checked(); utility->synchronizeRuby(); };
-  muteAudio.onToggle = [&] { config->audio.mute = muteAudio.checked(); utility->synchronizeRuby(); };
-  configurationSettings.onActivate = [&] { settings->setVisible(); };
-  for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); };
-  for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); };
-  resizeWindow.onActivate = [&] { utility->resize(true); };
-  stateManager.onActivate = [&] { tools->panels.setSelection(1); tools->setVisible(); };
-  cheatEditor.onActivate = [&] { tools->panels.setSelection(0); tools->setVisible(); };
-  synchronizeTime.onActivate = [&] { system().rtcsync(); };
-
-  synchronize();
-}
-
-void Presentation::bootstrap() {
-  for(auto& emulator : program->emulator) {
-    for(auto& media : emulator->media) {
-      if(media.bootable == false) continue;
-      Item* item = new Item;
-      item->setText({media.name, " ..."});
-      item->onActivate = [=] { libraryManager->show(media.type); };
-      loadBootableMedia.append(item);
-    }
-  }
-
-  for(auto& emulator : program->emulator) {
-    auto iEmulator = new Emulator;
-    iEmulator->interface = emulator;
-
-    iEmulator->menu.setText(emulator->information.name);
-    iEmulator->power.setText("Power");
-    iEmulator->reset.setText("Reset");
-    iEmulator->unload.setText("Unload");
-
-    for(auto& port : emulator->port) {
-      auto iPort = new Emulator::Port;
-      iPort->menu.setText(port.name);
-      iEmulator->port.append(iPort);
-
-      for(auto& device : port.device) {
-        auto iDevice = new RadioItem;
-        iDevice->setText(device.name);
-        iDevice->onActivate = [=] { utility->connect(port.id, device.id); };
-        iPort->group.append(*iDevice);
-        iPort->device.append(iDevice);
-      }
-
-      RadioItem::group(iPort->group);
-    }
-
-    iEmulator->menu.append(iEmulator->power);
-    if(emulator->information.resettable)
-    iEmulator->menu.append(iEmulator->reset);
-    iEmulator->menu.append(*new Separator);
-    unsigned visiblePorts = 0;
-    for(auto& iPort : iEmulator->port) {
-      iEmulator->menu.append(iPort->menu);
-      if(iPort->device.size() <= 1) iPort->menu.setVisible(false);
-      else visiblePorts++;
-      for(auto& iDevice : iPort->device) {
-        iPort->menu.append(*iDevice);
-      }
-    }
-    iEmulator->menu.append(iEmulator->controllerSeparator);
-    if(visiblePorts == 0) iEmulator->controllerSeparator.setVisible(false);
-    iEmulator->menu.append(iEmulator->unload);
-
-    iEmulator->power.onActivate = {&Utility::power, utility};
-    iEmulator->reset.onActivate = {&Utility::reset, utility};
-    iEmulator->unload.onActivate = {&Utility::unload, utility};
-
-    emulatorList.append(iEmulator);
-  }
-}
-
-void Presentation::loadShaders() {
-  //only the OpenGL driver has video shader support
-  if(config->video.driver == "OpenGL") {
-    string pathname = program->path("Video Shaders/");
-    lstring shaders = directory::folders(pathname, "*.shader");
-    for(auto& name : shaders) {
-      auto shader = new RadioItem;
-      shader->setText(name.split<1>(".shader/")(0));
-      shader->onActivate = [=] {
-        config->video.shader = {pathname, name};
-        utility->updateShader();
-      };
-      shaderList.append(shader);
-    }
-  }
-
-  nall::group<RadioItem> group;
-  group.append(shaderNone);
-  group.append(shaderBlur);
-  group.append(shaderEmulation);
-  for(auto& shader : shaderList) group.append(*shader);
-  RadioItem::group(group);
-}
diff --git a/target-higan/general/presentation.hpp b/target-higan/general/presentation.hpp
deleted file mode 100644
index 7bc83326..00000000
--- a/target-higan/general/presentation.hpp
+++ /dev/null
@@ -1,63 +0,0 @@
-struct Presentation : Window {
-  FixedLayout layout;
-  Viewport viewport;
-
-  struct Emulator {
-    ::Emulator::Interface* interface;
-
-    Menu menu;
-      Item power;
-      Item reset;
-      Item unload;
-      Separator controllerSeparator;
-      struct Port {
-        Menu menu;
-        nall::group<RadioItem> group;
-        vector<RadioItem*> device;
-      };
-      vector<Port*> port;
-    function<void (string)> callback;
-  };
-  vector<Emulator*> emulatorList;
-  Emulator* active = nullptr;
-
-  Menu loadMenu;
-    vector<Item*> loadBootableMedia;
-  Menu settingsMenu;
-    Menu videoMenu;
-      RadioItem centerVideo;
-      RadioItem scaleVideo;
-      RadioItem stretchVideo;
-      CheckItem aspectCorrection;
-      CheckItem maskOverscan;
-    Menu shaderMenu;
-      RadioItem shaderNone;
-      RadioItem shaderBlur;
-      RadioItem shaderEmulation;
-      vector<RadioItem*> shaderList;
-    CheckItem synchronizeVideo;
-    CheckItem synchronizeAudio;
-    CheckItem muteAudio;
-    Item configurationSettings;
-  Menu toolsMenu;
-    Menu saveStateMenu;
-      Item saveStateItem[5];
-    Menu loadStateMenu;
-      Item loadStateItem[5];
-    Separator stateMenuSeparator;
-    Item resizeWindow;
-    Item stateManager;
-    Item cheatEditor;
-    Item synchronizeTime;
-
-  void synchronize();
-  void setSystemName(string name);
-  void loadShaders();
-  void bootstrap();
-  Presentation();
-
-//internal:
-  string systemName;
-};
-
-extern Presentation* presentation;
diff --git a/target-higan/higan.cpp b/target-higan/higan.cpp
index 0d8fce73..ee967a88 100644
--- a/target-higan/higan.cpp
+++ b/target-higan/higan.cpp
@@ -1,164 +1,25 @@
 #include "higan.hpp"
-#include "bootstrap.cpp"
-#include "resource/resource.cpp"
 
-Program* program = nullptr;
-DSP dspaudio;
+struct Presentation : Window {
+  MenuBar menuBar{this};
+  Menu menuSystem{&menuBar};
+  StatusBar statusBar{this};
 
-Emulator::Interface& system() {
-  if(program->active == nullptr) throw;
-  return *program->active;
-}
+  Presentation() {
+    menuSystem.setText("System");
+    onClose(&Application::quit);
 
-bool Program::focused() {
-  return config->input.focus.allow || presentation->focused();
-}
-
-string Program::path(string name) {
-  string path = {basepath, name};
-  if(file::exists(path) || directory::exists(path)) return path;
-  path = {userpath, name};
-  if(file::exists(path) || directory::exists(path)) return path;
-  path = {sharedpath, name};
-  if(file::exists(path) || directory::exists(path)) return path;
-  return {userpath, name};
-}
-
-void Program::main() {
-  inputManager->poll();
-  utility->updateStatus();
-  autopause = config->input.focus.pause && presentation->focused() == false;
-
-  if(active == nullptr || system().loaded() == false || pause || autopause) {
-    audio.clear();
-    usleep(20 * 1000);
-    return;
+    setBackgroundColor({0, 0, 0});
+    setTitle({Emulator::Name, " v", Emulator::Version});
+    setSize({640, 480});
+    setCentered();
+    setVisible();
   }
+};
 
-  system().run();
-}
-
-Program::Program(int argc, char** argv) {
-  ananke.open("ananke");
-
-  program = this;
-  pause = false;
-  autopause = false;
-
-  basepath = nall::programpath();
-  userpath = {nall::configpath(), "higan/"};
-  sharedpath = {nall::sharedpath(), "higan/"};
-  directory::create(userpath);
-
-  bootstrap();
-  active = nullptr;
-
-  normalFont = Font::sans(8);
-  boldFont = Font::sans(8, "Bold");
-  titleFont = Font::sans(16, "Bold");
-  monospaceFont = Font::monospace(8);
-
-  config = new ConfigurationSettings;
-  video.driver(config->video.driver);
-  audio.driver(config->audio.driver);
-  input.driver(config->input.driver);
-
-  utility = new Utility;
-  inputManager = new InputManager;
-  windowManager = new WindowManager;
-  libraryManager = new LibraryManager;
-  presentation = new Presentation;
-  dipSwitches = new DipSwitches;
-  videoSettings = new VideoSettings;
-  audioSettings = new AudioSettings;
-  inputSettings = new InputSettings;
-  hotkeySettings = new HotkeySettings;
-  timingSettings = new TimingSettings;
-  serverSettings = new ServerSettings;
-  advancedSettings = new AdvancedSettings;
-  settings = new Settings;
-  cheatDatabase = new CheatDatabase;
-  cheatEditor = new CheatEditor;
-  stateManager = new StateManager;
-  tools = new Tools;
-  windowManager->loadGeometry();
-  presentation->setVisible();
-  utility->resize();
-
-  if(argc == 1 && config->library.showOnStartup) libraryManager->show();
-
-  video.set(Video::Handle, presentation->viewport.handle());
-  if(!video.cap(Video::Depth) || !video.set(Video::Depth, depth = 30u)) {
-    video.set(Video::Depth, depth = 24u);
-  }
-  if(video.init() == false) { video.driver("None"); video.init(); }
-
-  audio.set(Audio::Handle, presentation->viewport.handle());
-  if(audio.init() == false) { audio.driver("None"); audio.init(); }
-
-  input.set(Input::Handle, presentation->viewport.handle());
-  if(input.init() == false) { input.driver("None"); input.init(); }
-
-  dspaudio.setPrecision(16);
-  dspaudio.setBalance(0.0);
-  dspaudio.setFrequency(96000);
-
-  utility->synchronizeRuby();
-  utility->updateShader();
-
-  if(config->video.startFullScreen && argc >= 2) utility->toggleFullScreen();
-  Application::processEvents();
-
-  if(argc >= 2) utility->loadMedia(argv[1]);
-
-  Application::main = {&Program::main, this};
-  Application::run();
-
-  utility->unload();
-  config->save();
-  inputManager->saveConfiguration();
-  windowManager->saveGeometry();
-
-  ananke.close();
-}
-
-int main(int argc, char** argv) {
-  #if defined(PLATFORM_WINDOWS)
-  utf8_args(argc, argv);
-  #endif
-
+#include <nall/main.hpp>
+auto nall::main(lstring args) -> void {
   Application::setName("higan");
-
-  Application::Windows::onModalBegin = [&] {
-    audio.clear();
-  };
-
-  Application::Cocoa::onActivate = [&] {
-    presentation->setVisible();
-  };
-
-  Application::Cocoa::onAbout = [&] {
-    MessageWindow()
-    .setTitle({"About ", Emulator::Name})
-    .setText({
-      Emulator::Name, " v", Emulator::Version, "\n",
-      Emulator::Profile, " Profile\n",
-      "Author: ", Emulator::Author, "\n",
-      "License: ", Emulator::License, "\n",
-      "Website: ", Emulator::Website
-    })
-    .information();
-  };
-
-  Application::Cocoa::onPreferences = [&] {
-    settings->setVisible();
-  };
-
-  Application::Cocoa::onQuit = [&] {
-    Application::quit();
-  };
-
-  new Program(argc, argv);
-  delete program;
-  return 0;
+  new Presentation;
+  Application::run();
 }
diff --git a/target-higan/higan.hpp b/target-higan/higan.hpp
index b9b9a22d..97b63a1f 100644
--- a/target-higan/higan.hpp
+++ b/target-higan/higan.hpp
@@ -1,59 +1,8 @@
 #include <emulator/emulator.hpp>
 
-#include <nall/platform.hpp>
-#include <nall/config.hpp>
-#include <nall/directory.hpp>
-#include <nall/dsp.hpp>
-#include <nall/invoke.hpp>
-#include <nall/map.hpp>
-#include <nall/stream/file.hpp>
-#include <nall/stream/memory.hpp>
-#include <nall/stream/mmap.hpp>
-#include <nall/stream/vector.hpp>
-using namespace nall;
-
-#include <phoenix/phoenix.hpp>
-using namespace phoenix;
-
+#include <nall/nall.hpp>
 #include <ruby/ruby.hpp>
+#include <hiro/hiro.hpp>
+using namespace nall;
 using namespace ruby;
-
-#include "configuration/configuration.hpp"
-#include "interface/interface.hpp"
-#include "utility/utility.hpp"
-#include "input/input.hpp"
-#include "window/window.hpp"
-#include "general/general.hpp"
-#include "settings/settings.hpp"
-#include "tools/tools.hpp"
-#include "resource/resource.hpp"
-
-Emulator::Interface& system();
-
-struct Program {
-  vector<Emulator::Interface*> emulator;
-  Emulator::Interface* active = nullptr;
-  library ananke;
-
-  bool pause;
-  bool autopause;
-  unsigned depth;  //color depth; 24(bpp) or 30(bpp)
-
-  string basepath;
-  string userpath;
-  string sharedpath;
-
-  string normalFont;
-  string boldFont;
-  string titleFont;
-  string monospaceFont;
-
-  bool focused();
-  string path(string filename);
-  void main();
-  void bootstrap();
-  Program(int argc, char** argv);
-};
-
-extern Program* program;
-extern DSP dspaudio;
+using namespace hiro;
diff --git a/target-higan/input/hotkeys.cpp b/target-higan/input/hotkeys.cpp
deleted file mode 100644
index 0df72552..00000000
--- a/target-higan/input/hotkeys.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-void InputManager::appendHotkeys() {
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Toggle Fullscreen Mode";
-    hotkey->mapping = "1/Button/F11";
-
-    hotkey->press = [] {
-      utility->toggleFullScreen();
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Toggle Mouse Capture";
-    hotkey->mapping = "1/Button/F12";
-
-    hotkey->press = [] {
-      input.acquired() ? input.unacquire() : input.acquire();
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name = "Show Library";
-    hotkey->mapping = "1/Button/L";
-
-    hotkey->press = [] {
-      libraryManager->show();
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name = "Pause Emulation";
-    hotkey->mapping = "1/Button/P";
-
-    hotkey->press = [] {
-      program->pause = !program->pause;
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Fast Forward";
-    hotkey->mapping = "1/Button/Tilde";
-
-    hotkey->press = [] {
-      video.set(Video::Synchronize, false);
-      audio.set(Audio::Synchronize, false);
-    };
-
-    hotkey->release = [] {
-      video.set(Video::Synchronize, ::config->video.synchronize);
-      audio.set(Audio::Synchronize, ::config->audio.synchronize);
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Power Cycle";
-    hotkey->mapping = "None";
-
-    hotkey->press = [] {
-      utility->power();
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Soft Reset";
-    hotkey->mapping = "None";
-
-    hotkey->press = [] {
-      utility->reset();
-    };
-  }
-
-  static unsigned activeSlot = 1;
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Save State";
-    hotkey->mapping = "1/Button/F5";
-
-    hotkey->press = [&] {
-      utility->saveState(activeSlot);
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Load State";
-    hotkey->mapping = "1/Button/F7";
-
-    hotkey->press = [&] {
-      utility->loadState(activeSlot);
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Decrement Slot";
-    hotkey->mapping = "1/Button/F6";
-
-    hotkey->press = [&] {
-      if(--activeSlot == 0) activeSlot = 5;
-      utility->showMessage({"Selected slot ", activeSlot});
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Increment Slot";
-    hotkey->mapping = "1/Button/F8";
-
-    hotkey->press = [&] {
-      if(++activeSlot == 6) activeSlot = 1;
-      utility->showMessage({"Selected slot ", activeSlot});
-    };
-  }
-
-  {
-    auto hotkey = new HotkeyInput;
-    hotkey->name    = "Close Emulator";
-    hotkey->mapping = "None";
-
-    hotkey->press = [] {
-      Application::quit();
-    };
-  }
-
-  Configuration::Node node;
-  for(auto& hotkey : hotkeyMap) {
-    node.append(hotkey->mapping, string{hotkey->name}.replace(" ", ""));
-  }
-  config.append(node, "Hotkey");
-}
-
-void InputManager::pollHotkeys() {
-  for(auto& hotkey : hotkeyMap) {
-    bool state = hotkey->poll();
-    if(hotkey->state == 0 && state == 1) if(hotkey->press) hotkey->press();
-    if(hotkey->state == 1 && state == 0) if(hotkey->release) hotkey->release();
-    hotkey->state = state;
-  }
-}
diff --git a/target-higan/input/input.cpp b/target-higan/input/input.cpp
deleted file mode 100644
index bc0083c1..00000000
--- a/target-higan/input/input.cpp
+++ /dev/null
@@ -1,294 +0,0 @@
-#include "../higan.hpp"
-#include "hotkeys.cpp"
-InputManager* inputManager = nullptr;
-HID::Null hidNull;
-
-void AbstractInput::bind() {
-  inputList.reset();
-  lstring list = mapping.split(",");
-
-  for(auto& mapping : list) {
-    lstring values = mapping.split("/");
-    if(values.size() == 1) continue;  //skip "None" mapping
-
-    uint64_t id = hex(values[0]);
-    string group = values(1, "");
-    string input = values(2, "");
-    string qualifier = values(3, "");
-
-    Input item;
-    for(auto device : inputManager->devices) {
-      if(id != device->id) continue;
-      if(group == "Rumble") {
-        item.device = device;
-        item.id = id;
-        item.group = 0;
-        item.input = 0;
-        break;
-      }
-      if(auto groupID = device->find(group)) {
-        if(auto inputID = device->group[groupID()].find(input)) {
-          item.device = device;
-          item.id = id;
-          item.group = groupID();
-          item.input = inputID();
-          item.qualifier = Input::Qualifier::None;
-          if(qualifier == "Lo") item.qualifier = Input::Qualifier::Lo;
-          if(qualifier == "Hi") item.qualifier = Input::Qualifier::Hi;
-          break;
-        }
-      }
-    }
-    if(item.device == nullptr) continue;
-
-    inputList.append(item);
-  }
-}
-
-bool AbstractInput::append(string encode) {
-  lstring mappings = mapping.split(",");
-  if(mappings.find(encode)) return true;  //mapping already bound
-  if(mapping.empty() || mapping == "None") mapping = encode;  //remove "None"
-  else mapping.append(",", encode);  //add to existing mapping list
-  bind();
-  return true;
-}
-
-//
-
-bool DigitalInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
-  if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) {
-    inputList.reset();
-    mapping = "None";
-    return true;
-  }
-
-  string encode = {hex(device.id), "/", device.group[group].name, "/", device.group[group].input[input].name};
-
-  if((device.isKeyboard() && group == HID::Keyboard::GroupID::Button)
-  || (device.isMouse() && group == HID::Mouse::GroupID::Button)
-  || (device.isJoypad() && group == HID::Joypad::GroupID::Button)
-  ) {
-    if(newValue != 0) return append(encode);
-  }
-
-  if((device.isJoypad() && group == HID::Joypad::GroupID::Axis)
-  || (device.isJoypad() && group == HID::Joypad::GroupID::Hat)
-  ) {
-    if(newValue < -16384) return append({encode, "/Lo"});
-    if(newValue > +16384) return append({encode, "/Hi"});
-  }
-
-  return false;
-}
-
-int16_t DigitalInput::poll() {
-  if(program->focused() == false) return 0;
-  if(inputList.size() == 0) return 0;
-  bool result = logic;
-
-  for(auto& item : inputList) {
-    HID::Device& device = *(item.device);
-    int16_t value = device.group[item.group].input[item.input].value;
-    bool output = logic;
-    if((device.isKeyboard() && item.group == HID::Keyboard::GroupID::Button)
-    || (device.isMouse() && item.group == HID::Mouse::GroupID::Button)
-    || (device.isJoypad() && item.group == HID::Joypad::GroupID::Button)
-    ) {
-      output = value;
-    }
-    if((device.isJoypad() && item.group == HID::Joypad::GroupID::Axis)
-    || (device.isJoypad() && item.group == HID::Joypad::GroupID::Hat)
-    ) {
-      if(item.qualifier == Input::Qualifier::Lo) output = value < -16384;
-      if(item.qualifier == Input::Qualifier::Hi) output = value > +16384;
-    }
-    if(logic == 0) result |= output;
-    if(logic == 1) result &= output;
-  }
-
-  return result;
-}
-
-//
-
-bool RelativeInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
-  if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) {
-    inputList.reset();
-    mapping = "None";
-    return true;
-  }
-
-  string encode = {hex(device.id), "/", device.group[group].name, "/", device.group[group].input[input].name};
-
-  if((device.isMouse() && group == HID::Mouse::GroupID::Axis)
-  || (device.isJoypad() && group == HID::Joypad::GroupID::Axis)
-  || (device.isJoypad() && group == HID::Joypad::GroupID::Hat)
-  ) {
-    if(newValue < -16384) return append(encode);
-    if(newValue > +16384) return append(encode);
-  }
-
-  return false;
-}
-
-int16_t RelativeInput::poll() {
-  if(program->focused() == false) return 0;
-  if(inputList.size() == 0) return 0;
-  int16_t result = 0;
-
-  for(auto& item : inputList) {
-    HID::Device& device = *(item.device);
-    int16_t value = device.group[item.group].input[item.input].value;
-    if(device.isJoypad() && item.group == HID::Joypad::GroupID::Axis) value >>= 8;
-    if(device.isJoypad() && item.group == HID::Joypad::GroupID::Hat) value = (value < 0 ? -1 : value > 0 ? + 1 : 0);
-    if(device.isMouse() && input.acquired() == false) value = 0;
-    result += value;
-  }
-
-  return result;
-}
-
-//
-
-bool RumbleInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
-  if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) {
-    inputList.reset();
-    mapping = "None";
-    return true;
-  }
-
-  string encode = {hex(device.id), "/Rumble"};
-
-  if(device.isJoypad() && group == HID::Joypad::GroupID::Button) {
-    if(newValue != 0) return append(encode);
-  }
-
-  return false;
-}
-
-void RumbleInput::rumble(bool enable) {
-  if(program->focused() == false) return;
-  if(inputList.size() == 0) return;
-
-  for(auto& item : inputList) {
-    input.rumble(item.id, enable);
-  }
-}
-
-//
-
-HotkeyInput::HotkeyInput() {
-  logic = 1;  //AND
-  inputManager->hotkeyMap.append(this);
-}
-
-//
-
-//convert an input mapping string to a more human-readable form for the UI
-string InputManager::sanitize(string mapping, string concatenate) const {
-  lstring values = mapping.split(",");
-  for(auto& value : values) {
-    lstring part = value.split("/");
-    if(part.size() < 2) continue;  //skip "None" mapping
-    if(part[0] == "1") part[0] = "Keyboard";
-    else if(part[0] == "2") part[0] = "Mouse";
-    else part[0] = {"Joypad(", part[0].slice(0, 3), ")"};
-    value = part.merge(".");
-  }
-  return values.merge(concatenate);
-}
-
-void InputManager::onChange(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
-  if(settings->focused()) {
-    inputSettings->inputEvent(device, group, input, oldValue, newValue);
-    hotkeySettings->inputEvent(device, group, input, oldValue, newValue);
-  }
-}
-
-HID::Device* InputManager::findMouse() {
-  for(auto device : devices) {
-    if(device->isMouse()) return device;
-  }
-  return nullptr;
-}
-
-void InputManager::bind() {
-  for(auto& input : inputMap) input->bind();
-  for(auto& input : hotkeyMap) input->bind();
-}
-
-void InputManager::poll() {
-  auto devices = input.poll();
-  bool changed = devices.size() != this->devices.size();
-  if(changed == false) {
-    for(unsigned n = 0; n < devices.size(); n++) {
-      changed = devices[n] != this->devices[n];
-      if(changed) break;
-    }
-  }
-  if(changed == true) {
-    this->devices = devices;
-    bind();
-  }
-
-  if(presentation->focused()) pollHotkeys();
-}
-
-void InputManager::saveConfiguration() {
-  config.save(program->path("input.bml"));
-}
-
-InputManager::InputManager() {
-  inputManager = this;
-  bootstrap();
-
-  input.onChange = {&InputManager::onChange, this};
-}
-
-void InputManager::bootstrap() {
-  unsigned guid = 0;
-  for(auto& emulator : program->emulator) {
-    Configuration::Node emulatorNode;
-
-    for(auto& port : emulator->port) {
-      Configuration::Node portNode;
-
-      for(auto& device : port.device) {
-        Configuration::Node deviceNode;
-
-        for(auto& number : device.order) {
-          auto& input = device.input[number];
-
-          AbstractInput* abstract = nullptr;
-          if(input.type == 0) abstract = new DigitalInput;
-          if(input.type == 1) abstract = new RelativeInput;
-          if(input.type == 2) abstract = new RumbleInput;
-          if(abstract == nullptr) continue;
-
-          abstract->name = string{input.name}.replace(" ", "");
-          abstract->mapping = "None";
-          abstract->logic = 0;  //OR
-
-          input.guid = guid++;
-          inputMap.append(abstract);
-
-          deviceNode.append(abstract->mapping, abstract->name);
-        }
-
-        portNode.append(deviceNode, string{device.name}.replace(" ", ""));
-      }
-
-      emulatorNode.append(portNode, string{port.name}.replace(" ", ""));
-    }
-
-    config.append(emulatorNode, string{emulator->information.name}.replace(" ", ""));
-  }
-
-  appendHotkeys();
-
-  config.load(program->path("input.bml"));
-  config.save(program->path("input.bml"));
-
-  bind();
-}
diff --git a/target-higan/input/input.hpp b/target-higan/input/input.hpp
deleted file mode 100644
index 7466b5a2..00000000
--- a/target-higan/input/input.hpp
+++ /dev/null
@@ -1,71 +0,0 @@
-extern HID::Null hidNull;
-
-struct AbstractInput {
-  string name;
-  string mapping;
-  bool logic = 0;  //0 = OR, 1 = AND
-  bool state = 0;
-
-  struct Input {
-    HID::Device* device = nullptr;
-    uint64_t id = 0;
-    unsigned group = 0;
-    unsigned input = 0;
-    enum class Qualifier : unsigned { None, Lo, Hi } qualifier;
-  };
-  vector<Input> inputList;
-
-  void bind();
-  bool append(string mapping);
-  virtual bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) { return false; }
-  virtual int16_t poll() { return 0; }
-  virtual void rumble(bool enable) {}
-};
-
-struct DigitalInput : AbstractInput {
-  using AbstractInput::bind;
-  bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
-  int16_t poll();
-};
-
-struct RelativeInput : AbstractInput {
-  using AbstractInput::bind;
-  bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
-  int16_t poll();
-};
-
-struct RumbleInput : AbstractInput {
-  using AbstractInput::bind;
-  bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
-  void rumble(bool enable);
-};
-
-struct HotkeyInput : DigitalInput {
-  function<void ()> press;
-  function<void ()> release;
-  HotkeyInput();
-};
-
-struct InputManager {
-  vector<HID::Device*> devices;
-  vector<AbstractInput*> inputMap;
-  vector<HotkeyInput*> hotkeyMap;
-
-  string sanitize(string mapping, string concatenate) const;
-  void onChange(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
-  HID::Device* findMouse();
-  void bind();
-  void poll();
-  void saveConfiguration();
-  void bootstrap();
-  InputManager();
-
-  //hotkeys.cpp
-  void appendHotkeys();
-  void pollHotkeys();
-
-private:
-  Configuration::Document config;
-};
-
-extern InputManager* inputManager;
diff --git a/target-higan/interface/interface.cpp b/target-higan/interface/interface.cpp
deleted file mode 100644
index c48e7f96..00000000
--- a/target-higan/interface/interface.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "../higan.hpp"
-Interface* interface = nullptr;
-
-void Interface::loadRequest(unsigned id, string name, string type) {
-  return utility->loadRequest(id, name, type);
-}
-
-void Interface::loadRequest(unsigned id, string path) {
-  return utility->loadRequest(id, path);
-}
-
-void Interface::saveRequest(unsigned id, string path) {
-  return utility->saveRequest(id, path);
-}
-
-uint32_t Interface::videoColor(unsigned source, uint16_t a, uint16_t r, uint16_t g, uint16_t b) {
-  if(config->video.shader != "Display Emulation") {
-    if(config->video.saturation != 100) {
-      uint16_t grayscale = uclamp<16>((r + g + b) / 3);
-      double saturation = config->video.saturation * 0.01;
-      double inverse = max(0.0, 1.0 - saturation);
-      r = uclamp<16>(r * saturation + grayscale * inverse);
-      g = uclamp<16>(g * saturation + grayscale * inverse);
-      b = uclamp<16>(b * saturation + grayscale * inverse);
-    }
-
-    if(config->video.gamma != 100) {
-      double exponent = config->video.gamma * 0.01;
-      double reciprocal = 1.0 / 32767.0;
-      r = r > 32767 ? r : 32767 * pow(r * reciprocal, exponent);
-      g = g > 32767 ? g : 32767 * pow(g * reciprocal, exponent);
-      b = b > 32767 ? b : 32767 * pow(b * reciprocal, exponent);
-    }
-
-    if(config->video.luminance != 100) {
-      double luminance = config->video.luminance * 0.01;
-      r = r * luminance;
-      g = g * luminance;
-      b = b * luminance;
-    }
-  }
-
-  if(program->depth == 30) {
-    a >>= 14, r >>= 6, g >>= 6, b >>= 6;
-    return a << 30 | r << 20 | g << 10 | b << 0;
-  }
-
-  if(program->depth == 24) {
-    a >>= 8, r >>= 8, g >>= 8, b >>= 8;
-    return a << 24 | r << 16 | g << 8 | b << 0;
-  }
-
-  return 0u;
-}
-
-void Interface::videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) {
-  uint32_t* output;
-  unsigned outputPitch;
-
-  if(video.lock(output, outputPitch, width, height)) {
-    pitch >>= 2, outputPitch >>= 2;
-
-    for(unsigned y = 0; y < height; y++) {
-      const uint32_t* sp = data + y * pitch;
-      uint32_t* dp = output + y * outputPitch;
-      for(unsigned x = 0; x < width; x++) {
-        *dp++ = palette[*sp++];
-      }
-    }
-
-    if(system().information.overscan && config->video.maskOverscan.enable) {
-      unsigned h = config->video.maskOverscan.horizontal;
-      unsigned v = config->video.maskOverscan.vertical;
-
-      if(h) for(unsigned y = 0; y < height; y++) {
-        memset(output + y * outputPitch, 0, 4 * h);
-        memset(output + y * outputPitch + (width - h), 0, 4 * h);
-      }
-
-      if(v) for(unsigned y = 0; y < v; y++) {
-        memset(output + y * outputPitch, 0, 4 * width);
-        memset(output + (height - 1 - y) * outputPitch, 0, 4 * width);
-      }
-    }
-
-    video.unlock();
-    video.refresh();
-  }
-
-  static unsigned frameCounter = 0;
-  static time_t previous, current;
-  frameCounter++;
-
-  time(&current);
-  if(current != previous) {
-    previous = current;
-    utility->setStatusText({"FPS: ", frameCounter});
-    frameCounter = 0;
-  }
-}
-
-void Interface::audioSample(int16_t lsample, int16_t rsample) {
-  signed samples[] = {lsample, rsample};
-  dspaudio.sample(samples);
-  while(dspaudio.pending()) {
-    dspaudio.read(samples);
-    audio.sample(samples[0], samples[1]);
-  }
-}
-
-int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
-  unsigned guid = system().port[port].device[device].input[input].guid;
-  return inputManager->inputMap[guid]->poll();
-}
-
-void Interface::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) {
-  unsigned guid = system().port[port].device[device].input[input].guid;
-  return inputManager->inputMap[guid]->rumble(enable);
-}
-
-unsigned Interface::dipSettings(const Markup::Node& node) {
-  return dipSwitches->run(node);
-}
-
-string Interface::path(unsigned group) {
-  return utility->path(group);
-}
-
-string Interface::server() {
-  return {
-    config->server.username, ":",
-    config->server.password, "@",
-    config->server.hostname
-  };
-}
-
-void Interface::notify(string text) {
-  MessageWindow().setParent(*presentation).setText(text).information();
-}
diff --git a/target-higan/interface/interface.hpp b/target-higan/interface/interface.hpp
deleted file mode 100644
index 485c831f..00000000
--- a/target-higan/interface/interface.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-struct Interface : Emulator::Interface::Bind {
-  void loadRequest(unsigned id, string name, string type);
-  void loadRequest(unsigned id, string path);
-  void saveRequest(unsigned id, string path);
-  uint32_t videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue);
-  void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height);
-  void audioSample(int16_t lsample, int16_t rsample);
-  int16_t inputPoll(unsigned port, unsigned device, unsigned input);
-  void inputRumble(unsigned port, unsigned device, unsigned input, bool enable);
-  unsigned dipSettings(const Markup::Node& node);
-  string path(unsigned group);
-  string server();
-  void notify(string text);
-};
-
-extern Interface* interface;
diff --git a/target-higan/resource.rc b/target-higan/resource.rc
deleted file mode 100644
index 49441758..00000000
--- a/target-higan/resource.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-1 24 "../data/higan.Manifest"
-2 ICON DISCARDABLE "../data/higan.ico"
diff --git a/target-higan/resource/cheat-editor.png b/target-higan/resource/cheat-editor.png
deleted file mode 100644
index c8d899cf..00000000
Binary files a/target-higan/resource/cheat-editor.png and /dev/null differ
diff --git a/target-higan/resource/folder.png b/target-higan/resource/folder.png
deleted file mode 100644
index 472484f1..00000000
Binary files a/target-higan/resource/folder.png and /dev/null differ
diff --git a/target-higan/resource/game.png b/target-higan/resource/game.png
deleted file mode 100644
index 1990dbb8..00000000
Binary files a/target-higan/resource/game.png and /dev/null differ
diff --git a/target-higan/resource/resource.bml b/target-higan/resource/resource.bml
deleted file mode 100644
index e9a78611..00000000
--- a/target-higan/resource/resource.bml
+++ /dev/null
@@ -1,15 +0,0 @@
-resource name=resource
-  binary id=advanced name=advanced.png
-  binary id=audio name=audio.png
-  binary id=cheatEditor name=cheat-editor.png
-  binary id=folder name=folder.png
-  binary id=game name=game.png
-  binary id=home name=home.png
-  binary id=hotkeys name=hotkeys.png
-  binary id=input name=input.png
-  binary id=server name=server.png
-  binary id=stateManager name=state-manager.png
-  binary id=timing name=timing.png
-  binary id=unverified name=unverified.png
-  binary id=up name=up.png
-  binary id=video name=video.png
diff --git a/target-higan/resource/resource.cpp b/target-higan/resource/resource.cpp
deleted file mode 100644
index 1f84fc61..00000000
--- a/target-higan/resource/resource.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-namespace resource {
-
-const uint8_t advanced[611] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
-  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,9,26,12,19,57,139,99,194,72,0,0,1,240,73,
-  68,65,84,56,203,165,146,75,104,19,97,20,133,191,153,248,76,138,77,20,10,130,74,176,204,198,133,182,221,69,172,46,
-  52,20,98,102,235,66,112,33,84,164,168,181,45,18,82,2,21,3,67,66,160,107,65,116,101,64,138,226,194,137,141,208,
-  98,87,130,210,54,137,73,23,21,130,85,105,211,151,134,32,68,68,58,51,110,102,134,49,29,235,194,179,251,185,231,156,
-  123,239,185,63,252,39,4,231,35,157,81,162,128,10,200,241,88,34,247,23,77,52,157,81,108,142,216,82,84,251,175,246,
-  3,168,166,153,171,248,250,181,1,205,108,180,29,233,140,98,52,26,117,35,157,81,12,247,90,195,170,69,1,196,116,70,
-  137,90,100,171,171,215,219,230,102,28,5,120,240,240,62,241,88,66,6,114,0,34,160,14,222,28,50,158,62,155,48,0,
-  245,246,173,17,54,215,215,144,58,37,234,245,175,247,90,179,137,221,25,21,44,49,128,96,21,111,12,12,26,154,182,37,
-  232,186,110,232,186,70,97,126,78,40,188,47,2,200,59,5,43,180,116,176,17,233,187,72,185,82,97,185,246,5,32,25,
-  143,37,238,254,243,140,78,188,202,191,52,74,149,18,221,39,123,40,150,11,152,19,76,2,186,171,65,48,146,58,12,212,
-  172,247,149,144,78,215,9,137,163,71,142,209,108,54,121,55,247,22,183,53,60,78,241,248,112,152,190,80,39,29,1,47,
-  217,153,77,142,183,127,75,46,125,94,58,215,125,170,135,128,63,192,74,109,249,242,133,240,249,249,233,169,215,85,192,0,
-  16,131,145,84,23,80,123,52,38,51,91,94,100,182,188,72,199,65,31,0,230,222,114,46,255,2,73,146,56,123,186,215,
-  250,100,118,30,34,80,28,31,14,51,245,166,196,150,166,35,238,243,179,81,111,218,35,154,35,203,217,39,143,57,208,238,
-  231,76,168,23,96,204,105,0,192,238,93,30,246,248,14,241,243,199,47,178,249,5,128,112,171,73,46,175,82,253,88,253,
-  35,108,17,96,101,227,59,251,125,1,218,246,122,120,62,243,1,32,252,105,114,116,218,73,52,77,146,107,235,171,11,64,
-  210,190,66,48,146,186,4,76,56,184,219,196,59,225,55,55,226,213,246,234,188,84,188,0,0,0,0,73,69,78,68,174,
-  66,96,130,
-};
-
-const uint8_t audio[592] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
-  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,1,226,73,68,65,84,56,141,
-  165,147,191,79,83,81,20,199,63,231,246,245,165,68,168,3,193,201,63,64,99,66,4,139,128,113,100,241,71,88,40,88,
-  162,46,36,242,106,58,233,0,127,132,49,93,173,12,13,137,160,169,105,2,196,133,196,168,131,131,137,16,23,69,172,147,
-  58,176,184,152,247,76,244,93,222,61,14,198,218,22,140,65,191,227,57,159,156,123,190,223,123,175,168,42,255,35,243,167,
-  70,126,58,159,11,130,32,253,79,3,10,133,137,193,20,242,56,142,227,212,129,7,20,10,19,131,105,63,243,84,85,179,
-  173,245,161,224,94,112,122,118,121,172,147,247,46,77,79,134,64,198,243,188,175,168,222,73,251,153,235,51,51,51,217,202,
-  221,74,210,10,58,167,239,141,104,53,23,60,236,223,168,76,126,105,110,32,72,166,124,187,236,205,207,205,31,86,152,43,
-  149,74,217,209,145,51,120,158,7,64,110,118,169,56,114,109,233,232,230,194,213,39,10,235,104,124,179,205,130,162,244,245,
-  29,225,248,177,19,168,170,12,156,60,69,38,211,133,136,0,32,66,111,34,148,1,68,101,69,144,209,61,25,188,217,122,
-  205,187,198,91,0,26,141,109,182,183,183,176,214,2,144,114,44,2,99,34,136,26,247,10,116,72,4,105,102,0,16,134,
-  97,115,101,187,107,81,126,191,141,93,163,137,168,164,0,172,209,196,79,164,45,120,3,16,70,33,97,20,162,170,216,216,
-  98,99,219,4,4,83,64,244,153,42,234,37,169,1,96,83,91,78,48,0,81,20,177,179,179,3,192,135,79,31,137,109,
-  220,26,191,167,164,111,252,132,245,130,194,139,182,107,84,85,173,215,235,137,136,88,35,102,113,117,117,229,242,248,197,241,
-  238,95,192,203,133,43,183,0,134,139,247,71,129,60,98,250,59,45,244,168,106,183,115,174,123,121,233,65,209,126,79,206,
-  173,61,90,139,156,115,109,94,93,226,134,65,139,27,149,233,207,173,117,217,239,51,77,77,77,157,197,184,245,67,93,61,
-  189,213,106,245,219,30,160,51,196,78,213,106,181,231,56,115,222,247,253,100,191,254,95,55,56,136,126,0,228,148,200,42,
-  201,231,90,24,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t cheatEditor[937] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
-  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,11,28,23,56,34,56,174,182,224,0,0,3,54,73,
-  68,65,84,56,203,93,147,75,76,92,117,20,198,191,255,99,230,222,121,128,115,65,96,102,128,162,19,4,154,73,83,19,
-  23,197,87,221,24,74,210,141,169,85,27,31,171,54,193,84,107,213,210,102,72,67,26,98,162,13,148,88,155,52,172,76,
-  136,143,133,27,92,17,18,187,168,166,52,93,24,181,117,12,29,168,52,242,152,41,101,24,198,14,51,112,231,206,253,255,
-  143,27,192,73,191,213,57,223,201,247,203,73,78,14,195,182,70,70,47,246,2,248,206,52,205,135,167,62,248,56,142,42,
-  141,140,94,28,147,82,190,245,201,233,254,186,106,127,122,28,239,241,157,70,8,49,241,225,201,211,150,109,219,139,87,199,
-  174,12,238,248,151,191,26,205,28,234,233,237,3,112,231,177,240,203,0,190,222,5,40,165,204,123,243,179,252,196,241,190,
-  30,165,212,126,0,24,190,244,197,43,209,230,104,164,88,218,96,90,235,238,170,112,59,227,98,146,49,225,217,5,112,206,
-  75,11,11,255,192,52,12,86,46,151,227,87,199,174,12,74,41,175,197,98,49,172,60,88,209,90,235,249,237,176,197,133,
-  184,222,209,253,105,80,120,106,84,245,6,61,169,84,10,203,233,37,118,226,120,95,167,109,219,239,119,117,117,121,24,56,
-  230,239,207,3,192,15,211,227,240,112,33,166,90,227,71,155,234,90,14,49,0,216,5,156,235,31,184,197,57,95,156,155,
-  155,133,105,152,204,10,89,81,210,64,118,109,85,3,152,60,123,38,241,25,23,98,188,190,249,192,254,104,199,155,30,80,
-  9,68,244,63,0,0,180,214,71,102,238,206,208,234,234,10,92,229,98,99,163,128,100,50,233,0,24,186,249,13,63,239,
-  15,181,189,22,123,238,148,233,22,39,161,221,13,0,143,1,206,158,73,252,198,57,255,118,118,46,165,67,161,16,50,15,
-  50,68,68,227,207,215,39,158,242,120,107,207,119,118,15,248,221,210,52,42,91,139,208,202,6,136,192,170,1,19,225,240,
-  168,75,116,210,205,231,77,184,46,158,14,55,33,77,122,51,120,208,246,190,112,105,64,74,190,134,98,238,6,12,211,132,
-  55,244,6,110,255,148,80,2,0,38,34,145,193,35,149,202,245,78,203,234,14,9,33,149,114,169,236,58,168,11,212,176,
-  70,211,240,24,203,38,255,117,120,10,158,198,28,178,91,119,17,106,108,2,247,182,99,245,254,207,196,127,140,70,223,117,
-  114,185,161,206,112,88,207,44,45,93,43,108,109,58,249,194,6,247,29,214,45,66,50,252,107,151,80,136,153,104,111,168,
-  71,50,49,135,252,148,68,197,113,160,221,50,8,4,238,58,206,240,190,214,86,74,45,47,95,56,166,84,111,173,233,83,
-  194,231,83,141,175,243,239,255,206,172,32,24,12,32,254,101,3,242,47,173,185,123,163,17,42,222,240,66,85,42,112,221,
-  77,128,8,28,229,114,189,227,56,234,152,82,159,79,6,2,151,61,82,154,194,66,193,111,181,31,8,61,187,7,126,146,
-  184,55,244,23,204,87,183,36,24,49,93,208,112,29,7,170,178,9,128,32,225,245,102,130,126,255,158,219,109,109,101,173,
-  181,156,89,75,23,155,47,60,225,111,217,123,212,8,156,251,5,201,143,254,64,199,157,48,42,191,215,64,107,130,172,149,
-  80,218,221,6,0,226,109,203,202,165,215,215,95,92,119,28,153,103,196,194,253,90,60,115,248,29,131,115,15,140,134,39,
-  97,68,235,144,94,200,34,155,45,224,145,87,33,62,114,16,190,72,12,68,12,143,30,254,73,172,234,65,246,1,184,197,
-  152,52,133,244,59,0,1,68,219,83,218,61,53,237,212,68,80,202,246,253,7,17,166,115,66,199,238,239,170,0,0,0,
-  0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t folder[1176] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
-  244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
-  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
-  97,112,101,46,111,114,103,155,238,60,26,0,0,4,21,73,68,65,84,88,133,237,151,61,111,28,69,24,199,127,51,187,
-  119,123,175,246,57,145,48,9,5,86,148,194,31,0,9,5,33,83,110,149,130,26,137,2,90,58,62,64,36,62,64,74,
-  132,68,133,104,161,67,72,167,64,23,201,138,2,138,210,69,194,198,196,40,22,198,247,186,119,222,151,153,157,25,138,123,
-  201,58,119,142,19,114,82,40,248,75,143,118,118,118,247,121,126,243,60,51,179,187,34,12,67,94,167,228,107,141,254,95,
-  0,240,1,110,220,184,225,43,165,126,54,198,188,119,209,3,66,136,220,90,251,249,195,135,15,191,92,25,64,154,166,91,
-  149,74,229,157,155,55,111,250,73,146,224,156,3,192,90,11,48,63,159,182,253,59,119,238,220,222,222,222,254,254,209,163,
-  71,199,43,1,80,74,101,213,106,213,29,29,29,209,233,116,112,206,45,53,0,33,4,155,155,155,249,193,193,193,71,192,
-  237,149,0,104,173,211,44,203,196,165,75,27,24,99,176,214,158,9,92,204,132,16,130,106,181,90,63,60,60,252,98,123,
-  123,251,214,172,127,153,156,115,218,90,251,241,222,222,222,143,207,5,0,178,44,203,188,78,167,75,167,211,153,3,204,142,
-  179,32,197,64,91,91,91,245,70,163,65,179,217,68,8,129,16,2,0,41,229,188,29,69,17,247,238,221,187,5,60,31,
-  224,242,229,203,105,191,223,247,54,54,90,104,173,23,0,150,149,98,166,56,142,1,230,16,69,139,227,24,99,76,237,188,
-  224,115,128,221,221,93,117,237,218,53,217,235,245,230,25,0,150,130,204,178,81,212,108,196,197,224,82,202,25,64,229,66,
-  0,0,99,140,109,54,215,100,154,102,220,63,180,220,221,63,27,228,188,58,63,171,25,204,228,153,22,206,93,185,206,219,
-  31,232,226,61,158,39,190,93,255,253,155,79,206,0,56,231,116,183,219,245,187,221,46,143,59,13,62,253,240,93,174,191,
-  181,81,112,54,61,62,47,250,18,200,105,207,60,206,147,206,152,175,190,187,255,254,236,188,152,1,221,104,212,171,90,107,
-  212,31,146,102,181,204,254,223,99,162,56,159,140,108,233,104,207,35,17,11,77,1,52,42,62,42,211,8,65,103,118,121,
-  190,21,27,99,116,191,63,160,219,237,18,103,150,122,181,132,53,147,27,228,212,193,179,134,59,207,220,83,179,19,179,214,
-  145,100,134,52,203,193,137,249,6,86,44,65,86,169,4,180,90,45,50,35,168,5,37,172,83,120,114,53,175,139,113,154,
-  83,111,250,140,134,57,214,186,39,11,0,121,158,171,40,26,209,239,15,208,230,13,170,129,143,115,14,79,158,155,231,23,
-  147,131,81,170,209,185,37,240,37,113,170,173,49,249,209,2,128,49,38,43,151,75,84,155,27,172,213,202,56,64,10,177,
-  188,248,47,40,227,28,163,84,147,91,135,231,9,130,178,199,105,146,41,33,196,201,2,128,181,54,27,141,198,28,15,20,
-  235,245,75,232,220,190,210,232,51,109,137,51,3,78,224,79,253,148,61,201,232,84,229,130,167,147,176,152,129,212,247,125,
-  188,32,160,73,153,76,217,11,131,76,230,152,195,186,201,62,97,236,196,84,110,151,173,72,156,131,193,40,181,185,115,139,
-  0,214,218,100,60,30,211,25,248,84,90,101,162,68,147,233,233,75,8,200,141,69,27,135,206,45,185,153,110,203,47,153,
-  149,68,25,134,227,196,19,210,91,90,130,84,8,129,12,26,148,74,62,163,68,19,37,154,84,25,148,182,47,29,108,153,
-  226,44,231,52,209,62,231,148,32,142,227,83,122,81,133,74,13,30,159,196,12,99,189,212,209,191,145,0,134,177,66,25,
-  83,250,245,234,94,47,124,252,12,128,181,54,6,129,245,107,100,185,37,138,53,74,95,60,15,94,84,82,10,78,134,9,
-  82,200,56,220,221,157,59,62,51,7,146,36,33,74,12,87,175,4,84,3,143,160,188,186,111,86,41,4,105,170,144,82,
-  12,139,253,197,18,36,0,154,18,235,85,143,122,105,21,85,127,42,231,28,253,36,65,88,59,88,0,104,183,219,98,103,
-  103,199,68,177,98,112,234,248,243,100,136,236,190,226,14,184,68,199,199,67,155,36,195,7,187,237,118,45,12,195,120,14,
-  0,120,81,20,253,244,203,161,248,76,173,227,255,182,255,202,31,187,103,228,192,2,88,157,30,28,63,248,225,235,226,53,
-  49,251,53,107,183,219,205,96,109,237,205,90,208,218,116,56,41,133,92,201,18,200,141,49,42,75,107,185,16,121,62,58,
-  233,1,127,133,97,216,91,0,152,66,72,32,96,146,153,124,21,0,83,5,64,10,168,48,12,207,44,45,241,255,207,233,
-  235,6,248,7,188,50,165,151,203,8,55,43,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t game[1490] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
-  244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,5,137,73,68,65,84,88,133,237,150,91,108,20,
-  231,21,199,127,231,155,155,151,181,124,89,3,181,113,193,183,13,16,81,82,21,225,52,50,1,132,91,53,20,212,132,155,
-  212,84,149,18,161,40,82,213,151,72,68,81,108,212,74,121,168,122,81,165,190,24,171,125,141,218,226,66,171,66,91,37,
-  65,145,82,225,2,9,6,148,132,40,73,9,55,219,4,112,146,58,49,23,239,125,102,190,62,204,236,120,236,176,182,1,
-  169,79,29,233,232,236,203,156,255,239,252,207,249,190,29,248,255,115,31,79,182,135,157,153,94,116,102,47,91,239,181,134,
-  186,31,113,173,216,159,173,249,10,90,243,215,76,47,91,254,103,0,217,30,118,106,225,143,183,219,215,219,250,225,103,201,
-  180,173,179,53,28,186,23,136,187,6,8,58,151,253,147,233,13,142,151,106,97,226,76,63,238,194,54,50,237,143,222,19,
-  132,220,173,56,134,57,144,95,243,3,43,111,8,147,31,253,3,180,7,98,80,189,226,123,152,159,95,102,193,197,227,69,
-  129,237,201,95,240,234,124,106,206,219,129,108,47,59,48,204,1,111,211,139,150,145,254,22,217,11,175,160,68,163,148,66,
-  137,38,123,225,21,252,133,105,114,233,245,129,19,123,249,238,124,234,206,203,129,108,47,59,180,48,224,111,124,193,150,150,
-  46,138,19,23,64,107,4,184,253,225,1,220,155,163,160,65,139,34,153,222,140,57,62,76,213,197,227,69,17,182,37,127,
-  206,107,179,213,158,211,129,64,92,6,178,15,108,180,189,134,86,74,19,23,17,64,68,16,17,204,68,93,224,130,161,80,
-  2,185,203,175,227,54,180,145,79,175,179,181,230,240,92,78,204,234,64,36,158,126,212,246,83,203,64,187,56,141,107,65,
-  160,52,126,142,252,245,147,248,185,207,167,94,208,58,72,8,137,214,110,140,241,17,170,46,189,57,171,19,21,29,200,246,
-  176,93,139,12,228,210,235,108,63,245,85,242,163,255,68,187,249,168,115,63,51,6,133,137,160,251,40,12,68,41,148,8,
-  249,43,71,241,26,150,145,111,127,196,22,164,226,78,220,17,32,219,195,118,95,228,79,217,116,151,237,213,53,147,191,50,
-  136,8,129,128,8,34,10,17,65,41,99,70,168,169,12,20,174,30,195,91,216,66,182,173,211,169,4,241,37,128,178,120,
-  174,227,17,219,175,91,66,225,234,49,20,160,68,161,172,5,80,158,125,205,82,204,228,226,105,226,18,101,21,65,20,175,
-  158,192,75,45,37,219,182,214,17,228,208,100,15,155,43,2,100,122,217,22,136,63,108,251,117,77,20,174,190,137,8,72,
-  185,168,83,27,117,111,47,92,73,242,107,79,97,47,94,61,189,243,16,72,194,113,136,64,113,108,8,175,174,153,108,235,
-  26,71,41,57,28,135,136,0,244,75,40,224,80,177,190,209,102,201,215,41,140,157,14,108,151,169,249,154,53,205,136,72,
-  232,66,0,34,165,201,169,217,71,89,97,88,85,88,245,29,40,195,10,156,248,236,29,212,3,143,81,168,89,236,136,76,
-  45,100,4,32,47,225,227,179,205,158,248,164,40,215,222,193,105,90,19,21,20,81,24,201,69,40,59,137,136,160,115,227,
-  228,222,221,71,238,221,126,252,204,245,72,220,176,18,209,8,140,154,165,44,88,190,29,179,182,5,49,76,18,45,221,56,
-  185,12,206,237,255,20,181,158,218,133,105,35,72,254,138,191,9,250,251,137,225,51,69,243,198,117,236,198,111,32,202,68,
-  148,194,94,180,122,234,4,220,188,140,200,116,203,205,234,38,18,15,61,131,149,90,142,89,221,136,149,90,142,8,36,58,
-  182,80,219,245,83,18,84,33,39,250,139,190,231,63,81,253,75,142,68,141,223,233,20,100,122,217,134,200,129,98,251,90,
-  219,79,53,225,223,184,68,98,245,110,148,225,128,8,197,243,127,70,231,198,131,113,68,173,88,56,171,118,163,37,236,73,
-  107,52,160,18,13,232,145,33,244,241,254,146,246,220,199,227,226,21,1,166,65,164,59,109,107,197,86,84,162,1,9,174,
-  64,40,78,226,126,252,6,160,49,106,59,240,198,223,195,88,210,133,170,237,8,133,53,104,144,170,122,244,232,16,254,177,
-  59,139,207,10,80,134,208,168,131,170,251,121,75,45,93,131,206,127,17,118,45,248,55,47,66,225,38,102,227,90,180,214,
-  81,169,224,183,6,39,20,255,215,190,138,226,115,2,64,120,47,24,230,65,99,211,30,83,39,171,241,174,31,199,168,107,
-  71,223,56,15,94,1,107,229,15,65,153,83,215,176,214,96,215,161,71,79,226,205,33,62,47,128,50,132,54,140,3,254,
-  242,78,75,234,27,17,237,66,241,22,214,146,46,164,174,131,80,25,173,65,236,26,252,209,33,188,193,190,146,214,238,19,
-  115,253,27,206,247,131,196,248,108,15,59,146,85,242,7,86,126,211,182,59,127,140,246,242,168,240,78,40,3,96,85,227,
-  141,156,194,27,236,43,77,228,220,93,205,191,225,53,192,3,252,138,133,103,17,85,128,13,84,1,206,175,223,98,244,201,
-  85,92,72,101,174,109,241,201,24,102,115,39,248,110,112,52,145,72,188,52,216,231,190,255,169,251,244,131,253,28,5,204,
-  80,163,124,220,191,4,82,9,192,2,156,153,241,219,51,92,249,118,27,151,155,115,195,223,145,69,29,134,170,95,134,248,
-  46,88,73,252,145,33,74,131,125,238,209,97,247,71,221,47,115,44,38,94,6,80,4,142,135,91,90,25,192,8,1,236,
-  88,68,16,47,159,229,90,103,147,30,110,187,117,170,91,26,90,149,212,183,226,143,156,164,56,216,231,30,254,183,187,103,
-  215,65,222,10,197,227,162,241,81,107,130,177,204,10,96,134,16,102,8,80,6,114,0,231,192,7,140,173,72,249,87,86,
-  76,158,218,160,111,127,170,74,111,255,197,251,253,89,247,39,207,252,157,211,49,209,184,160,31,134,23,203,21,1,252,24,
-  189,17,203,101,48,3,176,14,159,99,172,165,198,191,182,218,26,89,255,187,51,254,207,158,59,194,233,25,66,238,140,40,
-  197,34,26,193,108,167,160,188,7,118,44,199,195,138,1,150,187,44,11,20,103,68,33,150,167,45,226,124,142,97,121,28,
-  54,211,71,83,22,143,47,150,23,235,62,14,51,173,235,187,5,184,211,59,113,241,114,141,153,179,158,215,243,95,119,198,
-  63,107,9,247,71,127,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t home[606] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
-  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,10,14,20,37,19,83,42,210,59,0,0,1,235,73,
-  68,65,84,56,203,149,147,191,107,83,81,20,128,191,123,251,222,75,211,64,242,36,160,85,135,100,81,123,19,104,85,172,
-  17,92,28,140,66,19,167,135,212,74,39,145,162,163,24,92,58,180,110,193,169,254,24,234,212,37,139,245,199,96,19,240,
-  15,240,63,16,121,91,92,196,90,219,240,98,81,137,33,121,215,33,246,217,151,80,33,103,186,92,206,247,221,115,14,231,
-  194,1,225,41,85,172,128,174,128,246,148,122,200,48,177,7,123,74,105,79,169,61,73,113,104,120,177,56,163,203,55,111,
-  252,87,34,250,225,154,235,110,20,148,98,253,220,89,182,39,78,113,228,240,56,39,188,38,83,107,107,212,92,151,130,82,
-  215,14,185,110,117,64,176,31,126,83,156,97,247,248,49,148,202,50,22,141,210,106,181,24,219,252,74,182,92,30,144,136,
-  126,184,122,251,22,157,100,146,116,42,77,42,149,2,64,107,104,183,219,236,214,235,156,44,149,66,18,177,31,126,247,160,
-  68,199,48,201,229,46,96,219,118,208,154,214,26,0,223,247,249,185,181,69,114,110,46,144,136,10,232,130,82,188,127,84,
-  198,107,54,57,63,157,35,30,143,35,132,8,9,124,223,15,206,134,16,140,230,243,212,92,23,3,184,3,172,78,157,62,
-  195,253,210,61,54,170,111,3,240,201,202,51,86,30,63,69,139,17,116,167,133,48,70,209,221,223,44,47,45,209,238,165,
-  204,202,121,120,190,48,153,33,17,79,0,160,212,4,153,140,34,155,205,244,94,20,146,145,244,149,222,196,83,121,144,22,
-  90,107,22,38,51,204,195,186,236,239,211,178,44,76,211,196,48,140,224,254,75,227,7,0,31,234,59,116,187,126,144,11,
-  48,32,144,82,34,165,196,146,159,1,232,96,241,241,83,3,128,111,222,47,58,34,18,90,36,99,96,179,254,14,207,231,
-  40,0,151,46,78,115,53,26,67,234,113,46,75,147,237,77,66,21,24,253,21,252,155,126,239,165,239,222,14,162,217,56,
-  112,245,3,65,36,18,193,182,109,18,137,4,2,137,16,16,139,197,112,28,103,0,178,44,43,252,23,174,207,58,175,0,
-  135,225,98,245,229,139,215,119,255,0,86,248,213,163,133,187,128,26,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t hotkeys[587] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
-  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
-  97,112,101,46,111,114,103,155,238,60,26,0,0,1,200,73,68,65,84,56,141,181,146,177,107,83,81,20,198,127,247,222,
-  115,95,95,8,73,159,136,180,209,38,177,21,7,165,21,26,16,204,228,34,102,8,8,34,212,168,127,72,193,191,160,131,
-  45,174,29,178,184,180,116,17,212,12,25,4,23,137,32,168,56,85,227,146,162,85,112,145,212,190,151,52,125,185,14,109,
-  53,29,164,193,226,7,151,115,46,231,124,223,189,231,240,169,82,169,196,113,160,15,146,122,189,174,142,37,48,191,48,159,
-  30,134,80,169,84,252,193,187,58,24,161,88,156,157,242,60,223,3,239,187,136,216,100,210,88,99,196,199,152,139,190,245,
-  175,2,87,28,76,3,30,74,189,72,38,82,229,106,181,26,203,31,45,127,195,57,123,201,250,186,108,69,143,59,103,50,
-  32,99,86,108,216,239,179,174,148,187,15,242,174,213,106,253,204,159,157,104,110,111,255,40,0,175,127,11,52,26,141,93,
-  224,205,254,249,219,247,131,252,228,196,50,142,157,173,173,206,251,67,59,56,10,115,119,230,174,163,226,183,202,169,200,104,
-  175,80,171,213,186,0,114,20,113,239,229,91,151,193,61,82,112,111,101,101,237,249,96,77,45,173,45,37,78,118,131,166,
-  159,24,57,163,181,70,27,141,86,123,209,104,131,214,10,173,13,206,57,0,246,123,94,61,88,120,88,4,144,157,86,120,
-  45,83,184,112,106,102,122,6,107,61,68,4,43,22,99,4,173,13,56,71,223,245,137,162,136,48,10,49,90,211,104,188,
-  156,173,84,110,158,95,93,125,220,20,17,239,118,144,62,225,133,97,135,205,175,159,72,37,211,160,0,20,74,41,218,237,
-  54,249,92,142,56,142,249,178,249,153,108,54,79,106,52,144,245,15,31,111,0,139,98,180,46,7,65,0,206,145,61,157,
-  99,183,31,31,154,127,52,157,166,215,235,1,138,169,201,115,116,186,93,50,99,227,102,196,179,119,129,69,137,227,120,227,
-  233,179,39,90,169,225,157,236,156,3,199,55,24,112,226,191,98,104,31,252,55,129,95,252,113,137,228,164,151,154,151,0,
-  0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t input[812] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,6,98,75,71,68,0,172,0,77,0,0,52,214,215,123,0,0,0,9,112,72,89,115,0,0,11,19,0,
-  0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,7,15,10,39,178,201,163,153,0,0,0,140,116,
-  69,88,116,67,111,109,109,101,110,116,0,77,101,110,117,45,115,105,122,101,100,32,105,99,111,110,10,61,61,61,61,61,
-  61,61,61,61,61,10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83,
-  116,101,105,110,101,114,44,32,10,104,116,116,112,58,47,47,106,105,109,109,97,99,46,109,117,115,105,99,104,97,108,108,
-  46,99,122,10,10,99,114,101,97,116,101,100,32,119,105,116,104,32,116,104,101,32,71,73,77,80,44,10,104,116,116,112,
-  58,47,47,119,119,119,46,103,105,109,112,46,111,114,103,103,138,199,71,0,0,2,33,73,68,65,84,56,203,149,146,203,
-  107,83,65,20,198,127,147,220,228,38,214,210,164,177,54,245,209,135,160,184,73,176,20,255,130,130,123,17,138,136,130,168,
-  32,88,8,193,133,173,15,180,221,136,46,68,10,10,130,72,22,93,213,186,19,68,208,133,187,34,84,179,19,68,20,42,
-  21,53,53,183,105,244,230,117,111,230,184,184,105,154,66,178,232,129,3,195,240,205,55,191,249,230,40,58,212,199,179,132,
-  255,170,221,51,97,41,95,2,124,235,42,178,96,80,191,58,62,95,40,181,234,84,167,195,117,124,223,250,14,237,141,27,
-  225,16,162,53,142,93,102,237,251,186,181,161,122,6,79,204,231,237,77,173,175,157,129,70,165,99,67,177,184,175,43,140,
-  235,247,99,21,107,216,174,143,129,35,253,189,192,253,86,173,175,195,11,82,129,93,97,252,166,137,17,12,16,12,26,252,
-  94,181,136,14,198,137,200,198,153,86,161,145,74,79,206,0,119,90,55,203,86,6,167,84,193,202,217,116,71,187,40,230,
-  255,225,214,28,220,170,67,69,153,209,84,250,178,52,164,179,42,149,158,148,185,135,143,182,93,255,235,229,61,170,203,115,
-  88,185,34,249,181,18,162,96,223,240,30,162,177,46,234,135,47,112,240,212,109,108,219,230,198,173,41,12,0,173,53,133,
-  66,97,11,235,248,57,114,111,30,211,63,212,199,200,104,15,134,25,160,248,243,15,185,85,135,3,231,47,98,89,22,65,
-  51,184,149,129,104,241,90,188,86,129,16,3,215,151,88,212,167,249,186,244,133,207,239,62,241,170,52,206,254,155,239,81,
-  129,16,34,2,141,71,120,4,162,209,34,32,226,125,172,0,134,137,27,234,101,228,193,42,43,43,43,228,223,190,134,128,
-  137,214,26,192,51,105,18,136,96,189,184,198,143,233,97,214,23,167,26,68,26,183,238,182,36,35,72,93,35,186,209,13,
-  3,163,153,252,242,2,82,43,81,90,94,160,251,228,93,0,142,37,71,121,250,236,73,115,93,23,221,68,111,230,181,25,
-  98,104,108,130,202,135,231,132,198,38,208,90,163,68,72,36,146,36,18,73,68,20,74,137,135,223,48,216,70,96,154,38,
-  71,175,100,128,76,219,169,178,109,111,114,203,229,50,0,149,74,101,59,65,54,155,197,117,93,118,82,74,169,45,131,72,
-  36,210,68,2,168,86,171,0,212,106,181,142,6,142,227,120,70,237,70,121,7,53,251,31,168,192,0,159,97,230,172,204,
-  0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t server[408] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,18,0,
-  0,11,18,1,210,221,126,252,0,0,0,7,116,73,77,69,7,214,2,16,22,3,20,11,54,9,17,0,0,1,37,73,
-  68,65,84,56,203,157,146,61,75,195,80,20,134,159,155,92,242,63,164,46,221,2,226,238,32,226,38,82,255,131,17,10,
-  34,56,74,196,42,232,228,80,63,210,95,32,4,99,5,255,134,131,110,46,130,56,152,22,170,129,214,170,225,82,18,7,
-  13,26,242,97,240,29,207,121,207,203,121,206,189,130,2,181,246,182,61,160,65,185,58,178,164,217,88,152,95,164,86,55,
-  115,155,131,167,7,188,174,107,149,5,80,171,155,44,55,47,48,140,180,77,169,9,87,206,10,0,165,1,66,8,12,67,
-  98,206,78,3,49,32,0,184,189,190,39,138,190,60,178,140,245,180,189,207,220,20,168,151,30,143,98,134,247,15,197,235,
-  56,76,121,100,69,86,122,207,111,223,21,29,128,56,142,127,16,170,176,70,163,97,170,254,123,131,74,172,238,225,82,102,
-  179,204,17,117,93,163,63,24,231,178,58,71,7,121,132,118,42,224,230,174,15,177,200,101,93,91,109,166,38,53,77,227,
-  196,105,183,100,98,82,106,130,49,28,101,110,144,40,8,130,220,35,203,132,231,242,184,81,248,10,0,238,249,89,97,64,
-  199,235,186,214,31,127,222,182,183,118,118,139,2,172,141,245,77,194,48,204,157,76,88,129,194,0,124,223,231,191,146,101,
-  124,85,244,9,241,192,132,130,214,14,135,66,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t stateManager[378] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
-  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,6,16,18,41,48,44,67,93,90,0,0,1,7,73,
-  68,65,84,56,203,189,146,189,74,3,65,28,196,127,251,145,69,177,176,73,44,124,128,128,207,225,19,8,130,136,31,129,
-  136,104,37,34,104,173,32,10,130,181,165,112,6,242,20,190,78,138,120,8,90,104,178,183,123,107,17,15,205,37,94,238,
-  82,56,229,252,255,59,179,179,179,226,168,221,4,216,19,130,136,10,8,129,125,224,73,103,135,175,78,46,73,146,1,225,
-  123,90,4,33,36,215,15,183,81,8,32,142,15,154,225,188,125,138,148,211,151,245,48,198,153,250,4,175,134,49,55,157,
-  14,58,35,30,163,59,250,214,148,186,254,138,177,180,118,47,70,6,25,217,183,134,237,213,151,82,2,221,94,131,193,231,
-  59,90,215,144,204,9,169,70,222,114,150,83,183,215,40,20,210,191,115,253,181,156,231,151,148,35,245,14,231,146,31,129,
-  245,250,219,92,81,52,128,181,31,60,199,203,149,90,216,202,71,168,218,130,79,253,236,71,44,213,6,64,234,221,255,182,
-  48,33,96,236,43,155,27,135,99,131,212,123,164,82,164,222,231,62,208,56,39,90,59,107,247,139,11,254,76,235,218,84,
-  7,231,146,194,8,95,134,90,101,183,231,143,210,134,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t timing[897] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
-  0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,9,15,8,58,5,128,132,46,85,0,0,3,14,73,
-  68,65,84,56,203,101,146,75,76,92,101,28,197,127,223,119,239,188,152,185,119,96,34,8,210,202,35,88,29,108,48,150,
-  190,226,162,177,59,220,152,138,193,186,32,26,54,38,68,87,181,53,38,44,77,99,26,31,137,198,68,77,172,53,177,139,
-  62,240,17,77,76,163,46,176,5,77,85,40,5,58,88,43,218,17,233,76,167,204,12,195,204,189,195,12,119,190,207,141,
-  109,40,158,228,236,206,57,255,71,142,96,19,142,28,24,25,210,146,23,5,226,97,148,14,8,67,122,90,138,27,85,181,
-  126,230,157,207,94,127,101,179,94,108,48,246,104,248,116,247,190,71,123,182,247,198,9,71,194,8,1,90,67,113,181,196,
-  204,84,130,75,151,230,211,131,195,231,142,245,60,146,10,34,117,146,80,242,180,185,193,124,97,112,120,192,46,27,138,85,
-  159,131,40,190,118,103,138,221,113,140,120,224,65,26,59,83,205,141,205,149,55,61,189,115,213,208,191,172,8,183,93,73,
-  0,141,62,62,56,60,96,223,88,43,208,254,80,136,104,245,237,187,214,180,235,45,182,110,107,161,119,111,142,104,195,46,
-  131,192,187,13,200,167,163,192,14,121,228,192,200,208,238,125,59,118,150,180,71,91,87,35,45,91,226,155,207,68,74,137,
-  16,2,51,16,67,178,68,165,116,2,167,52,19,4,253,131,4,6,182,247,198,89,46,103,105,109,109,166,144,255,135,248,
-  227,167,72,103,125,119,63,75,8,42,94,31,158,232,39,149,172,114,242,195,7,170,120,242,156,169,209,123,194,145,48,150,
-  23,66,41,133,207,103,81,118,139,132,234,44,70,94,218,134,223,222,203,115,109,57,108,219,66,200,24,210,127,16,195,187,
-  201,194,212,199,209,161,23,250,131,18,168,23,2,154,154,26,200,23,242,40,165,168,41,141,227,20,57,250,222,239,68,98,
-  221,76,76,76,160,148,66,107,133,97,26,152,134,4,165,5,208,101,2,43,90,19,43,187,85,58,58,218,169,84,214,80,
-  74,81,44,22,233,238,238,230,229,195,175,114,237,218,85,106,181,26,82,74,180,82,104,13,72,52,176,32,5,226,162,83,
-  114,88,252,59,141,82,10,207,243,240,60,143,67,135,14,147,72,36,200,100,210,88,150,69,173,86,195,178,108,202,110,25,
-  167,228,128,207,112,63,57,253,150,35,129,179,115,147,243,216,134,133,83,114,241,249,252,140,141,141,49,58,58,74,42,181,
-  116,39,48,28,142,160,53,44,95,207,50,55,57,79,133,218,12,128,124,227,203,163,39,126,62,63,53,217,24,182,25,255,
-  254,87,180,214,244,245,61,65,58,157,98,173,82,65,107,77,32,16,192,52,77,242,153,28,238,178,203,197,31,167,171,95,
-  77,156,121,6,192,0,232,188,183,107,110,97,54,249,236,254,253,143,249,255,252,99,145,92,33,207,253,109,91,8,6,130,
-  4,131,33,252,190,0,55,255,202,144,191,190,194,201,15,206,234,217,165,217,247,167,230,127,154,3,178,6,192,76,114,58,
-  239,55,253,87,150,174,102,246,52,53,196,162,109,45,173,120,69,143,74,126,141,114,182,204,173,228,45,46,143,207,242,245,
-  231,223,174,143,255,118,225,248,249,233,239,190,1,210,64,78,108,232,74,61,208,249,228,174,254,231,239,187,103,235,83,117,
-  254,186,22,41,164,169,209,202,93,119,139,233,66,58,241,197,248,169,143,128,20,112,5,88,4,180,224,255,136,252,23,118,
-  155,26,40,3,171,64,22,40,0,234,182,248,95,201,36,100,6,22,194,54,223,0,0,0,0,73,69,78,68,174,66,96,
-  130,
-};
-
-const uint8_t unverified[1675] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
-  244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,6,66,73,68,65,84,88,133,229,151,217,115,84,
-  85,30,199,191,231,119,238,146,78,210,183,59,107,147,116,167,151,132,152,72,132,208,128,27,13,9,104,160,28,202,146,161,
-  3,79,83,214,164,242,52,207,62,251,224,195,252,13,62,88,53,37,102,134,69,12,250,162,165,14,58,162,65,81,17,179,
-  66,7,59,91,111,233,108,244,96,210,33,73,247,237,123,231,161,23,110,98,2,1,103,158,230,86,253,234,156,170,123,239,
-  249,124,206,249,253,234,87,117,128,255,247,135,253,158,159,143,118,116,119,106,76,239,37,157,94,253,234,139,191,125,242,36,
-  107,208,239,129,103,152,126,94,177,213,32,67,218,135,109,199,186,78,60,201,58,252,73,225,26,99,231,188,190,131,242,169,
-  147,29,88,211,56,159,137,70,207,184,234,247,254,28,154,24,8,254,79,5,142,118,116,119,106,132,243,222,67,62,217,225,
-  170,195,59,189,223,224,224,179,45,40,85,20,30,143,68,30,91,226,177,4,142,118,116,119,130,211,133,87,94,123,69,42,
-  171,182,225,131,43,55,161,105,192,157,169,89,28,122,174,5,102,197,194,167,195,225,199,146,216,182,64,91,71,151,159,56,
-  191,248,122,215,105,241,233,150,167,240,246,197,175,0,157,65,32,2,17,67,48,52,7,223,129,93,48,91,44,60,22,10,
-  159,113,55,236,187,25,26,239,31,251,175,8,180,117,116,249,193,232,194,159,94,247,75,45,45,77,24,15,207,195,251,180,
-  19,7,118,185,112,247,94,18,43,171,105,16,99,24,11,207,227,57,111,19,20,139,149,71,67,83,219,146,120,164,64,91,
-  71,151,159,49,186,112,224,240,33,105,207,158,102,68,226,255,6,24,64,44,187,243,249,68,18,139,75,171,16,56,129,24,
-  195,100,100,1,251,91,159,202,74,132,31,45,241,80,129,60,124,223,33,159,84,235,172,67,96,124,6,85,21,102,16,17,
-  194,211,119,241,237,207,227,152,187,187,4,129,19,4,226,16,136,192,115,239,188,187,119,66,177,150,241,72,40,116,198,221,
-  224,221,82,98,203,62,208,126,172,251,20,35,186,224,245,249,164,29,14,59,62,191,54,130,180,154,1,39,2,103,12,137,
-  123,247,113,127,37,5,81,224,16,57,135,40,80,97,78,68,184,118,115,12,110,143,27,190,246,118,137,129,125,212,126,188,
-  251,15,219,22,104,63,214,125,10,76,191,232,61,152,133,255,235,251,81,112,70,133,99,38,202,206,69,158,135,103,71,129,
-  19,132,220,156,51,194,15,131,147,112,215,123,224,59,210,38,211,22,18,191,73,65,30,190,247,69,159,84,227,112,224,155,
-  159,126,1,129,65,224,4,119,109,5,170,203,205,32,98,208,52,29,43,43,41,168,25,173,112,244,198,84,8,156,192,25,
-  97,110,97,17,187,154,60,80,172,86,33,58,21,58,237,118,183,254,52,53,57,48,182,169,192,145,227,93,127,4,195,251,
-  173,47,100,119,254,93,255,120,1,46,16,161,209,85,13,171,82,12,98,12,101,74,49,26,221,54,164,83,42,150,150,215,
-  10,223,100,225,188,48,231,68,88,72,36,209,220,232,130,181,204,42,132,67,225,51,70,137,7,41,120,235,45,210,117,246,
-  81,149,221,37,181,182,54,227,198,208,36,56,123,144,87,145,115,216,42,20,112,198,178,117,64,12,196,24,86,83,234,186,
-  111,178,115,130,73,22,81,91,109,133,44,8,224,140,16,28,159,197,97,223,126,212,185,93,178,78,236,211,223,158,192,213,
-  171,122,125,189,119,96,121,121,209,207,4,19,111,110,116,98,62,177,84,200,125,185,165,4,205,245,59,64,68,88,76,174,
-  224,202,181,0,198,166,230,145,74,169,133,221,154,100,17,12,217,230,100,171,84,240,236,30,55,146,203,107,72,165,51,216,
-  183,219,133,196,92,28,125,125,215,83,200,104,175,230,79,96,93,10,166,38,6,238,120,26,90,71,102,98,81,191,217,98,
-  229,77,13,78,220,189,151,4,39,194,174,134,26,84,150,149,130,17,67,40,150,192,226,210,202,131,156,115,66,133,181,4,
-  109,207,55,98,109,85,133,36,114,212,213,150,67,49,155,80,93,105,198,254,221,78,68,67,97,156,59,255,97,42,147,86,
-  79,94,253,242,236,103,91,22,225,212,248,192,168,167,161,117,36,30,139,250,45,214,50,190,211,227,192,210,242,26,246,63,
-  227,130,32,16,136,17,130,147,179,80,85,173,80,108,2,113,232,58,208,224,172,66,173,205,10,103,109,5,44,102,19,136,
-  8,149,229,165,24,25,30,197,63,206,93,78,111,132,111,42,96,148,152,142,70,252,86,107,57,63,122,112,55,100,89,0,
-  67,182,251,217,42,21,36,147,107,40,41,150,80,95,87,133,213,149,20,158,105,178,67,49,155,10,29,146,136,80,102,41,
-  198,224,208,109,244,252,253,114,58,147,86,95,219,8,223,82,96,189,68,180,179,185,201,195,157,142,26,164,83,42,136,8,
-  178,40,64,18,57,100,81,64,131,179,26,110,71,37,148,210,34,16,35,176,28,220,170,152,48,56,20,64,79,207,229,116,
-  38,157,222,20,254,80,129,188,132,171,222,59,50,60,28,232,116,56,106,72,42,50,97,240,86,12,153,140,134,88,252,30,
-  150,146,171,112,218,203,193,11,13,42,11,87,204,121,120,239,67,225,143,20,0,128,208,68,255,168,211,179,119,100,104,232,
-  182,159,139,197,188,178,170,28,247,151,83,80,211,26,154,118,238,128,82,106,2,203,117,71,34,6,115,105,81,246,216,123,
-  122,211,170,150,57,249,245,23,239,110,9,223,150,64,78,34,104,119,182,4,98,145,200,201,138,138,42,254,114,219,30,216,
-  119,88,97,46,41,42,180,102,34,134,210,146,34,12,15,7,240,94,79,111,122,117,109,229,116,223,151,239,253,51,183,132,
-  254,36,2,4,64,2,80,4,64,14,79,14,133,170,108,158,224,108,60,126,130,139,37,188,169,209,9,77,211,11,69,87,
-  82,44,99,104,56,128,119,123,46,169,139,191,46,252,249,135,190,75,87,1,8,57,70,190,225,105,219,21,16,1,200,27,
-  99,58,18,8,91,202,107,39,98,177,217,227,110,151,157,219,107,170,161,105,58,138,77,89,248,217,158,75,234,194,124,244,
-  47,253,63,126,220,103,128,231,5,8,217,107,128,110,60,145,205,4,120,78,64,50,68,65,98,102,58,24,43,85,42,38,
-  127,25,139,190,228,114,216,169,206,97,195,224,208,109,156,237,185,164,198,167,39,222,24,233,191,114,61,7,55,66,141,247,
-  15,29,64,230,81,2,66,78,66,200,9,228,133,100,0,242,220,204,68,188,200,100,14,7,39,166,219,18,137,69,250,248,
-  211,43,153,104,248,206,155,163,195,95,223,48,64,141,64,45,23,25,195,184,165,128,102,176,231,134,49,47,198,1,136,11,
-  115,161,184,44,149,196,18,191,174,30,142,132,110,253,53,24,184,126,99,3,72,221,16,105,67,20,82,240,176,171,89,190,
-  14,36,195,104,12,209,32,152,223,101,30,144,218,16,107,134,113,93,33,110,231,110,152,79,135,132,245,169,201,195,141,133,
-  149,49,236,222,40,179,110,215,143,43,176,217,63,70,120,126,141,141,185,222,214,243,31,50,77,124,34,176,20,164,223,0,
-  0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t up[652] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
-  101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,30,73,68,65,84,56,141,
-  149,147,79,104,19,65,20,198,191,55,187,51,217,141,133,122,104,76,255,209,130,4,237,193,64,42,168,208,213,138,104,68,
-  98,69,45,20,114,107,74,201,73,144,98,22,193,171,199,82,145,160,23,15,69,79,30,165,66,241,226,77,42,94,68,80,
-  208,67,41,180,42,149,148,52,137,166,154,38,217,217,25,15,81,172,33,169,246,29,223,251,230,199,124,223,155,33,173,53,
-  218,213,201,155,124,30,0,150,102,189,233,118,26,214,110,224,184,60,221,223,21,73,246,117,29,76,58,46,79,239,9,224,
-  184,60,26,16,251,178,87,206,76,6,199,70,147,193,128,8,102,29,151,71,255,11,224,184,188,3,132,197,137,115,211,214,
-  150,151,199,150,183,137,248,200,37,11,132,69,199,229,29,255,4,16,225,209,232,112,162,167,55,52,72,159,75,31,240,177,
-  244,30,157,157,157,20,27,58,209,13,194,195,93,1,142,203,211,253,7,34,137,179,71,199,249,74,254,13,12,198,97,50,
-  142,87,107,11,136,13,29,23,161,253,61,137,230,60,216,142,195,81,193,237,108,42,225,218,235,229,101,104,242,32,132,137,
-  58,125,199,54,21,241,46,255,28,23,78,95,13,154,166,248,43,15,182,211,247,212,88,198,82,228,161,34,75,16,1,1,
-  30,224,40,203,117,8,219,64,65,174,162,168,86,113,254,212,69,139,216,159,60,216,111,223,241,99,227,221,135,250,98,148,
-  175,172,193,228,6,56,55,80,81,5,84,141,175,16,54,131,176,25,150,191,189,64,184,55,68,71,14,15,135,137,53,242,
-  160,145,140,153,30,232,142,100,111,76,220,177,5,15,64,65,194,135,135,215,185,5,188,45,60,131,100,21,16,35,36,6,
-  50,208,90,67,41,160,94,175,225,241,211,249,74,177,180,57,99,18,67,234,83,110,197,158,185,119,185,225,137,163,122,247,
-  250,19,171,44,115,240,141,109,112,193,192,76,130,193,9,115,15,110,87,101,77,91,191,236,7,137,33,101,46,205,122,78,
-  211,38,180,214,10,165,250,23,48,131,96,112,6,30,104,64,100,77,91,47,231,60,218,245,29,0,128,210,62,126,212,139,
-  141,43,251,26,178,174,160,85,235,63,99,182,106,250,74,162,90,219,134,50,53,160,21,124,73,240,189,61,0,164,47,17,
-  31,188,6,98,0,49,2,17,246,0,32,108,220,186,63,25,110,37,38,194,70,115,239,39,48,247,197,219,182,208,154,34,
-  0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-const uint8_t video[662] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
-  97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
-  215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
-  97,112,101,46,111,114,103,155,238,60,26,0,0,2,19,73,68,65,84,56,141,165,147,203,106,20,65,20,134,191,83,85,
-  115,109,72,50,32,6,98,18,35,38,11,183,222,54,186,112,99,6,29,240,25,124,4,55,130,184,81,80,4,241,49,124,
-  131,108,92,8,9,17,34,226,222,96,188,129,38,16,131,97,72,156,233,234,233,158,238,58,46,58,17,19,3,65,60,155,
-  42,170,234,255,207,119,206,161,164,221,110,243,63,225,58,157,206,29,17,121,168,170,181,127,17,138,136,87,213,187,78,85,
-  31,183,111,92,143,154,141,38,121,174,199,103,116,130,181,134,110,183,91,95,90,124,245,196,169,106,109,116,116,132,249,155,
-  207,73,233,3,160,122,132,209,222,81,77,34,94,190,184,205,200,232,40,64,195,237,223,167,161,207,133,171,99,136,200,30,
-  34,128,148,171,0,90,238,223,44,111,179,250,238,61,83,83,147,37,81,154,166,128,160,226,136,211,80,10,20,68,228,128,
-  73,54,204,217,238,254,4,28,103,103,103,49,18,74,3,239,61,33,20,24,99,24,228,82,102,43,155,4,148,194,205,31,
-  59,196,62,5,160,97,90,124,88,91,99,122,122,146,36,73,112,73,146,80,20,1,140,35,201,203,108,138,32,64,156,164,
-  236,244,18,84,29,149,250,94,181,137,101,122,230,52,21,103,240,222,227,226,56,70,131,98,76,133,65,112,251,0,36,233,
-  144,108,104,112,181,232,224,248,172,229,243,199,79,204,205,205,210,239,247,75,2,69,17,107,73,131,3,5,69,81,107,169,
-  216,35,134,97,135,76,156,154,192,24,202,18,188,247,0,24,227,176,213,232,111,197,161,24,154,62,69,81,160,8,113,28,
-  227,66,40,187,217,106,56,178,205,193,177,6,81,213,33,70,49,34,37,129,115,174,183,254,109,163,245,236,209,249,99,197,
-  191,41,210,156,245,245,13,128,29,233,116,58,183,106,181,218,3,17,141,64,12,48,83,169,56,87,169,86,197,26,3,64,
-  17,2,89,150,105,62,204,135,192,87,208,160,202,174,247,201,125,151,231,249,66,158,231,11,0,170,26,181,78,140,189,157,
-  111,183,207,93,186,120,153,241,147,227,168,42,91,91,223,89,121,189,162,75,203,75,171,73,111,112,37,203,50,191,79,35,
-  135,191,179,49,102,164,222,172,63,109,70,205,107,214,154,51,128,22,161,248,50,240,131,197,222,110,255,158,136,196,127,190,
-  255,5,119,143,242,70,185,147,13,30,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-};
diff --git a/target-higan/resource/resource.hpp b/target-higan/resource/resource.hpp
deleted file mode 100644
index 8a518946..00000000
--- a/target-higan/resource/resource.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace resource {
-  extern const uint8_t advanced[611];
-  extern const uint8_t audio[592];
-  extern const uint8_t cheatEditor[937];
-  extern const uint8_t folder[1176];
-  extern const uint8_t game[1490];
-  extern const uint8_t home[606];
-  extern const uint8_t hotkeys[587];
-  extern const uint8_t input[812];
-  extern const uint8_t server[408];
-  extern const uint8_t stateManager[378];
-  extern const uint8_t timing[897];
-  extern const uint8_t unverified[1675];
-  extern const uint8_t up[652];
-  extern const uint8_t video[662];
-};
diff --git a/target-higan/resource/unverified.png b/target-higan/resource/unverified.png
deleted file mode 100644
index 8a150b84..00000000
Binary files a/target-higan/resource/unverified.png and /dev/null differ
diff --git a/target-higan/settings/advanced.cpp b/target-higan/settings/advanced.cpp
deleted file mode 100644
index 508c11a9..00000000
--- a/target-higan/settings/advanced.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-AdvancedSettings* advancedSettings = nullptr;
-
-AdvancedSettings::AdvancedSettings() {
-  driverTitle.setFont(program->boldFont);
-  driverTitle.setText("Driver Selection:");
-  videoLabel.setText("Video:");
-  audioLabel.setText("Audio:");
-  inputLabel.setText("Input:");
-  libraryTitle.setFont(program->boldFont);
-  libraryTitle.setText("Game Library:");
-  libraryLabel.setText("Path:");
-  libraryPath.setEditable(false);
-  libraryPath.setText(utility->libraryPath());
-  libraryBrowse.setText("Browse ...");
-  libraryShowOnStartup.setChecked(config->library.showOnStartup);
-  libraryShowOnStartup.setText("Show game library on program start");
-  information.setText("Note: changing advanced settings requires program restart to take effect.");
-  infoLabel.setFont(program->boldFont);
-  infoLabel.setText({
-    Emulator::Name, " v", Emulator::Version, "\n",
-    "  ", Emulator::Profile, " Profile\n",
-    "  Author: ", Emulator::Author, "\n",
-    "  License: ", Emulator::License, "\n",
-    "  Website: ", Emulator::Website
-  });
-
-  lstring list;
-
-  list.split(";", video.availableDrivers());
-  for(unsigned n = 0; n < list.size(); n++) {
-    videoDriver.append(list[n]);
-    if(list[n] == config->video.driver) videoDriver.setSelection(n);
-  }
-
-  list.split(";", audio.availableDrivers());
-  for(unsigned n = 0; n < list.size(); n++) {
-    audioDriver.append(list[n]);
-    if(list[n] == config->audio.driver) audioDriver.setSelection(n);
-  }
-
-  list.split(";", input.availableDrivers());
-  for(unsigned n = 0; n < list.size(); n++) {
-    inputDriver.append(list[n]);
-    if(list[n] == config->input.driver) inputDriver.setSelection(n);
-  }
-
-  append(driverTitle, {~0, 0});
-  append(driverLayout, {~0, 0}, 15);
-    driverLayout.append(videoLabel, {0, 0}, 5);
-    driverLayout.append(videoDriver, {~0, 0}, 5);
-    driverLayout.append(audioLabel, {0, 0}, 5);
-    driverLayout.append(audioDriver, {~0, 0}, 5);
-    driverLayout.append(inputLabel, {0, 0}, 5);
-    driverLayout.append(inputDriver, {~0, 0});
-  append(libraryTitle, {~0, 0});
-  append(libraryLayout, {~0, 0});
-    libraryLayout.append(libraryLabel, {0, 0}, 5);
-    libraryLayout.append(libraryPath, {~0, 0}, 5);
-    libraryLayout.append(libraryBrowse, {80, 0});
-  append(libraryShowOnStartup, {~0, 0}, 15);
-  append(information, {~0, 0}, 15);
-  if(Intrinsics::platform() != Intrinsics::Platform::MacOSX) {
-    append(spacer, {~0, ~0});
-    append(infoLabel, {~0, 0});
-  }
-
-  videoDriver.onChange = [&] { config->video.driver = videoDriver.text(); };
-  audioDriver.onChange = [&] { config->audio.driver = audioDriver.text(); };
-  inputDriver.onChange = [&] { config->input.driver = inputDriver.text(); };
-
-  libraryBrowse.onActivate = [&] {
-    string path = BrowserWindow().setParent(*settings).setPath(userpath()).directory();
-    if(path.empty()) return;
-    file::write({configpath(), "higan/library.bml"}, {"Path: ", path, "\n"});
-    libraryPath.setText(path);
-  };
-
-  libraryShowOnStartup.onToggle = [&] {
-    config->library.showOnStartup = libraryShowOnStartup.checked();
-  };
-}
diff --git a/target-higan/settings/advanced.hpp b/target-higan/settings/advanced.hpp
deleted file mode 100644
index eaf0041a..00000000
--- a/target-higan/settings/advanced.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-struct AdvancedSettings : SettingsLayout {
-  Label driverTitle;
-  HorizontalLayout driverLayout;
-    Label videoLabel;
-    ComboButton videoDriver;
-    Label audioLabel;
-    ComboButton audioDriver;
-    Label inputLabel;
-    ComboButton inputDriver;
-
-  Label libraryTitle;
-  HorizontalLayout libraryLayout;
-    Label libraryLabel;
-    LineEdit libraryPath;
-    Button libraryBrowse;
-  CheckLabel libraryShowOnStartup;
-
-  Label information;
-
-  Widget spacer;
-  Label infoLabel;
-
-  AdvancedSettings();
-};
-
-extern AdvancedSettings* advancedSettings;
diff --git a/target-higan/settings/audio.cpp b/target-higan/settings/audio.cpp
deleted file mode 100644
index 6059576c..00000000
--- a/target-higan/settings/audio.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-AudioSettings* audioSettings = nullptr;
-
-AudioSlider::AudioSlider() {
-  append(name, {75, 0});
-  append(value, {75, 0});
-  append(slider, {~0, 0});
-}
-
-AudioSettings::AudioSettings() {
-  frequencyLabel.setText("Frequency:");
-  frequency.append("32000hz");
-  frequency.append("44100hz");
-  frequency.append("48000hz");
-  frequency.append("96000hz");
-  latencyLabel.setText("Latency:");
-  latency.append("20ms");
-  latency.append("40ms");
-  latency.append("60ms");
-  latency.append("80ms");
-  latency.append("100ms");
-  resamplerLabel.setText("Resampler:");
-  resampler.append("Linear");
-  resampler.append("Hermite");
-  resampler.append("Sinc");
-  volume.name.setText("Volume:");
-  volume.slider.setLength(201);
-
-  append(controlLayout, {~0, 0}, 5);
-    controlLayout.append(frequencyLabel, {0, 0}, 5);
-    controlLayout.append(frequency, {~0, 0}, 5);
-    controlLayout.append(latencyLabel, {0, 0}, 5);
-    controlLayout.append(latency, {~0, 0}, 5);
-    controlLayout.append(resamplerLabel, {0, 0}, 5);
-    controlLayout.append(resampler, {~0, 0});
-  append(volume, {~0, 0});
-
-  switch(config->audio.frequency) { default:
-  case 32000: frequency.setSelection(0); break;
-  case 44100: frequency.setSelection(1); break;
-  case 48000: frequency.setSelection(2); break;
-  case 96000: frequency.setSelection(3); break;
-  }
-  switch(config->audio.latency) { default:
-  case  20: latency.setSelection(0); break;
-  case  40: latency.setSelection(1); break;
-  case  60: latency.setSelection(2); break;
-  case  80: latency.setSelection(3); break;
-  case 100: latency.setSelection(4); break;
-  }
-  resampler.setSelection(config->audio.resampler);
-  volume.slider.setPosition(config->audio.volume);
-
-  frequency.onChange = latency.onChange = resampler.onChange = volume.slider.onChange =
-  {&AudioSettings::synchronize, this};
-
-  synchronize();
-}
-
-void AudioSettings::synchronize() {
-  switch(frequency.selection()) {
-  case 0: config->audio.frequency = 32000; break;
-  case 1: config->audio.frequency = 44100; break;
-  case 2: config->audio.frequency = 48000; break;
-  case 3: config->audio.frequency = 96000; break;
-  }
-  switch(latency.selection()) {
-  case 0: config->audio.latency =  20; break;
-  case 1: config->audio.latency =  40; break;
-  case 2: config->audio.latency =  60; break;
-  case 3: config->audio.latency =  80; break;
-  case 4: config->audio.latency = 100; break;
-  }
-  config->audio.resampler = resampler.selection();
-  config->audio.volume = volume.slider.position();
-
-  volume.value.setText({config->audio.volume, "%"});
-
-  utility->synchronizeRuby();
-}
diff --git a/target-higan/settings/audio.hpp b/target-higan/settings/audio.hpp
deleted file mode 100644
index d27fcfc8..00000000
--- a/target-higan/settings/audio.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-struct AudioSlider : HorizontalLayout {
-  Label name;
-  Label value;
-  HorizontalSlider slider;
-
-  AudioSlider();
-};
-
-struct AudioSettings : SettingsLayout {
-  HorizontalLayout controlLayout;
-    Label frequencyLabel;
-    ComboButton frequency;
-    Label latencyLabel;
-    ComboButton latency;
-    Label resamplerLabel;
-    ComboButton resampler;
-  AudioSlider volume;
-
-  void synchronize();
-  AudioSettings();
-};
-
-extern AudioSettings* audioSettings;
diff --git a/target-higan/settings/hotkey.cpp b/target-higan/settings/hotkey.cpp
deleted file mode 100644
index e5ec041e..00000000
--- a/target-higan/settings/hotkey.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-HotkeySettings* hotkeySettings = nullptr;
-
-HotkeySettings::HotkeySettings() {
-  inputList.setHeaderText({"Name", "Mapping"});
-  inputList.setHeaderVisible();
-  eraseButton.setText("Erase");
-
-  append(inputList, {~0, ~0}, 5);
-  append(controlLayout, {~0, 0});
-    controlLayout.append(spacer, {~0, 0});
-    controlLayout.append(eraseButton, {80, 0});
-
-  inputList.onChange = {&HotkeySettings::synchronize, this};
-  inputList.onActivate = {&HotkeySettings::assignInput, this};
-  eraseButton.onActivate = {&HotkeySettings::eraseInput, this};
-
-  for(auto& hotkey : inputManager->hotkeyMap) inputList.append({"", ""});
-  refresh();
-}
-
-void HotkeySettings::synchronize() {
-  eraseButton.setEnabled(inputList.selected());
-}
-
-void HotkeySettings::refresh() {
-  unsigned index = 0;
-  for(auto& hotkey : inputManager->hotkeyMap) {
-    string mapping = inputManager->sanitize(hotkey->mapping, " and ");
-    inputList.setText(index++, {hotkey->name, mapping});
-  }
-  synchronize();
-}
-
-void HotkeySettings::eraseInput() {
-  activeInput = inputManager->hotkeyMap[inputList.selection()];
-  inputEvent(hidNull, 0, 0, 0, 1);
-}
-
-void HotkeySettings::assignInput() {
-  activeInput = inputManager->hotkeyMap[inputList.selection()];
-
-  settings->setStatusText({"Set assignment for [", activeInput->name, "] ..."});
-  settings->layout.setEnabled(false);
-  setEnabled(false);
-}
-
-void HotkeySettings::inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
-  if(activeInput == nullptr) return;
-  if(device.isMouse()) return;
-  if(device.isJoypad() && group == HID::Joypad::GroupID::Axis) return;
-  if(activeInput->bind(device, group, input, oldValue, newValue) == false) return;
-
-  activeInput = nullptr;
-  settings->setStatusText("");
-  settings->layout.setEnabled(true);
-  setEnabled(true);
-  refresh();
-}
diff --git a/target-higan/settings/hotkey.hpp b/target-higan/settings/hotkey.hpp
deleted file mode 100644
index c254df73..00000000
--- a/target-higan/settings/hotkey.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-struct HotkeySettings : SettingsLayout {
-  ListView inputList;
-  HorizontalLayout controlLayout;
-    Widget spacer;
-    Button eraseButton;
-
-  void synchronize();
-  void refresh();
-  void eraseInput();
-  void assignInput();
-  void inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
-  HotkeySettings();
-
-private:
-  HotkeyInput* activeInput = nullptr;
-};
-
-extern HotkeySettings* hotkeySettings;
diff --git a/target-higan/settings/input.cpp b/target-higan/settings/input.cpp
deleted file mode 100644
index b720524c..00000000
--- a/target-higan/settings/input.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-InputSettings* inputSettings = nullptr;
-
-InputSettings::InputSettings() {
-  focusLabel.setText("When Focus is Lost:");
-  focusPause.setText("Pause Emulation");
-  focusAllow.setText("Allow Input");
-  inputList.setHeaderText({"Name", "Mapping"});
-  inputList.setHeaderVisible();
-  resetButton.setText("Reset");
-  eraseButton.setText("Erase");
-
-  append(focusLayout, {~0, 0}, 5);
-    focusLayout.append(focusLabel, {0, 0}, 5);
-    focusLayout.append(focusPause, {0, 0}, 5);
-    focusLayout.append(focusAllow, {0, 0});
-  append(selectionLayout, {~0, 0}, 5);
-    selectionLayout.append(systemList, {~0, 0}, 5);
-    selectionLayout.append(portList, {~0, 0}, 5);
-    selectionLayout.append(deviceList, {~0, 0});
-  append(inputList, {~0, ~0}, 5);
-  append(controlLayout, {~0, 0});
-    controlLayout.append(assign[0], {100, 0}, 5);
-    controlLayout.append(assign[1], {100, 0}, 5);
-    controlLayout.append(assign[2], {100, 0}, 5);
-    controlLayout.append(spacer, {~0, 0});
-    controlLayout.append(resetButton, {80, 0}, 5);
-    controlLayout.append(eraseButton, {80, 0});
-
-  for(auto& emulator : program->emulator) {
-    systemList.append(emulator->information.name);
-  }
-
-  focusPause.setChecked(config->input.focus.pause);
-  focusAllow.setChecked(config->input.focus.allow);
-  focusAllow.setEnabled(!config->input.focus.pause);
-
-  focusPause.onToggle = [&] { config->input.focus.pause = focusPause.checked(); focusAllow.setEnabled(!focusPause.checked()); };
-  focusAllow.onToggle = [&] { config->input.focus.allow = focusAllow.checked(); };
-  systemList.onChange = {&InputSettings::systemChanged, this};
-  portList.onChange = {&InputSettings::portChanged, this};
-  deviceList.onChange = {&InputSettings::deviceChanged, this};
-  inputList.onChange = {&InputSettings::synchronize, this};
-  inputList.onActivate = {&InputSettings::assignInput, this};
-  assign[0].onActivate = [&] { assignMouseInput(0); };
-  assign[1].onActivate = [&] { assignMouseInput(1); };
-  assign[2].onActivate = [&] { assignMouseInput(2); };
-  resetButton.onActivate = {&InputSettings::resetInput, this};
-  eraseButton.onActivate = {&InputSettings::eraseInput, this};
-
-  systemChanged();
-}
-
-void InputSettings::synchronize() {
-  if(inputList.selected() == false) {
-    assign[0].setVisible(false);
-    assign[1].setVisible(false);
-    assign[2].setVisible(false);
-  } else {
-    unsigned number = activeDevice().order[inputList.selection()];
-    auto& input = activeDevice().input[number];
-    auto selectedInput = inputManager->inputMap[input.guid];
-
-    if(dynamic_cast<DigitalInput*>(selectedInput)) {
-      assign[0].setText("Mouse Left");
-      assign[1].setText("Mouse Middle");
-      assign[2].setText("Mouse Right");
-      assign[0].setVisible(true);
-      assign[1].setVisible(true);
-      assign[2].setVisible(true);
-    }
-
-    if(dynamic_cast<RelativeInput*>(selectedInput)) {
-      assign[0].setText("Mouse X-axis");
-      assign[1].setText("Mouse Y-axis");
-      assign[0].setVisible(true);
-      assign[1].setVisible(true);
-      assign[2].setVisible(false);
-    }
-  }
-
-  eraseButton.setEnabled(inputList.selected());
-}
-
-Emulator::Interface& InputSettings::activeSystem() {
-  return *program->emulator[systemList.selection()];
-}
-
-Emulator::Interface::Port& InputSettings::activePort() {
-  return activeSystem().port[portList.selection()];
-}
-
-Emulator::Interface::Device& InputSettings::activeDevice() {
-  return activePort().device[deviceList.selection()];
-}
-
-void InputSettings::systemChanged() {
-  portList.reset();
-  for(auto& port : activeSystem().port) {
-    portList.append(port.name);
-  }
-  portChanged();
-}
-
-void InputSettings::portChanged() {
-  deviceList.reset();
-  for(auto& device : activePort().device) {
-    deviceList.append(device.name);
-  }
-  deviceChanged();
-}
-
-void InputSettings::deviceChanged() {
-  inputList.reset();
-  for(unsigned number : activeDevice().order) inputList.append({"", ""});
-  inputChanged();
-  synchronize();
-}
-
-void InputSettings::inputChanged() {
-  unsigned index = 0;
-  for(unsigned number : activeDevice().order) {
-    auto& input = activeDevice().input[number];
-    auto abstract = inputManager->inputMap(input.guid);
-    string mapping = inputManager->sanitize(abstract->mapping, " or ");
-    inputList.setText(index++, {input.name, mapping});
-  }
-}
-
-void InputSettings::resetInput() {
-  if(MessageWindow().setParent(*settings).setText("All inputs will be erased. Are you sure you want to do this?")
-  .question() == MessageWindow::Response::No) return;
-
-  auto& device = activeDevice();
-  unsigned length = device.input.size();
-  for(unsigned n = 0; n < length; n++) {
-    activeInput = inputManager->inputMap[device.input[n].guid];
-    inputEvent(hidNull, 0, 0, 0, 1);
-  }
-}
-
-void InputSettings::eraseInput() {
-  unsigned number = activeDevice().order[inputList.selection()];
-  auto& input = activeDevice().input[number];
-  activeInput = inputManager->inputMap[input.guid];
-  inputEvent(hidNull, 0, 0, 0, 1);
-}
-
-void InputSettings::assignInput() {
-  unsigned number = activeDevice().order[inputList.selection()];
-  auto& input = activeDevice().input[number];
-  activeInput = inputManager->inputMap[input.guid];
-
-  settings->setStatusText({"Set assignment for [", activeDevice().name, "::", input.name, "] ..."});
-  settings->layout.setEnabled(false);
-  setEnabled(false);
-}
-
-void InputSettings::assignMouseInput(unsigned n) {
-  unsigned number = activeDevice().order[inputList.selection()];
-  auto& input = activeDevice().input[number];
-  activeInput = inputManager->inputMap[input.guid];
-
-  if(dynamic_cast<DigitalInput*>(activeInput)) {
-    if(auto hidMouse = inputManager->findMouse()) {
-      return inputEvent(*hidMouse, HID::Mouse::GroupID::Button, n, 0, 1, true);
-    }
-  }
-
-  if(dynamic_cast<RelativeInput*>(activeInput)) {
-    if(auto hidMouse = inputManager->findMouse()) {
-      return inputEvent(*hidMouse, HID::Mouse::GroupID::Axis, n, 0, +32767, true);
-    }
-  }
-}
-
-void InputSettings::inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue, bool allowMouseInput) {
-  if(activeInput == nullptr) return;
-  if(allowMouseInput == false && device.isMouse()) return;
-  if(activeInput->bind(device, group, input, oldValue, newValue) == false) return;
-
-  activeInput = nullptr;
-  inputChanged();
-  settings->setStatusText("");
-  settings->layout.setEnabled(true);
-  setEnabled(true);
-  synchronize();
-}
diff --git a/target-higan/settings/input.hpp b/target-higan/settings/input.hpp
deleted file mode 100644
index 20a505f3..00000000
--- a/target-higan/settings/input.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-struct InputSettings : SettingsLayout {
-  HorizontalLayout focusLayout;
-    Label focusLabel;
-    CheckLabel focusPause;
-    CheckLabel focusAllow;
-  HorizontalLayout selectionLayout;
-    ComboButton systemList;
-    ComboButton portList;
-    ComboButton deviceList;
-  ListView inputList;
-  HorizontalLayout controlLayout;
-    Button assign[3];
-    Widget spacer;
-    Button resetButton;
-    Button eraseButton;
-
-  void synchronize();
-
-  Emulator::Interface& activeSystem();
-  Emulator::Interface::Port& activePort();
-  Emulator::Interface::Device& activeDevice();
-
-  void systemChanged();
-  void portChanged();
-  void deviceChanged();
-  void inputChanged();
-  void resetInput();
-  void eraseInput();
-  void assignInput();
-  void assignMouseInput(unsigned n);
-  void inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue, bool allowMouseInput = false);
-  InputSettings();
-
-private:
-  AbstractInput* activeInput = nullptr;
-};
-
-extern InputSettings* inputSettings;
diff --git a/target-higan/settings/server.cpp b/target-higan/settings/server.cpp
deleted file mode 100644
index 44e4fd1a..00000000
--- a/target-higan/settings/server.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-ServerSettings* serverSettings = nullptr;
-
-ServerSettings::ServerSettings() {
-  hostLabel.setText("Hostname:");
-  userLabel.setText("Username:");
-  passLabel.setText("Password:");
-
-  unsigned width = min(
-    Font::size(program->normalFont, "Hostname:").width,
-    Font::size(program->normalFont, "Username:").width
-  );
-
-  append(hostLayout, {~0, 0}, 5);
-    hostLayout.append(hostLabel, {width, 0}, 5);
-    hostLayout.append(hostEdit, {~0, 0});
-  append(userLayout, {~0, 0});
-    userLayout.append(userLabel, {width, 0}, 5);
-    userLayout.append(userEdit, {~0, 0}, 5);
-    userLayout.append(passLabel, {0, 0}, 5);
-    userLayout.append(passEdit, {~0, 0});
-
-  hostEdit.setText(config->server.hostname);
-  userEdit.setText(config->server.username);
-  passEdit.setText(config->server.password);
-
-  hostEdit.onChange = [&] { config->server.hostname = hostEdit.text(); };
-  userEdit.onChange = [&] { config->server.username = userEdit.text(); };
-  passEdit.onChange = [&] { config->server.password = passEdit.text(); };
-}
diff --git a/target-higan/settings/server.hpp b/target-higan/settings/server.hpp
deleted file mode 100644
index 617c49c2..00000000
--- a/target-higan/settings/server.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-struct ServerSettings : SettingsLayout {
-  HorizontalLayout hostLayout;
-    Label hostLabel;
-    LineEdit hostEdit;
-  HorizontalLayout userLayout;
-    Label userLabel;
-    LineEdit userEdit;
-    Label passLabel;
-    LineEdit passEdit;
-
-  ServerSettings();
-};
-
-extern ServerSettings* serverSettings;
diff --git a/target-higan/settings/settings.cpp b/target-higan/settings/settings.cpp
deleted file mode 100644
index b9be5910..00000000
--- a/target-higan/settings/settings.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "../higan.hpp"
-#include "video.cpp"
-#include "audio.cpp"
-#include "input.cpp"
-#include "hotkey.cpp"
-#include "timing.cpp"
-#include "server.cpp"
-#include "advanced.cpp"
-Settings* settings = nullptr;
-
-SettingsLayout::SettingsLayout() {
-  setMargin(5);
-}
-
-Settings::Settings() {
-  setGeometry({128, 128, 640, 360});
-  windowManager->append(this, "Settings");
-
-  setTitle("Configuration Settings");
-  setStatusVisible();
-
-  layout.setMargin(5);
-  panels.append("Video", {resource::video, sizeof resource::video});
-  panels.append("Audio", {resource::audio, sizeof resource::audio});
-  panels.append("Input", {resource::input, sizeof resource::input});
-  panels.append("Hotkeys", {resource::hotkeys, sizeof resource::hotkeys});
-  panels.append("Timing", {resource::timing, sizeof resource::timing});
-  panels.append("Server", {resource::server, sizeof resource::server});
-  panels.append("Advanced", {resource::advanced, sizeof resource::advanced});
-  panels.setLayout(0, *videoSettings);
-  panels.setLayout(1, *audioSettings);
-  panels.setLayout(2, *inputSettings);
-  panels.setLayout(3, *hotkeySettings);
-  panels.setLayout(4, *timingSettings);
-  panels.setLayout(5, *serverSettings);
-  panels.setLayout(6, *advancedSettings);
-  panels.setSelection(2);
-
-  append(layout);
-  layout.append(panels, {~0, ~0});
-
-  onClose = [&] {
-    timingSettings->analysis.stop = true;
-    setVisible(false);
-  };
-}
diff --git a/target-higan/settings/settings.hpp b/target-higan/settings/settings.hpp
deleted file mode 100644
index db3e8235..00000000
--- a/target-higan/settings/settings.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-struct SettingsLayout : VerticalLayout {
-  SettingsLayout();
-};
-
-#include "video.hpp"
-#include "audio.hpp"
-#include "input.hpp"
-#include "hotkey.hpp"
-#include "timing.hpp"
-#include "server.hpp"
-#include "advanced.hpp"
-
-struct Settings : Window {
-  VerticalLayout layout;
-  TabFrame panels;
-
-  Settings();
-};
-
-extern Settings* settings;
diff --git a/target-higan/settings/timing.cpp b/target-higan/settings/timing.cpp
deleted file mode 100644
index 3fdc0c36..00000000
--- a/target-higan/settings/timing.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-TimingSettings* timingSettings = nullptr;
-
-TimingAdjustment::TimingAdjustment() {
-  assign.setEnabled(false);
-  assign.setText("Assign");
-  analyze.setText("Analyze");
-  stop.setEnabled(false);
-  stop.setText("Stop");
-
-  append(name, {40, 0});
-  append(value, {100, 0}, 5);
-  append(assign, {80, 0}, 5);
-  append(spacer, {~0, 0});
-  append(analyze, {80, 0}, 5);
-  append(stop, {80, 0});
-}
-
-TimingSettings::TimingSettings() {
-  videoAdjust.name.setText("Video:");
-  videoAdjust.value.setText({config->timing.video});
-  audioAdjust.name.setText("Audio:");
-  audioAdjust.value.setText({config->timing.audio});
-
-  append(videoAdjust, {~0, 0}, 5);
-  append(audioAdjust, {~0, 0}, 5);
-
-  videoAdjust.value.onChange = [&] { videoAdjust.assign.setEnabled(true); };
-  audioAdjust.value.onChange = [&] { audioAdjust.assign.setEnabled(true); };
-  videoAdjust.assign.onActivate = [&] {
-    config->timing.video = atof(videoAdjust.value.text());
-    videoAdjust.value.setText({config->timing.video});
-    videoAdjust.assign.setEnabled(false);
-    utility->synchronizeDSP();
-  };
-  audioAdjust.assign.onActivate = [&] {
-    config->timing.audio = atof(audioAdjust.value.text());
-    audioAdjust.value.setText({config->timing.audio});
-    audioAdjust.assign.setEnabled(false);
-    utility->synchronizeDSP();
-  };
-  videoAdjust.analyze.onActivate = {&TimingSettings::analyzeVideoFrequency, this};
-  audioAdjust.analyze.onActivate = {&TimingSettings::analyzeAudioFrequency, this};
-  videoAdjust.stop.onActivate = audioAdjust.stop.onActivate = [&] { analysis.stop = true; };
-}
-
-void TimingSettings::analyzeVideoFrequency() {
-  video.set(Video::Synchronize, true);
-  audio.set(Audio::Synchronize, false);
-  videoAdjust.stop.setEnabled(true);
-  analyzeStart();
-  do {
-    uint32_t* output;
-    unsigned pitch;
-    if(video.lock(output, pitch, 16, 16)) {
-      pitch >>= 2;
-      for(unsigned y = 0; y < 16; y++) memset(output + y * pitch, 0, 4 * 16);
-      video.unlock();
-      video.refresh();
-    }
-  } while(analyzeTick("Video"));
-  analyzeStop();
-}
-
-void TimingSettings::analyzeAudioFrequency() {
-  video.set(Video::Synchronize, false);
-  audio.set(Audio::Synchronize, true);
-  audioAdjust.stop.setEnabled(true);
-  analyzeStart();
-  do {
-    audio.sample(0, 0);
-  } while(analyzeTick("Audio"));
-  analyzeStop();
-}
-
-void TimingSettings::analyzeStart() {
-  audio.clear();
-
-//settings->panels.setEnabled(false);
-  videoAdjust.analyze.setEnabled(false);
-  audioAdjust.analyze.setEnabled(false);
-  settings->setStatusText("Initializing ...");
-  Application::processEvents();
-
-  analysis.stop = false;
-  analysis.seconds = 0;
-  analysis.counter = 0;
-  analysis.sample.reset();
-  analysis.systemTime = time(0);
-}
-
-bool TimingSettings::analyzeTick(string type) {
-  analysis.counter++;
-
-  time_t systemTime = time(0);
-  if(systemTime > analysis.systemTime) {
-    analysis.systemTime = systemTime;
-    Application::processEvents();
-
-    if(analysis.seconds < 3) {
-      analysis.seconds++;
-    } else {
-      analysis.sample.append(analysis.counter);
-      uintmax_t sum = 0;
-      for(auto& point : analysis.sample) sum += point;
-      settings->setStatusText({
-        type, " sample rate: ", (double)sum / analysis.sample.size(), "hz",
-        " (", analysis.sample.size(), " sample points)"
-      });
-    }
-
-    analysis.counter = 0;
-  }
-
-  return !analysis.stop;
-}
-
-void TimingSettings::analyzeStop() {
-  video.set(Video::Synchronize, config->video.synchronize);
-  audio.set(Audio::Synchronize, config->audio.synchronize);
-
-//settings->panels.setEnabled(true);
-  videoAdjust.analyze.setEnabled(true);
-  audioAdjust.analyze.setEnabled(true);
-  videoAdjust.stop.setEnabled(false);
-  audioAdjust.stop.setEnabled(false);
-}
diff --git a/target-higan/settings/timing.hpp b/target-higan/settings/timing.hpp
deleted file mode 100644
index 8c89cc9b..00000000
--- a/target-higan/settings/timing.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-struct TimingAdjustment : HorizontalLayout {
-  Label name;
-  LineEdit value;
-  Button assign;
-  Widget spacer;
-  Button analyze;
-  Button stop;
-
-  TimingAdjustment();
-};
-
-struct TimingSettings : SettingsLayout {
-  TimingAdjustment videoAdjust;
-  TimingAdjustment audioAdjust;
-
-  void analyzeVideoFrequency();
-  void analyzeAudioFrequency();
-
-  void analyzeStart();
-  bool analyzeTick(string type);
-  void analyzeStop();
-
-  TimingSettings();
-
-  struct Analysis {
-    bool stop;
-    unsigned seconds;
-    unsigned counter;
-    vector<unsigned> sample;
-    time_t systemTime;
-  } analysis;
-};
-
-extern TimingSettings* timingSettings;
diff --git a/target-higan/settings/video.cpp b/target-higan/settings/video.cpp
deleted file mode 100644
index 6d33aadf..00000000
--- a/target-higan/settings/video.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-VideoSettings* videoSettings = nullptr;
-
-VideoSlider::VideoSlider() {
-  append(name, {75, 0});
-  append(value, {75, 0});
-  append(slider, {~0, 0});
-}
-
-VideoSettings::VideoSettings() {
-  colorAdjustment.setFont(program->boldFont);
-  colorAdjustment.setText("Color adjustment:");
-  saturation.name.setText("Saturation:");
-  saturation.slider.setLength(201);
-  gamma.name.setText("Gamma:");
-  gamma.slider.setLength(101);
-  luminance.name.setText("Luminance:");
-  luminance.slider.setLength(101);
-  colorEmulation.setText("Color emulation");
-  overscanAdjustment.setFont(program->boldFont);
-  overscanAdjustment.setText("Overscan mask:");
-  overscanHorizontal.name.setText("Horizontal:");
-  overscanHorizontal.slider.setLength(17);
-  overscanVertical.name.setText("Vertical:");
-  overscanVertical.slider.setLength(17);
-
-  append(colorAdjustment, {~0, 0});
-  append(saturation, {~0, 0});
-  append(gamma, {~0, 0});
-  append(luminance, {~0, 0});
-  append(colorEmulation, {~0, 0}, 5);
-  append(overscanAdjustment, {~0, 0});
-  append(overscanHorizontal, {~0, 0});
-  append(overscanVertical, {~0, 0}, 5);
-
-  colorEmulation.setChecked(config->video.colorEmulation);
-  saturation.slider.setPosition(config->video.saturation);
-  gamma.slider.setPosition(config->video.gamma - 100);
-  luminance.slider.setPosition(config->video.luminance);
-  overscanHorizontal.slider.setPosition(config->video.maskOverscan.horizontal);
-  overscanVertical.slider.setPosition(config->video.maskOverscan.vertical);
-
-  synchronize();
-
-  saturation.slider.onChange = gamma.slider.onChange = luminance.slider.onChange = colorEmulation.onToggle =
-  overscanHorizontal.slider.onChange = overscanVertical.slider.onChange =
-  {&VideoSettings::synchronize, this};
-}
-
-void VideoSettings::synchronize() {
-  config->video.saturation = saturation.slider.position();
-  config->video.gamma = 100 + gamma.slider.position();
-  config->video.luminance = luminance.slider.position();
-  config->video.colorEmulation = colorEmulation.checked();
-  config->video.maskOverscan.horizontal = overscanHorizontal.slider.position();
-  config->video.maskOverscan.vertical = overscanVertical.slider.position();
-
-  saturation.value.setText({config->video.saturation, "%"});
-  gamma.value.setText({config->video.gamma, "%"});
-  luminance.value.setText({config->video.luminance, "%"});
-  overscanHorizontal.value.setText({config->video.maskOverscan.horizontal, "px"});
-  overscanVertical.value.setText({config->video.maskOverscan.vertical, "px"});
-
-  utility->updatePalette();
-}
diff --git a/target-higan/settings/video.hpp b/target-higan/settings/video.hpp
deleted file mode 100644
index 3e47f882..00000000
--- a/target-higan/settings/video.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-struct VideoSlider : HorizontalLayout {
-  Label name;
-  Label value;
-  HorizontalSlider slider;
-
-  VideoSlider();
-};
-
-struct VideoSettings : SettingsLayout {
-  Label colorAdjustment;
-  VideoSlider saturation;
-  VideoSlider gamma;
-  VideoSlider luminance;
-  CheckLabel colorEmulation;
-  Label overscanAdjustment;
-  VideoSlider overscanHorizontal;
-  VideoSlider overscanVertical;
-
-  void synchronize();
-  VideoSettings();
-};
-
-extern VideoSettings* videoSettings;
diff --git a/target-higan/tools/cheat-database.cpp b/target-higan/tools/cheat-database.cpp
deleted file mode 100644
index 5063188a..00000000
--- a/target-higan/tools/cheat-database.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-CheatDatabase* cheatDatabase = nullptr;
-
-CheatDatabase::CheatDatabase() {
-  setGeometry({256, 256, 600, 360});
-  windowManager->append(this, "CheatDatabase");
-
-  layout.setMargin(5);
-  cheatList.setCheckable();
-  selectAllButton.setText("Select All");
-  unselectAllButton.setText("Unselect All");
-  acceptButton.setText("Add Codes");
-
-  append(layout);
-  layout.append(cheatList, {~0, ~0}, 5);
-  layout.append(controlLayout, {~0, 0});
-    controlLayout.append(selectAllButton, {100, 0}, 5);
-    controlLayout.append(unselectAllButton, {100, 0}, 5);
-    controlLayout.append(spacer, {~0, 0});
-    controlLayout.append(acceptButton, {80, 0});
-
-  selectAllButton.onActivate = [&] {
-    for(unsigned n = 0; n < cheat.size(); n++) cheatList.setChecked(n, true);
-  };
-
-  unselectAllButton.onActivate = [&] {
-    for(unsigned n = 0; n < cheat.size(); n++) cheatList.setChecked(n, false);
-  };
-
-  acceptButton.onActivate = {&CheatDatabase::addCodes, this};
-}
-
-void CheatDatabase::findCodes() {
-  const string sha256 = system().sha256();
-  cheatList.reset();
-  cheat.reset();
-
-  auto document = Markup::Document(string::read(program->path("cheats.bml")));
-  for(auto& node : document) {
-    if(node.name != "cartridge") continue;
-    if(node["sha256"].text() != sha256) continue;
-
-    setTitle(node["name"].text());
-    for(auto& cheat : node) {
-      if(cheat.name != "cheat") continue;
-      cheatList.append(cheat["description"].text());
-      this->cheat.append({cheat["code"].text(), cheat["description"].text()});
-    }
-
-    setVisible();
-    return;
-  }
-
-  MessageWindow().setParent(*tools).setText("Sorry, no cheat codes were found.").information();
-}
-
-void CheatDatabase::addCodes() {
-  for(unsigned n = 0; n < cheat.size(); n++) {
-    if(cheatList.checked(n) == false) continue;
-    if(cheatEditor->import(cheat[n].code, cheat[n].desc) == false) {
-      MessageWindow().setParent(*this).setText("Ran out of empty slots for cheat codes.\nNot all cheat codes were added.").warning();
-      break;
-    }
-  }
-
-  setVisible(false);
-  cheatEditor->synchronize();
-  cheatEditor->refresh();
-}
diff --git a/target-higan/tools/cheat-database.hpp b/target-higan/tools/cheat-database.hpp
deleted file mode 100644
index 3d0ea5d1..00000000
--- a/target-higan/tools/cheat-database.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-struct CheatDatabase : Window {
-  VerticalLayout layout;
-  ListView cheatList;
-  HorizontalLayout controlLayout;
-    Button selectAllButton;
-    Button unselectAllButton;
-    Widget spacer;
-    Button acceptButton;
-
-  void findCodes();
-  void addCodes();
-  CheatDatabase();
-
-private:
-  struct Cheat {
-    string code;
-    string desc;
-  };
-  vector<Cheat> cheat;
-};
-
-extern CheatDatabase* cheatDatabase;
diff --git a/target-higan/tools/cheat-editor.cpp b/target-higan/tools/cheat-editor.cpp
deleted file mode 100644
index 010b8da8..00000000
--- a/target-higan/tools/cheat-editor.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-CheatEditor* cheatEditor = nullptr;
-
-CheatEditor::CheatEditor() {
-  cheatList.setHeaderText({"Slot", "Code", "Description"});
-  cheatList.setHeaderVisible();
-  cheatList.setCheckable();
-  for(unsigned n = 0; n < Codes; n++) cheatList.append({"", "", ""});
-  codeLabel.setText("Code(s):");
-  descLabel.setText("Description:");
-  findButton.setText("Find Codes ...");
-  resetButton.setText("Reset");
-  eraseButton.setText("Erase");
-  unsigned width = max(
-    Font::size(program->normalFont, "Codes(s)"    ).width,
-    Font::size(program->normalFont, "Description:").width
-  );
-
-  append(cheatList, {~0, ~0}, 5);
-  append(codeLayout, {~0, 0}, 5);
-    codeLayout.append(codeLabel, {width, 0}, 5);
-    codeLayout.append(codeEdit, {~0, 0});
-  append(descLayout, {~0, 0}, 5);
-    descLayout.append(descLabel, {width, 0}, 5);
-    descLayout.append(descEdit, {~0, 0});
-  append(controlLayout, {~0, 0});
-    controlLayout.append(findButton, {0, 0}, 5);
-    controlLayout.append(spacer, {~0, 0});
-    controlLayout.append(resetButton, {80, 0}, 5);
-    controlLayout.append(eraseButton, {80, 0});
-
-  cheatList.onChange = {&CheatEditor::synchronize, this};
-  cheatList.onToggle = [&](unsigned) { update(); };
-  codeEdit.onChange = {&CheatEditor::updateCode, this};
-  descEdit.onChange = {&CheatEditor::updateDesc, this};
-  findButton.onActivate = {&CheatDatabase::findCodes, cheatDatabase};
-  resetButton.onActivate = [&] {
-    if(MessageWindow().setParent(*tools).setText("All codes will be erased. Are you sure you want to do this?")
-    .question() == MessageWindow::Response::Yes) reset();
-  };
-  eraseButton.onActivate = {&CheatEditor::erase, this};
-
-  cheatList.setSelection(0);
-  synchronize();
-}
-
-void CheatEditor::synchronize() {
-  setEnabled(program->active);
-
-  if(cheatList.selected()) {
-    unsigned n = cheatList.selection();
-    codeEdit.setText(cheat[n].code);
-    descEdit.setText(cheat[n].desc);
-    codeEdit.setEnabled(true);
-    descEdit.setEnabled(true);
-    eraseButton.setEnabled(true);
-  } else {
-    codeEdit.setText("");
-    descEdit.setText("");
-    codeEdit.setEnabled(false);
-    descEdit.setEnabled(false);
-    eraseButton.setEnabled(false);
-  }
-}
-
-void CheatEditor::refresh() {
-  for(unsigned n = 0; n < Codes; n++) {
-    string code = cheat[n].code;
-    string desc = cheat[n].code.empty() && cheat[n].desc.empty() ? "(empty)" : cheat[n].desc;
-    lstring codes = code.split("+");
-    if(codes.size() > 1) code = {codes[0], "+..."};
-    cheatList.setText(n, {format<3>(1 + n), code, desc});
-  }
-  cheatList.autoSizeColumns();
-}
-
-void CheatEditor::update() {
-  lstring codes;
-  for(unsigned n = 0; n < Codes; n++) {
-    string code = cheat[n].code;
-    if(cheatList.checked(n) && !code.empty()) codes.append(code);
-  }
-  system().cheatSet(codes);
-}
-
-void CheatEditor::reset() {
-  for(unsigned n = 0; n < Codes; n++) {
-    cheatList.setChecked(n, false);
-    cheat[n].code = "";
-    cheat[n].desc = "";
-  }
-  codeEdit.setText("");
-  descEdit.setText("");
-  refresh();
-  update();
-}
-
-void CheatEditor::erase() {
-  unsigned n = cheatList.selection();
-  cheatList.setChecked(n, false);
-  cheat[n].code = "";
-  cheat[n].desc = "";
-  codeEdit.setText("");
-  descEdit.setText("");
-  refresh();
-  update();
-}
-
-void CheatEditor::updateCode() {
-  unsigned n = cheatList.selection();
-  cheat[n].code = codeEdit.text();
-  refresh();
-  update();
-}
-
-void CheatEditor::updateDesc() {
-  unsigned n = cheatList.selection();
-  cheat[n].desc = descEdit.text();
-  refresh();
-}
-
-bool CheatEditor::load(string filename) {
-  string data = string::read(filename);
-  if(data.empty()) return false;
-
-  unsigned n = 0;
-  auto document = Markup::Document(data);
-  for(auto& node : document["cartridge"]) {
-    if(node.name != "cheat") continue;
-    cheatList.setChecked(n, node["enabled"].exists());
-    cheat[n].code = node["code"].text();
-    cheat[n].desc = node["description"].text();
-    if(++n >= Codes) break;
-  }
-
-  refresh();
-  update();
-  return true;
-}
-
-bool CheatEditor::save(string filename) {
-  signed lastSave = -1;
-  for(signed n = 127; n >= 0; n--) {
-    if(!cheat[n].code.empty() || !cheat[n].desc.empty()) {
-      lastSave = n;
-      break;
-    }
-  }
-
-  if(lastSave == -1) {
-    file::remove(filename);
-    return true;
-  }
-
-  file fp;
-  if(fp.open(filename, file::mode::write) == false) return false;
-
-  fp.print("cartridge sha256:", system().sha256(), "\n");
-  for(unsigned n = 0; n <= lastSave; n++) {
-    fp.print("  cheat", cheatList.checked(n) ? " enabled\n" : "\n");
-    fp.print("    description:", cheat[n].desc, "\n");
-    fp.print("    code:", cheat[n].code, "\n");
-  }
-  fp.close();
-
-  return true;
-}
-
-bool CheatEditor::import(string code, string desc) {
-  for(unsigned n = 0; n < Codes; n++) {
-    if(cheat[n].code.empty() && cheat[n].desc.empty()) {
-      cheatList.setChecked(n, false);
-      cheat[n].code = code;
-      cheat[n].desc = desc;
-      return true;
-    }
-  }
-  return false;
-}
diff --git a/target-higan/tools/cheat-editor.hpp b/target-higan/tools/cheat-editor.hpp
deleted file mode 100644
index 0a35317c..00000000
--- a/target-higan/tools/cheat-editor.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-struct CheatEditor : ToolsLayout {
-  ListView cheatList;
-  HorizontalLayout codeLayout;
-    Label codeLabel;
-    LineEdit codeEdit;
-  HorizontalLayout descLayout;
-    Label descLabel;
-    LineEdit descEdit;
-  HorizontalLayout controlLayout;
-    Button findButton;
-    Widget spacer;
-    Button resetButton;
-    Button eraseButton;
-
-  void reset();
-  void erase();
-  void updateCode();
-  void updateDesc();
-
-  bool load(string filename);
-  bool save(string filename);
-  bool import(string code, string desc);
-
-  void update();
-  void refresh();
-  void synchronize();
-  CheatEditor();
-
-private:
-  enum : unsigned { Codes = 128 };
-  struct Cheat {
-    string code;
-    string desc;
-  };
-  Cheat cheat[Codes];
-};
-
-extern CheatEditor* cheatEditor;
diff --git a/target-higan/tools/state-manager.cpp b/target-higan/tools/state-manager.cpp
deleted file mode 100644
index fde3cb96..00000000
--- a/target-higan/tools/state-manager.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-StateManager* stateManager = nullptr;
-
-StateManager::StateManager() {
-  stateList.setHeaderText({"Slot", "Description"});
-  stateList.setHeaderVisible();
-  for(unsigned n = 0; n < Slots; n++) stateList.append({format<2>(1 + n), "(empty)"});
-  stateList.autoSizeColumns();
-  descLabel.setText("Description:");
-  saveButton.setText("Save");
-  loadButton.setText("Load");
-  resetButton.setText("Reset");
-  eraseButton.setText("Erase");
-
-  append(stateList, {~0, ~0}, 5);
-  append(descLayout, {~0, 0}, 5);
-    descLayout.append(descLabel, {0, 0}, 5);
-    descLayout.append(descEdit, {~0, 0});
-  append(controlLayout, {~0, 0});
-    controlLayout.append(saveButton, {80, 0}, 5);
-    controlLayout.append(loadButton, {80, 0}, 5);
-    controlLayout.append(spacer, {~0, 0});
-    controlLayout.append(resetButton, {80, 0}, 5);
-    controlLayout.append(eraseButton, {80, 0});
-
-  stateList.onChange = {&StateManager::synchronize, this};
-  stateList.onActivate = {&StateManager::slotLoad, this};
-  descEdit.onChange = {&StateManager::slotSaveDescription, this};
-  saveButton.onActivate = {&StateManager::slotSave, this};
-  loadButton.onActivate = {&StateManager::slotLoad, this};
-  resetButton.onActivate = [&] {
-    if(MessageWindow().setParent(*tools).setText("All states will be erased. Are you sure you want to do this?")
-    .question() == MessageWindow::Response::Yes) reset();
-  };
-  eraseButton.onActivate = {&StateManager::slotErase, this};
-
-  stateList.setSelection(0);
-  synchronize();
-}
-
-void StateManager::synchronize() {
-  setEnabled(program->active);
-
-  descEdit.setText("");
-  descEdit.setEnabled(false);
-  controlLayout.setEnabled(stateList.selected());
-  if(stateList.selected() == false) return;
-
-  if(slot[stateList.selection()].capacity() > 0) {
-    descEdit.setText(slotLoadDescription(stateList.selection()));
-    descEdit.setEnabled(true);
-  }
-}
-
-void StateManager::refresh() {
-  for(unsigned n = 0; n < Slots; n++) {
-    stateList.setText(n, {format<2>(1 + n), slotLoadDescription(n)});
-  }
-  stateList.autoSizeColumns();
-}
-
-void StateManager::reset() {
-  for(auto& slot : this->slot) slot = serializer();
-  synchronize();
-  refresh();
-}
-
-bool StateManager::load(string filename, unsigned revision) {
-  for(auto& slot : this->slot) slot = serializer();
-  synchronize();
-
-  file fp;
-  if(fp.open(filename, file::mode::read) == false) return false;
-
-  if(fp.readl(4) == 0x31415342 /* 'BSA1' */ && fp.readl(4) == revision) {
-    for(auto &slot : this->slot) {
-      if(fp.read() == false) continue;  //slot is empty
-      unsigned size = fp.readl(4);
-      uint8_t* data = new uint8_t[size];
-      fp.read(data, size);
-      slot = serializer(data, size);
-      delete[] data;
-    }
-  }
-
-  refresh();
-  synchronize();
-  return true;
-}
-
-bool StateManager::save(string filename, unsigned revision) {
-  bool hasSave = false;
-  for(auto& slot : this->slot) hasSave |= slot.capacity() > 0;
-  if(hasSave == false) {
-    file::remove(filename);
-    return true;
-  }
-
-  directory::create(dir(filename));
-
-  file fp;
-  if(fp.open(filename, file::mode::write) == false) return false;
-
-  fp.writel(0x31415342, 4);  //'BSA1'
-  fp.writel(revision, 4);
-  for(auto& slot : this->slot) {
-    fp.write(slot.capacity() > 0);
-    if(slot.capacity()) {
-      fp.writel(slot.capacity(), 4);
-      fp.write(slot.data(), slot.capacity());
-    }
-  }
-
-  return true;
-}
-
-void StateManager::slotLoad() {
-  if(stateList.selected() == false) return;
-  serializer s(slot[stateList.selection()].data(), slot[stateList.selection()].capacity());
-  system().unserialize(s);
-}
-
-void StateManager::slotSave() {
-  if(stateList.selected()) {
-    slot[stateList.selection()] = system().serialize();
-  }
-  refresh();
-  synchronize();
-  descEdit.setFocused();
-}
-
-void StateManager::slotErase() {
-  if(stateList.selected()) {
-    slot[stateList.selection()] = serializer();
-  }
-  refresh();
-  synchronize();
-}
-
-string StateManager::slotLoadDescription(unsigned id) {
-  if(slot[id].capacity() == 0) return "(empty)";
-  char text[DescriptionLength];
-  strmcpy(text, (const char*)slot[id].data() + HeaderLength, DescriptionLength);
-  return text;
-}
-
-void StateManager::slotSaveDescription() {
-  if(stateList.selected() == false) return;
-  string text = descEdit.text();
-  if(slot[stateList.selection()].capacity() > 0) {
-    strmcpy((char*)slot[stateList.selection()].data() + HeaderLength, (const char*)text, DescriptionLength);
-  }
-  refresh();
-}
diff --git a/target-higan/tools/state-manager.hpp b/target-higan/tools/state-manager.hpp
deleted file mode 100644
index daf06001..00000000
--- a/target-higan/tools/state-manager.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-struct StateManager : ToolsLayout {
-  ListView stateList;
-  HorizontalLayout descLayout;
-    Label descLabel;
-    LineEdit descEdit;
-  HorizontalLayout controlLayout;
-    Button saveButton;
-    Button loadButton;
-    Widget spacer;
-    Button resetButton;
-    Button eraseButton;
-
-  void reset();
-  bool load(string filename, unsigned revision);
-  bool save(string filename, unsigned revision);
-
-  void slotLoad();
-  void slotSave();
-  void slotErase();
-  string slotLoadDescription(unsigned id);
-  void slotSaveDescription();
-
-  void refresh();
-  void synchronize();
-  StateManager();
-
-private:
-  enum : unsigned {
-    Slots = 32,
-    HeaderLength = 72,
-    DescriptionLength = 512,
-  };
-  serializer slot[Slots];
-};
-
-extern StateManager* stateManager;
diff --git a/target-higan/tools/tools.cpp b/target-higan/tools/tools.cpp
deleted file mode 100644
index 894b7d9b..00000000
--- a/target-higan/tools/tools.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "../higan.hpp"
-#include "cheat-database.cpp"
-#include "cheat-editor.cpp"
-#include "state-manager.cpp"
-Tools* tools = nullptr;
-
-ToolsLayout::ToolsLayout() {
-  setMargin(5);
-}
-
-Tools::Tools() {
-  setGeometry({128, 128, 640, 360});
-  windowManager->append(this, "Tools");
-
-  setTitle("Tools");
-
-  layout.setMargin(5);
-  panels.append("Cheat Editor", {resource::cheatEditor, sizeof resource::cheatEditor});
-  panels.append("State Manager", {resource::stateManager, sizeof resource::stateManager});
-  panels.setLayout(0, *cheatEditor);
-  panels.setLayout(1, *stateManager);
-
-  append(layout);
-  layout.append(panels, {~0, ~0});
-}
diff --git a/target-higan/tools/tools.hpp b/target-higan/tools/tools.hpp
deleted file mode 100644
index 06680369..00000000
--- a/target-higan/tools/tools.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-struct ToolsLayout : VerticalLayout {
-  ToolsLayout();
-};
-
-#include "cheat-database.hpp"
-#include "cheat-editor.hpp"
-#include "state-manager.hpp"
-
-struct Tools : Window {
-  VerticalLayout layout;
-  TabFrame panels;
-
-  Tools();
-};
-
-extern Tools* tools;
diff --git a/target-higan/utility/utility.cpp b/target-higan/utility/utility.cpp
deleted file mode 100644
index 2645092a..00000000
--- a/target-higan/utility/utility.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-#include "../higan.hpp"
-
-Utility* utility = nullptr;
-
-void Utility::setInterface(Emulator::Interface* emulator) {
-  program->active = emulator;
-  presentation->synchronize();
-}
-
-//load from command-line, etc
-void Utility::loadMedia(string pathname) {
-  pathname.transform("\\", "/");
-  if(pathname.endsWith("/")) pathname.rtrim("/");
-
-  if(!directory::exists(pathname)) return;
-  string type = extension(pathname);
-
-  //determine type by comparing extension against all emulation cores
-  for(auto& emulator : program->emulator) {
-    for(auto& media : emulator->media) {
-      if(media.bootable == false) continue;
-      if(type != media.type) continue;
-      loadMedia(emulator, media, {pathname, "/"});
-      libraryManager->setVisible(false);
-      return;
-    }
-  }
-
-  MessageWindow().setText("Unable to determine media type.").warning();
-}
-
-//load base cartridge
-void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media, string pathname) {
-  unload();
-  setInterface(emulator);
-  path(0) = program->path({media.name, ".sys/"});
-  path(media.id) = pathname;
-  this->pathname.append(pathname);
-
-  system().load(media.id);
-  system().power();
-
-  presentation->setSystemName(media.name);
-  presentation->setVisible();
-  load();
-}
-
-//request from emulation core to load non-volatile media folder
-void Utility::loadRequest(unsigned id, string name, string type) {
-  string pathname = libraryManager->load(type);
-  if(pathname.empty()) return;
-  path(id) = pathname;
-  this->pathname.append(pathname);
-
-  system().load(id);
-}
-
-//request from emulation core to load non-volatile media file
-void Utility::loadRequest(unsigned id, string path) {
-  string pathname = {this->path(system().group(id)), path};
-  if(file::exists(pathname) == false) return;
-  mmapstream stream(pathname);
-  return system().load(id, stream);
-}
-
-//request from emulation core to save non-volatile media file
-void Utility::saveRequest(unsigned id, string path) {
-  string pathname = {this->path(system().group(id)), path};
-  filestream stream(pathname, file::mode::write);
-  return system().save(id, stream);
-}
-
-void Utility::connect(unsigned port, unsigned device) {
-  if(program->active == nullptr) return;
-  system().connect(port, device);
-}
-
-void Utility::power() {
-  if(program->active == nullptr) return;
-  system().power();
-}
-
-void Utility::reset() {
-  if(program->active == nullptr) return;
-  system().reset();
-}
-
-void Utility::load() {
-  presentation->setTitle(system().title());
-
-  cheatEditor->load({pathname[0], "cheats.bml"});
-  stateManager->load({pathname[0], "higan/states.bsa"}, 1);
-
-  synchronizeDSP();
-
-  resize();
-  updateShader();
-  cheatEditor->synchronize();
-  cheatEditor->refresh();
-}
-
-void Utility::unload() {
-  if(program->active == nullptr) return;
-
-  cheatEditor->save({pathname[0], "cheats.bml"});
-  stateManager->save({pathname[0], "higan/states.bsa"}, 1);
-
-  system().unload();
-  path.reset();
-  pathname.reset();
-  cheatEditor->reset();
-  stateManager->reset();
-  setInterface(nullptr);
-
-  video.clear();
-  audio.clear();
-  presentation->setTitle({Emulator::Name, " v", Emulator::Version});
-  cheatDatabase->setVisible(false);
-  cheatEditor->setVisible(false);
-  stateManager->setVisible(false);
-}
-
-void Utility::saveState(unsigned slot) {
-  if(program->active == nullptr) return;
-  serializer s = system().serialize();
-  if(s.size() == 0) return;
-  directory::create({pathname[0], "higan/"});
-  if(file::write({pathname[0], "higan/state-", slot, ".bsa"}, s.data(), s.size()) == false);
-  showMessage({"Saved to slot ", slot});
-}
-
-void Utility::loadState(unsigned slot) {
-  if(program->active == nullptr) return;
-  auto memory = file::read({pathname[0], "higan/state-", slot, ".bsa"});
-  if(memory.size() == 0) return showMessage({"Unable to locate slot ", slot, " state"});
-  serializer s(memory.data(), memory.size());
-  if(system().unserialize(s) == false) return showMessage({"Slot ", slot, " state incompatible"});
-  showMessage({"Loaded from slot ", slot});
-}
-
-void Utility::synchronizeDSP() {
-  if(program->active == nullptr) return;
-
-  if(config->video.synchronize == false) {
-    return dspaudio.setFrequency(system().audioFrequency());
-  }
-
-  double inputRatio = system().audioFrequency() / system().videoFrequency();
-  double outputRatio = config->timing.audio / config->timing.video;
-  double frequency = inputRatio / outputRatio * config->audio.frequency;
-
-  dspaudio.setFrequency(frequency);
-}
-
-void Utility::synchronizeRuby() {
-  video.set(Video::Synchronize, config->video.synchronize);
-  audio.set(Audio::Synchronize, config->audio.synchronize);
-  audio.set(Audio::Frequency, config->audio.frequency);
-  audio.set(Audio::Latency, config->audio.latency);
-
-  switch(config->audio.resampler) {
-  case 0: dspaudio.setResampler(DSP::ResampleEngine::Linear);  break;
-  case 1: dspaudio.setResampler(DSP::ResampleEngine::Hermite); break;
-  case 2: dspaudio.setResampler(DSP::ResampleEngine::Sinc);    break;
-  }
-  dspaudio.setResamplerFrequency(config->audio.frequency);
-  dspaudio.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
-  synchronizeDSP();
-}
-
-void Utility::updatePalette() {
-  if(program->active == nullptr) return;
-
-  if(config->video.shader == "Display Emulation" && config->video.driver == "OpenGL") {
-    system().paletteUpdate(Emulator::Interface::PaletteMode::Channel);
-  } else if(config->video.colorEmulation) {
-    system().paletteUpdate(Emulator::Interface::PaletteMode::Emulation);
-  } else {
-    system().paletteUpdate(Emulator::Interface::PaletteMode::Standard);
-  }
-}
-
-void Utility::updateShader() {
-  if(config->video.shader == "None") {
-    video.set(Video::Shader, (const char*)"");
-    video.set(Video::Filter, Video::FilterNearest);
-  } else if(config->video.shader == "Blur") {
-    video.set(Video::Shader, (const char*)"");
-    video.set(Video::Filter, Video::FilterLinear);
-  } else if(config->video.shader == "Display Emulation" && config->video.driver != "OpenGL") {
-    video.set(Video::Shader, (const char*)"");
-    video.set(Video::Filter, Video::FilterLinear);
-  } else if(config->video.shader == "Display Emulation") {
-    if(program->active) {
-      string pathname = program->path("Video Shaders/");
-      pathname.append("Display Emulation/");
-      pathname.append(presentation->systemName, ".shader/");
-      if(directory::exists(pathname)) {
-        video.set(Video::Shader, (const char*)pathname);
-      } else {
-        video.set(Video::Shader, (const char*)"");
-        video.set(Video::Filter, Video::FilterLinear);
-      }
-    } else {
-      video.set(Video::Shader, (const char*)"");
-      video.set(Video::Filter, Video::FilterLinear);
-    }
-  } else {
-    video.set(Video::Shader, (const char*)config->video.shader);
-  }
-  updatePalette();
-}
-
-void Utility::resize(bool resizeWindow) {
-  if(program->active == nullptr) {
-    auto geometry = presentation->geometry();
-    presentation->viewport.setGeometry({0, 0, geometry.width, geometry.height});
-    return;
-  }
-
-  Geometry geometry = presentation->geometry();
-  unsigned width  = system().information.width;
-  unsigned height = system().information.height;
-
-  unsigned scaledWidth  = geometry.width  / width;
-  unsigned scaledHeight = geometry.height / height;
-  unsigned multiplier   = max(1u, min(scaledWidth, scaledHeight));
-
-  if(config->video.aspectCorrection) {
-    width *= system().information.aspectRatio;
-  }
-
-  width  *= multiplier;
-  height *= multiplier;
-
-  unsigned scaleMode = 0;
-
-  if(config->video.scaleMode == 1) {
-    width  = (double)width * ((double)geometry.height / height);
-    height = geometry.height;
-  }
-
-  if(config->video.scaleMode == 2) {
-    width  = geometry.width;
-    height = geometry.height;
-  }
-
-  if(resizeWindow == false) {
-    if(geometry.width  < width ) width  = geometry.width;
-    if(geometry.height < height) height = geometry.height;
-
-    presentation->viewport.setGeometry({
-      (geometry.width - width) / 2, (geometry.height - height) / 2, width, height
-    });
-  } else {
-    presentation->setGeometry({geometry.x, geometry.y, width, height});
-    presentation->viewport.setGeometry({0, 0, width, height});
-  }
-
-  presentation->synchronize();
-}
-
-void Utility::toggleFullScreen() {
-  static Geometry geometry;
-
-  if(presentation->fullScreen() == false) {
-    geometry = presentation->geometry();
-    presentation->setMenuVisible(false);
-    presentation->setStatusVisible(false);
-    presentation->setFullScreen(true);
-    input.acquire();
-  } else {
-    input.unacquire();
-    presentation->setMenuVisible(true);
-    presentation->setStatusVisible(true);
-    presentation->setFullScreen(false);
-    presentation->setGeometry(geometry);
-  }
-
-  resize();
-}
-
-void Utility::updateStatus() {
-  time_t currentTime = time(0);
-  string text;
-  if((currentTime - statusTime) <= 2) {
-    text = statusMessage;
-  } else if(program->active == nullptr) {
-    text = "No cartridge loaded";
-  } else if(program->pause || program->autopause) {
-    text = "Paused";
-  } else {
-    text = statusText;
-  }
-  if(text != presentation->statusText()) {
-    presentation->setStatusText(text);
-  }
-}
-
-void Utility::setStatusText(string text) {
-  statusText = text;
-}
-
-void Utility::showMessage(string message) {
-  statusTime = time(0);
-  statusMessage = message;
-}
-
-string Utility::libraryPath() {
-  string path = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").transform("\\", "/");
-  if(path.empty()) path = {userpath(), "Emulation/"};
-  if(path.endsWith("/") == false) path.append("/");
-  return path;
-}
diff --git a/target-higan/utility/utility.hpp b/target-higan/utility/utility.hpp
deleted file mode 100644
index 5b3fb92c..00000000
--- a/target-higan/utility/utility.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-struct Utility {
-  void setInterface(Emulator::Interface* emulator);
-
-  void loadMedia(string pathname);
-  void loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media, string pathname);
-
-  void loadRequest(unsigned id, string name, string type);
-  void loadRequest(unsigned id, string path);
-  void saveRequest(unsigned id, string path);
-
-  void connect(unsigned port, unsigned device);
-  void power();
-  void reset();
-  void load();
-  void unload();
-
-  void saveState(unsigned slot);
-  void loadState(unsigned slot);
-
-  void synchronizeDSP();
-  void synchronizeRuby();
-  void updatePalette();
-  void updateShader();
-  void resize(bool resizeWindow = false);
-  void toggleFullScreen();
-
-  void updateStatus();
-  void setStatusText(string text);
-  void showMessage(string message);
-
-  string libraryPath();
-
-  lstring path;
-  lstring pathname;
-
-private:
-  string statusText;
-  string statusMessage;
-  time_t statusTime = 0;
-};
-
-extern Utility* utility;
diff --git a/target-higan/window/window.cpp b/target-higan/window/window.cpp
deleted file mode 100644
index 582f3817..00000000
--- a/target-higan/window/window.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "../higan.hpp"
-WindowManager* windowManager = nullptr;
-
-void WindowManager::append(Window* window, string name) {
-  window->setMenuFont(program->normalFont);
-  window->setWidgetFont(program->normalFont);
-  window->setStatusFont(program->boldFont);
-  windowList.append({window, name, window->geometry().text()});
-}
-
-void WindowManager::loadGeometry() {
-  static bool initialized = false;
-  if(initialized == false) {
-    initialized = true;
-    Configuration::Node geometry;
-    for(auto& window : windowList) {
-      geometry.append(window.geometry, window.name);
-    }
-    config.append(geometry, "Geometry");
-  }
-  config.load(program->path("geometry.bml"));
-  config.save(program->path("geometry.bml"));
-  for(auto& window : windowList) {
-    window.window->setGeometry(window.geometry);
-  }
-}
-
-void WindowManager::saveGeometry() {
-  for(auto& window : windowList) {
-    window.geometry = window.window->geometry().text();
-  }
-  config.save(program->path("geometry.bml"));
-}
-
-void WindowManager::hideAll() {
-  for(auto& window : windowList) {
-    window.window->setVisible(false);
-  }
-}
diff --git a/target-higan/window/window.hpp b/target-higan/window/window.hpp
deleted file mode 100644
index caa81e5d..00000000
--- a/target-higan/window/window.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-struct WindowManager {
-  struct WindowList {
-    Window* window;
-    string name;
-    string geometry;
-  };
-  vector<WindowList> windowList;
-
-  void append(Window* window, string name);
-  void loadGeometry();
-  void saveGeometry();
-  void hideAll();
-
-private:
-  Configuration::Document config;
-};
-
-extern WindowManager* windowManager;
diff --git a/target-loki/Makefile b/target-loki/Makefile
deleted file mode 100644
index 28b9d9be..00000000
--- a/target-loki/Makefile
+++ /dev/null
@@ -1,96 +0,0 @@
-name := loki
-
-processors := arm gsu hg51b lr35902 r65816 spc700 upd96050
-include processor/Makefile
-
-include sfc/Makefile
-include gb/Makefile
-
-ui_objects := ui-loki ui-settings ui-input
-ui_objects += ui-interface ui-debugger
-ui_objects += ui-presentation ui-terminal
-ui_objects += ruby phoenix
-ui_objects += $(if $(call streq,$(platform),windows),resource)
-
-ifeq ($(platform),windows)
-  ruby := video.wgl audio.xaudio2 input.windows
-else ifeq ($(platform),macosx)
-  ruby := video.cgl audio.openal input.carbon
-else ifeq ($(platform),linux)
-  ruby := video.glx audio.alsa input.udev
-else ifeq ($(platform),bsd)
-  ruby := video.glx audio.openal input.x
-endif
-
-include ruby/Makefile
-link += $(rubylink)
-
-include phoenix/Makefile
-link += $(phoenixlink)
-
-objects := $(ui_objects) $(objects)
-objects := $(patsubst %,obj/%.o,$(objects))
-
-obj/ui-loki.o: $(ui)/loki.cpp $(call rwildcard,$(ui)/)
-obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
-obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
-obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
-obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(call rwildcard,$(ui)/)
-obj/ui-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
-obj/ui-terminal.o: $(ui)/terminal/terminal.cpp $(call rwildcard,$(ui)/)
-
-obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/)
-	$(compiler) $(rubyflags) -c $< -o $@
-
-obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/)
-	$(compiler) $(phoenixflags) -c $< -o $@
-
-obj/resource.o: $(ui)/resource.rc
-ifeq ($(arch),win32)
-	windres --target=pe-i386 $(ui)/resource.rc obj/resource.o
-else
-	windres $(ui)/resource.rc obj/resource.o
-endif
-
-build: $(objects)
-ifeq ($(platform),windows)
-	$(strip $(compiler) -shared -o out/phoenix.dll obj/phoenix.o $(phoenixlink))
-	$(strip $(compiler) -o out/$(name) $(subst obj/phoenix.o,,$(objects)) $(link) -Lout -lphoenix)
-else ifeq ($(platform),macosx)
-	if [ -d out/$(name).app ]; then rm -r out/$(name).app; fi
-	mkdir out/$(name).app
-	mkdir out/$(name).app/Contents
-	mkdir out/$(name).app/Contents/MacOS
-	mkdir out/$(name).app/Contents/Resources
-	cp data/Info.plist out/$(name).app/Contents/Info.plist
-	$(strip $(compiler) -o out/$(name).app/Contents/MacOS/$(name) $(objects) $(link))
-else
-	$(strip $(compiler) -o out/$(name) $(objects) $(link))
-endif
-
-resource:
-	sourcery $(ui)/resource/resource.bml $(ui)/resource/resource.cpp $(ui)/resource/resource.hpp
-
-install:
-ifneq ($(shell id -un),root)
-	$(error "make install must be run as root")
-else ifeq ($(platform),windows)
-else ifeq ($(platform),macosx)
-	mkdir -p /Library/Application\ Support/$(name)
-	cp -R profile/* /Library/Application\ Support/$(name)
-	chmod -R 777 /Library/Application\ Support/$(name)
-else
-	cp out/$(name) $(prefix)/bin/$(name)
-	mkdir -p /usr/share/$(name)
-	cp -R profile/* /usr/share/$(name)
-	chmod -R 777 /usr/share/$(name)
-endif
-
-uninstall:
-ifneq ($(shell id -un),root)
-	$(error "make uninstall must be run as root")
-else ifeq ($(platform),windows)
-else ifeq ($(platform),macosx)
-else
-	rm $(prefix)/bin/$(name)
-endif
diff --git a/target-loki/debugger/debugger.cpp b/target-loki/debugger/debugger.cpp
deleted file mode 100644
index 200681fa..00000000
--- a/target-loki/debugger/debugger.cpp
+++ /dev/null
@@ -1,479 +0,0 @@
-#include "../loki.hpp"
-Debugger* debugger = nullptr;
-
-Debugger::Debugger() {
-  debugger = this;
-  SFC::cpu.debugger.op_exec = {&Debugger::cpuExec, this};
-  SFC::cpu.debugger.op_read = {&Debugger::cpuRead, this};
-  SFC::cpu.debugger.op_write = {&Debugger::cpuWrite, this};
-  SFC::smp.debugger.op_exec = {&Debugger::smpExec, this};
-  SFC::smp.debugger.op_read = {&Debugger::smpRead, this};
-  SFC::smp.debugger.op_write = {&Debugger::smpWrite, this};
-  SFC::ppu.debugger.vram_read = {&Debugger::ppuVramRead, this};
-  SFC::ppu.debugger.vram_write = {&Debugger::ppuVramWrite, this};
-  SFC::ppu.debugger.oam_read = {&Debugger::ppuOamRead, this};
-  SFC::ppu.debugger.oam_write = {&Debugger::ppuOamWrite, this};
-  SFC::ppu.debugger.cgram_read = {&Debugger::ppuCgramRead, this};
-  SFC::ppu.debugger.cgram_write = {&Debugger::ppuCgramWrite, this};
-}
-
-void Debugger::load() {
-  directory::create({interface->pathname, "loki/"});
-
-  cpuUsage = new uint8[0x1000000]();
-  apuUsage = new uint8[0x10000]();
-  file fp;
-
-  if(fp.open({interface->pathname, "loki/cpu.usage.map"}, file::mode::read)) {
-    if(fp.size() == 0x1000000) fp.read(cpuUsage, 0x1000000);
-    fp.close();
-  }
-
-  if(fp.open({interface->pathname, "loki/apu.usage.map"}, file::mode::read)) {
-    if(fp.size() == 0x10000) fp.read(apuUsage, 0x10000);
-    fp.close();
-  }
-}
-
-void Debugger::unload() {
-  if(cpuTracerFile.open()) cpuTracerFile.close();
-  if(smpTracerFile.open()) smpTracerFile.close();
-  file::write({interface->pathname, "loki/cpu.usage.map"}, cpuUsage, 0x1000000);
-  file::write({interface->pathname, "loki/apu.usage.map"}, apuUsage, 0x10000);
-  delete[] cpuUsage;
-  delete[] apuUsage;
-  cpuUsage = nullptr;
-  apuUsage = nullptr;
-}
-
-void Debugger::main() {
-  if(running == false) {
-    usleep(20 * 1000);
-    return;
-  }
-
-  emulator->run();
-}
-
-void Debugger::run() {
-  running = true;
-}
-
-void Debugger::stop() {
-  running = false;
-  cpuRunFor = nothing;
-  cpuRunTo = nothing;
-  cpuStepFor = nothing;
-  cpuStepTo = nothing;
-  smpRunFor = nothing;
-  smpRunTo = nothing;
-  smpStepFor = nothing;
-  smpStepTo = nothing;
-}
-
-void Debugger::leave() {
-  stop();
-  SFC::scheduler.debug();
-}
-
-bool Debugger::breakpointTest(Source source, Breakpoint::Mode mode, unsigned addr, uint8 data) {
-  if(savingState) return false;
-  for(unsigned n = 0; n < breakpoints.size(); n++) {
-    auto& bp = breakpoints[n];
-    if(bp.source != source) continue;
-    if(bp.mode != mode) continue;
-    if(bp.addr != addr) continue;
-    if(bp.mode != Breakpoint::Mode::Execute && bp.data && bp.data() != data) continue;
-    string output = {"Breakpoint #", n, " hit"};
-    if(bp.mode == Breakpoint::Mode::Read ) output.append("; read ",  hex<2>(data));
-    if(bp.mode == Breakpoint::Mode::Write) output.append("; wrote ", hex<2>(data));
-    output.append("; triggered: ", ++bp.triggered);
-    echo(output, "\n");
-    return true;
-  }
-  return false;
-}
-
-string Debugger::cpuDisassemble() {
-  char text[4096];
-  SFC::cpu.disassemble_opcode(text);
-  return {text, " F:", (unsigned)SFC::cpu.field(), " V:", format<3>(SFC::cpu.vcounter()), " H:", format<4>(SFC::cpu.hcounter())};
-}
-
-string Debugger::cpuDisassemble(unsigned addr, bool e, bool m, bool x) {
-  char text[4096];
-  SFC::cpu.disassemble_opcode(text, addr, e, m, x);
-  return {text, " F:", (unsigned)SFC::cpu.field(), " V:", format<3>(SFC::cpu.vcounter()), " H:", format<4>(SFC::cpu.hcounter())};
-}
-
-void Debugger::cpuExec(uint24 addr) {
-  cpuUsage[addr] |= Usage::Execute;
-  if(SFC::cpu.regs.e   == 0) cpuUsage[addr] &= ~Usage::FlagE;
-  if(SFC::cpu.regs.p.m == 0) cpuUsage[addr] &= ~Usage::FlagM;
-  if(SFC::cpu.regs.p.x == 0) cpuUsage[addr] &= ~Usage::FlagX;
-  if(SFC::cpu.regs.e   == 1) cpuUsage[addr] |=  Usage::FlagE;
-  if(SFC::cpu.regs.p.m == 1) cpuUsage[addr] |=  Usage::FlagM;
-  if(SFC::cpu.regs.p.x == 1) cpuUsage[addr] |=  Usage::FlagX;
-
-  cpuInstructionCounter++;
-
-  if(cpuTracerFile.open()) {
-    if(!cpuTracerMask || cpuTracerMask[addr] == false) {
-      if(cpuTracerMask) cpuTracerMask[addr] = true;
-      cpuTracerFile.print(cpuDisassemble(), "\n");
-    }
-  }
-
-  if(savingState) return;
-
-  if(breakpointTest(Source::CPU, Breakpoint::Mode::Execute, addr)) {
-    echo(cpuDisassemble(), "\n");
-    return leave();
-  }
-
-  if(cpuRunFor) {
-    if(--cpuRunFor() == 0) {
-      echo(cpuDisassemble(), "\n");
-      return leave();
-    }
-  }
-
-  if(cpuRunTo) {
-    if(addr == cpuRunTo()) {
-      echo(cpuDisassemble(), "\n");
-      return leave();
-    }
-  }
-
-  if(cpuStepFor) {
-    echo(cpuDisassemble(), "\n");
-    if(--cpuStepFor() == 0) return leave();
-  }
-
-  if(cpuStepTo) {
-    echo(cpuDisassemble(), "\n");
-    if(addr == cpuStepTo()) return leave();
-  }
-}
-
-void Debugger::cpuRead(uint24 addr, uint8 data) {
-  cpuUsage[addr] |= Usage::Read;
-  if(breakpointTest(Source::CPU, Breakpoint::Mode::Read, addr, data)) leave();
-}
-
-void Debugger::cpuWrite(uint24 addr, uint8 data) {
-  cpuUsage[addr] |= Usage::Write;
-  if(breakpointTest(Source::CPU, Breakpoint::Mode::Write, addr, data)) leave();
-}
-
-void Debugger::echoBreakpoints() {
-  if(breakpoints.size() == 0) return;
-  echo("#    source  type      addr    data  triggered\n");
-  echo("---  ------  --------  ------  ----  ---------\n");
-  for(unsigned n = 0; n < breakpoints.size(); n++) {
-    auto& bp = breakpoints[n];
-    string output = {format<-3>(n), "  "};
-    output.append(format<-6>(sourceName(bp.source)), "  ");
-    if(bp.mode == Breakpoint::Mode::Disabled) output.append("disabled  ");
-    if(bp.mode == Breakpoint::Mode::Read    ) output.append("read      ");
-    if(bp.mode == Breakpoint::Mode::Write   ) output.append("write     ");
-    if(bp.mode == Breakpoint::Mode::Execute ) output.append("execute   ");
-    output.append(hex<6>(bp.addr), "  ");
-    output.append(bp.data ? hex<2>(bp.data()) : "  ", "    ");
-    output.append(format<-9>(bp.triggered));
-    echo(output, "\n");
-  }
-}
-
-void Debugger::echoDisassemble(Source source, unsigned addr, signed size) {
-  if(source != Source::CPU && source != Source::SMP) return;
-  const unsigned maximumDisplacement = (source == Source::CPU ? 5 : 4);  //maximum opcode length
-  uint8* usage = (source == Source::CPU ? cpuUsage : apuUsage);
-  if(!(usage[addr] & Usage::Execute)) return echo("No usage data available for ", sourceName(source), "/", hex<6>(addr), "\n");
-
-  while(size > 0) {
-    string text;
-    if(source == Source::CPU) {
-      text = cpuDisassemble(addr, usage[addr] & Usage::FlagE, usage[addr] & Usage::FlagM, usage[addr] & Usage::FlagX);
-    }
-    if(source == Source::SMP) {
-      text = smpDisassemble(addr, usage[addr] & Usage::FlagP);
-    }
-    text.resize(20);  //remove register information
-    echo(text, "\n");
-    if(--size <= 0) break;
-
-    unsigned displacement = 1;
-    while(displacement < maximumDisplacement) {  //maximum opcode length is four bytes
-      if(usage[addr + displacement] & Usage::Execute) break;
-      displacement++;
-    }
-    if(displacement >= maximumDisplacement) {
-      echo("...\n");
-      return;
-    }
-    addr += displacement;
-  }
-}
-
-void Debugger::echoHex(Source source, unsigned addr, signed size) {
-  if(memorySize(source) == 0) return;  //not a valid memory pool
-  while(size > 0) {
-    string hexdata, asciidata;
-    for(unsigned n = 0; n < 16; n++) {
-      unsigned offset = addr;
-      if(source == Source::CPU && ((offset & 0x40e000) == 0x002000 || (offset & 0x40e000) == 0x004000)) {
-        //$00-3f,80-bf:2000-5fff
-        //reading MMIO registers can negatively impact emulation, so disallow these reads
-        hexdata.append("?? ");
-        asciidata.append("?");
-      } else {
-        uint8 byte = memoryRead(source, addr + n);
-        hexdata.append(hex<2>(byte), " ");
-        asciidata.append(byte >= 0x20 && byte <= 0x7e ? (char)byte : '.');
-      }
-    }
-    echo(hex<6>(addr % memorySize(source)), " [ ", hexdata, "] ", asciidata, "\n");
-    addr += 16, size -= 16;
-  }
-}
-
-void Debugger::memoryExport(Source source, string filename) {
-  file fp;
-  if(fp.open(filename, file::mode::write)) {
-    unsigned size = memorySize(source);
-    for(unsigned addr = 0; addr < size; addr++) {
-      fp.write(memoryRead(source, addr));
-    }
-    echo("Exported memory to ", notdir(filename), "\n");
-  }
-}
-
-uint8 Debugger::memoryRead(Source source, unsigned addr) {
-  if(source == Source::CPU) {
-    return SFC::bus.read(addr & 0xffffff);
-  }
-
-  if(source == Source::APU) {
-    return SFC::smp.apuram[addr & 0xffff];
-  }
-
-  if(source == Source::WRAM) {
-    return SFC::cpu.wram[addr & 0x1ffff];
-  }
-
-  if(source == Source::VRAM) {
-    return SFC::ppu.vram[addr & 0xffff];
-  }
-
-  if(source == Source::OAM) {
-    return SFC::ppu.oam[addr % 544];
-  }
-
-  if(source == Source::CGRAM) {
-    return SFC::ppu.cgram[addr & 511];
-  }
-
-  return 0x00;
-}
-
-unsigned Debugger::memorySize(Source source) {
-  switch(source) {
-  case Source::CPU: return 0x1000000;
-  case Source::APU: return 0x10000;
-  case Source::WRAM: return 0x20000;
-  case Source::VRAM: return 0x10000;
-  case Source::OAM: return 544;
-  case Source::CGRAM: return 512;
-  }
-  return 0;
-}
-
-void Debugger::memoryWrite(Source source, unsigned addr, uint8 data) {
-  if(source == Source::CPU) {
-    SFC::bus.write(addr & 0xffffff, data);
-    return;
-  }
-
-  if(source == Source::APU) {
-    SFC::smp.apuram[addr & 0xffff] = data;
-    return;
-  }
-
-  if(source == Source::WRAM) {
-    SFC::cpu.wram[addr & 0x1ffff] = data;
-    return;
-  }
-
-  if(source == Source::VRAM) {
-    SFC::ppu.vram[addr & 0xffff] = data;
-    return;
-  }
-
-  if(source == Source::OAM) {
-    SFC::ppu.oam[addr % 544] = data;
-    SFC::ppu.sprite.update(addr % 544, data);
-    return;
-  }
-
-  if(source == Source::CGRAM) {
-    if(addr & 1) data &= 0x7f;
-    SFC::ppu.cgram[addr] = data;
-    return;
-  }
-}
-
-void Debugger::ppuCgramRead(uint16 addr, uint8 data) {
-  if(breakpointTest(Source::CGRAM, Breakpoint::Mode::Read, addr, data)) leave();
-}
-
-void Debugger::ppuCgramWrite(uint16 addr, uint8 data) {
-  if(breakpointTest(Source::CGRAM, Breakpoint::Mode::Write, addr, data)) leave();
-}
-
-void Debugger::ppuOamRead(uint16 addr, uint8 data) {
-  if(breakpointTest(Source::OAM, Breakpoint::Mode::Read, addr, data)) leave();
-}
-
-void Debugger::ppuOamWrite(uint16 addr, uint8 data) {
-  if(breakpointTest(Source::OAM, Breakpoint::Mode::Write, addr, data)) leave();
-}
-
-void Debugger::ppuVramRead(uint16 addr, uint8 data) {
-  if(breakpointTest(Source::VRAM, Breakpoint::Mode::Read, addr, data)) leave();
-}
-
-void Debugger::ppuVramWrite(uint16 addr, uint8 data) {
-  if(breakpointTest(Source::VRAM, Breakpoint::Mode::Write, addr, data)) leave();
-}
-
-string Debugger::smpDisassemble() {
-  return SFC::smp.disassemble_opcode(SFC::smp.regs.pc, SFC::smp.regs.p.p);
-}
-
-string Debugger::smpDisassemble(uint16 addr, bool p) {
-  return SFC::smp.disassemble_opcode(addr, p);
-}
-
-void Debugger::smpExec(uint16 addr) {
-  apuUsage[addr] |= Usage::Execute;
-  if(SFC::smp.regs.p.p == 0) apuUsage[addr] &= ~Usage::FlagP;
-  if(SFC::smp.regs.p.p == 1) apuUsage[addr] |=  Usage::FlagP;
-
-  smpInstructionCounter++;
-
-  if(smpTracerFile.open()) {
-    if(!smpTracerMask || smpTracerMask[addr] == false) {
-      if(smpTracerMask) smpTracerMask[addr] = true;
-      smpTracerFile.print(smpDisassemble(), "\n");
-    }
-  }
-
-  if(savingState) return;
-
-  if(breakpointTest(Source::SMP, Breakpoint::Mode::Execute, addr)) {
-    echo(smpDisassemble(), "\n");
-    return leave();
-  }
-
-  if(smpRunFor) {
-    if(--smpRunFor() == 0) {
-      echo(smpDisassemble(), "\n");
-      return leave();
-    }
-  }
-
-  if(smpRunTo) {
-    if(addr == smpRunTo()) {
-      echo(smpDisassemble(), "\n");
-      return leave();
-    }
-  }
-
-  if(smpStepFor) {
-    echo(smpDisassemble(), "\n");
-    if(--smpStepFor() == 0) return leave();
-  }
-
-  if(smpStepTo) {
-    echo(smpDisassemble(), "\n");
-    if(addr == smpStepTo()) return leave();
-  }
-}
-
-void Debugger::smpRead(uint16 addr, uint8 data) {
-  apuUsage[addr] |= Usage::Read;
-  if(breakpointTest(Source::SMP, Breakpoint::Mode::Read, addr, data)) leave();
-  if(breakpointTest(Source::APU, Breakpoint::Mode::Read, addr, data)) leave();
-}
-
-void Debugger::smpWrite(uint16 addr, uint8 data) {
-  apuUsage[addr] |= Usage::Write;
-  if(breakpointTest(Source::SMP, Breakpoint::Mode::Write, addr, data)) leave();
-  if(breakpointTest(Source::APU, Breakpoint::Mode::Write, addr, data)) leave();
-}
-
-string Debugger::sourceName(Source source) {
-  switch(source) {
-  case Source::CPU: return "cpu";
-  case Source::SMP: return "smp";
-  case Source::PPU: return "ppu";
-  case Source::DSP: return "dsp";
-  case Source::APU: return "apu";
-  case Source::WRAM: return "wram";
-  case Source::VRAM: return "vram";
-  case Source::OAM: return "oam";
-  case Source::CGRAM: return "cgram";
-  }
-  return "none";
-}
-
-void Debugger::stateLoad(string filename) {
-  auto memory = file::read(filename);
-  if(memory.size() == 0) return echo("Error: state file ", notdir(filename), " not found\n");
-  serializer s(memory.data(), memory.size());
-  if(emulator->unserialize(s) == false) return echo("Error: failed to unserialize state from ", notdir(filename), "\n");
-  echo("State loaded from ", notdir(filename), "\n");
-}
-
-void Debugger::stateSave(string filename) {
-  savingState = true;
-  serializer s = emulator->serialize();
-  if(file::write(filename, s.data(), s.size())) {
-    echo("State saved to ", notdir(filename), "\n");
-  }
-  savingState = false;
-}
-
-void Debugger::tracerDisable(Source source) {
-  if(source != Source::CPU && source != Source::SMP) return;
-  file& tracerFile = (source == Source::CPU ? cpuTracerFile : smpTracerFile);
-  if(tracerFile.open() == false) return;
-  tracerFile.close();
-  echo(sourceName(source).upper(), " tracer disabled\n");
-}
-
-void Debugger::tracerEnable(Source source, string filename) {
-  if(source != Source::CPU && source != Source::SMP) return;
-  file& tracerFile = (source == Source::CPU ? cpuTracerFile : smpTracerFile);
-  if(tracerFile.open() == true) return;
-  if(tracerFile.open(filename, file::mode::write)) {
-    echo(sourceName(source).upper(), " tracer enabled\n");
-  }
-}
-
-void Debugger::tracerMaskDisable(Source source) {
-  if(source != Source::CPU && source != Source::SMP) return;
-  bitvector& tracerMask = (source == Source::CPU ? cpuTracerMask : smpTracerMask);
-  tracerMask.reset();
-  echo(sourceName(source).upper(), " tracer mask disabled\n");
-}
-
-void Debugger::tracerMaskEnable(Source source) {
-  if(source != Source::CPU && source != Source::SMP) return;
-  bitvector& tracerMask = (source == Source::CPU ? cpuTracerMask : smpTracerMask);
-  unsigned size = (source == Source::CPU ? 0x1000000 : 0x10000);
-  tracerMask.resize(size);
-  tracerMask.clear();
-  echo(sourceName(source).upper(), " tracer mask enabled\n");
-}
diff --git a/target-loki/debugger/debugger.hpp b/target-loki/debugger/debugger.hpp
deleted file mode 100644
index 3ed978cb..00000000
--- a/target-loki/debugger/debugger.hpp
+++ /dev/null
@@ -1,92 +0,0 @@
-struct Debugger {
-  enum class Source : unsigned { CPU, SMP, PPU, DSP, APU, WRAM, VRAM, OAM, CGRAM };
-
-  struct Breakpoint {
-    Source source = Source::CPU;
-    enum class Mode : unsigned { Disabled, Read, Write, Execute } mode = Mode::Disabled;
-    unsigned addr = 0;
-    maybe<uint8> data;
-    unsigned triggered = 0;  //counter for number of times breakpoint was hit
-  };
-
-  struct Usage {
-    enum : unsigned {
-      Read     = 0x01,
-      Write    = 0x02,
-      Execute  = 0x04,
-      //CPU
-      FlagE    = 0x08,
-      FlagM    = 0x10,
-      FlagX    = 0x20,
-      //APU
-      FlagP    = 0x08,
-      DspRead  = 0x10,
-      DspWrite = 0x20,
-    };
-  };
-
-  Debugger();
-
-  void load();
-  void unload();
-  void main();
-
-  void run();
-  void stop();
-  void leave();
-
-  bool breakpointTest(Source source, Breakpoint::Mode mode, unsigned addr, uint8 data = 0x00);
-  string cpuDisassemble();
-  string cpuDisassemble(unsigned addr, bool e, bool m, bool x);
-  void cpuExec(uint24 addr);
-  void cpuRead(uint24 addr, uint8 data);
-  void cpuWrite(uint24 addr, uint8 data);
-  void echoBreakpoints();
-  void echoDisassemble(Source source, unsigned addr, signed size);
-  void echoHex(Source source, unsigned addr, signed size);
-  void memoryExport(Source source, string filename);
-  uint8 memoryRead(Source source, unsigned addr);
-  unsigned memorySize(Source source);
-  void memoryWrite(Source source, unsigned addr, uint8 data);
-  void ppuCgramRead(uint16 addr, uint8 data);
-  void ppuCgramWrite(uint16 addr, uint8 data);
-  void ppuOamRead(uint16 addr, uint8 data);
-  void ppuOamWrite(uint16 addr, uint8 data);
-  void ppuVramRead(uint16 addr, uint8 data);
-  void ppuVramWrite(uint16 addr, uint8 data);
-  string smpDisassemble();
-  string smpDisassemble(uint16 addr, bool p);
-  void smpExec(uint16 addr);
-  void smpRead(uint16 addr, uint8 data);
-  void smpWrite(uint16 addr, uint8 data);
-  string sourceName(Source source);
-  void stateLoad(string filename);
-  void stateSave(string filename);
-  void tracerDisable(Source source);
-  void tracerEnable(Source source, string filename);
-  void tracerMaskDisable(Source source);
-  void tracerMaskEnable(Source source);
-
-  bool running = false;      //emulation runs asynchronously (cooperatively) to terminal commands
-  bool savingState = false;  //suppresses all break events to allow state to be captured synchronously
-
-  uint8* apuUsage = nullptr;
-  vector<Breakpoint> breakpoints;
-  unsigned cpuInstructionCounter = 0;
-  maybe<unsigned> cpuRunFor;
-  maybe<unsigned> cpuRunTo;
-  maybe<unsigned> cpuStepFor;
-  maybe<unsigned> cpuStepTo;
-  file cpuTracerFile;
-  bitvector cpuTracerMask;
-  uint8* cpuUsage = nullptr;
-  unsigned smpInstructionCounter = 0;
-  maybe<unsigned> smpRunFor;
-  maybe<unsigned> smpRunTo;
-  maybe<unsigned> smpStepFor;
-  maybe<unsigned> smpStepTo;
-  file smpTracerFile;
-  bitvector smpTracerMask;
-};
-
-extern Debugger* debugger;
diff --git a/target-loki/input/input.cpp b/target-loki/input/input.cpp
deleted file mode 100644
index a45105a4..00000000
--- a/target-loki/input/input.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "../loki.hpp"
-InputManager* inputManager = nullptr;
-
-void AbstractInput::bind() {
-  for(auto device : inputManager->devices) {
-    if(device->isKeyboard() == false) continue;
-    if(auto group = device->find("Button")) {
-      if(auto input = device->group[group()].find(mapping)) {
-        this->device = device;
-        this->group = group();
-        this->input = input();
-        break;
-      }
-    }
-  }
-}
-
-int16_t AbstractInput::poll() {
-  if(device == nullptr) return 0;
-  return device->group[group].input[input].value;
-}
-
-InputManager::InputManager() {
-  inputManager = this;
-}
-
-void InputManager::load() {
-  unsigned guid = 0;
-  Configuration::Node emulatorNode;
-
-  for(auto& port : emulator->port) {
-    Configuration::Node portNode;
-
-    for(auto& device : port.device) {
-      Configuration::Node deviceNode;
-
-      for(auto& number : device.order) {
-        auto& input = device.input[number];
-        input.guid = guid++;
-
-        auto abstract = new AbstractInput;
-        abstract->name = string{input.name}.replace(" ", "");
-        abstract->mapping = "None";
-        inputMap.append(abstract);
-
-        deviceNode.append(abstract->mapping, abstract->name);
-      }
-
-      portNode.append(deviceNode, string{device.name}.replace(" ", ""));
-    }
-
-    emulatorNode.append(portNode, string{port.name}.replace(" ", ""));
-  }
-
-  append(emulatorNode, "SuperFamicom");
-
-  Configuration::Document::load(program->path("input.bml"));
-  Configuration::Document::save(program->path("input.bml"));
-}
-
-void InputManager::unload() {
-  Configuration::Document::save(program->path("input.bml"));
-}
-
-void InputManager::bind() {
-  for(auto input : inputMap) input->bind();
-}
-
-void InputManager::poll() {
-  auto devices = input.poll();
-  bool changed = devices.size() != this->devices.size();
-  if(changed == false) {
-    for(unsigned n = 0; n < devices.size(); n++) {
-      changed = devices[n] != this->devices[n];
-      if(changed) break;
-    }
-  }
-  if(changed == true) {
-    this->devices = devices;
-    bind();
-  }
-}
diff --git a/target-loki/input/input.hpp b/target-loki/input/input.hpp
deleted file mode 100644
index d8b84a87..00000000
--- a/target-loki/input/input.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-struct AbstractInput {
-  void bind();
-  int16_t poll();
-
-  string name;
-  string mapping;
-
-  HID::Device* device = nullptr;
-  unsigned group = 0;
-  unsigned input = 0;
-};
-
-struct InputManager : Configuration::Document {
-  InputManager();
-  void load();
-  void unload();
-
-  void bind();
-  void poll();
-
-  vector<HID::Device*> devices;
-  vector<AbstractInput*> inputMap;
-};
-
-extern InputManager* inputManager;
diff --git a/target-loki/interface/interface.cpp b/target-loki/interface/interface.cpp
deleted file mode 100644
index 388b30db..00000000
--- a/target-loki/interface/interface.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-#include "../loki.hpp"
-Interface* interface = nullptr;
-SuperFamicom::Interface* emulator = nullptr;
-
-Interface::Interface() {
-  interface = this;
-  emulator = new SuperFamicom::Interface;
-  emulator->bind = this;
-}
-
-bool Interface::load(string pathname) {
-  pathname.transform("\\", "/").rtrim("/");
-  if(!directory::exists(pathname)) return false;
-
-  string type = extension(pathname);
-
-  for(auto& media : emulator->media) {
-    if(media.bootable == false) continue;
-    if(type != media.type) continue;
-
-    this->pathname = pathname.append("/");
-    pathnames.reset();
-    pathnames(0) = program->path({media.name, ".sys/"});
-    pathnames(media.id) = pathname;
-    echo("Loaded ", pathname, "\n");
-
-    emulator->load(media.id);
-    emulator->paletteUpdate(Emulator::Interface::PaletteMode::Standard);
-    emulator->power();
-    presentation->setTitle(emulator->title());
-
-    return true;
-  }
-
-  return false;
-}
-
-void Interface::unload() {
-  emulator->unload();
-}
-
-//bindings
-
-void Interface::loadRequest(unsigned id, string name, string type) {
-}
-
-void Interface::loadRequest(unsigned id, string path) {
-  string pathname = {pathnames(emulator->group(id)), path};
-  if(file::exists(pathname) == false) return;
-  mmapstream stream(pathname);
-  emulator->load(id, stream);
-  echo("Loaded ", path, "\n");
-}
-
-void Interface::saveRequest(unsigned id, string path) {
-  string pathname = {pathnames(emulator->group(id)), path};
-  filestream stream(pathname, file::mode::write);
-  emulator->save(id, stream);
-  echo("Saved ", path, "\n");
-}
-
-uint32_t Interface::videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue) {
-  return ((alpha >> 8) << 24) | ((red >> 8) << 16) | ((green >> 8) << 8) | ((blue >> 8) << 0);
-}
-
-void Interface::videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) {
-  uint32_t* output;
-  unsigned outputPitch;
-
-  if(video.lock(output, outputPitch, width, height)) {
-    pitch >>= 2, outputPitch >>= 2;
-
-    for(unsigned y = 0; y < height; y++) {
-      const uint32_t* sp = data + y * pitch;
-      uint32_t* dp = output + y * outputPitch;
-      for(unsigned x = 0; x < width; x++) {
-        *dp++ = palette[*sp++];
-      }
-    }
-
-    video.unlock();
-    video.refresh();
-  }
-}
-
-void Interface::audioSample(int16_t lsample, int16_t rsample) {
-  if(settings->audio.mute) lsample = 0, rsample = 0;
-  signed samples[] = {lsample, rsample};
-  dspaudio.sample(samples);
-  while(dspaudio.pending()) {
-    dspaudio.read(samples);
-    audio.sample(samples[0], samples[1]);
-  }
-}
-
-int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
-  unsigned guid = emulator->port[port].device[device].input[input].guid;
-  return inputManager->inputMap[guid]->poll();
-}
-
-void Interface::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) {
-}
-
-unsigned Interface::dipSettings(const Markup::Node& node) {
-  return 0;
-}
-
-string Interface::path(unsigned group) {
-  return pathnames(group);
-}
-
-string Interface::server() {
-  return "";
-}
-
-void Interface::notify(string text) {
-}
diff --git a/target-loki/interface/interface.hpp b/target-loki/interface/interface.hpp
deleted file mode 100644
index 1438075e..00000000
--- a/target-loki/interface/interface.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-struct Interface : Emulator::Interface::Bind {
-  Interface();
-  bool load(string pathname);
-  void unload();
-
-  //bindings
-  void loadRequest(unsigned id, string name, string type);
-  void loadRequest(unsigned id, string path);
-  void saveRequest(unsigned id, string path);
-  uint32_t videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue);
-  void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height);
-  void audioSample(int16_t lsample, int16_t rsample);
-  int16_t inputPoll(unsigned port, unsigned device, unsigned input);
-  void inputRumble(unsigned port, unsigned device, unsigned input, bool enable);
-  unsigned dipSettings(const Markup::Node& node);
-  string path(unsigned group);
-  string server();
-  void notify(string text);
-
-  string pathname;  //path to game folder
-  lstring pathnames;
-};
-
-extern Interface* interface;
-extern SuperFamicom::Interface* emulator;
diff --git a/target-loki/loki.cpp b/target-loki/loki.cpp
deleted file mode 100644
index 26232e1c..00000000
--- a/target-loki/loki.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "loki.hpp"
-#include "resource/resource.cpp"
-
-Program* program = nullptr;
-DSP dspaudio;
-
-string Program::path(string name) {
-  string path = {basepath, name};
-  if(file::exists(path) || directory::exists(path)) return path;
-  path = {userpath, name};
-  if(file::exists(path) || directory::exists(path)) return path;
-  path = {sharedpath, name};
-  if(file::exists(path) || directory::exists(path)) return path;
-  return {userpath, name};
-}
-
-void Program::main() {
-  inputManager->poll();
-  debugger->main();
-}
-
-Program::Program(string pathname) {
-  program = this;
-
-  basepath = nall::programpath();
-  userpath = {nall::configpath(), "loki/"};
-  sharedpath = {nall::sharedpath(), "loki/"};
-  directory::create(userpath);
-
-  new Settings;
-  new InputManager;
-  new Interface;
-  new Debugger;
-  new Presentation;
-  new Terminal;
-
-  presentation->setVisible();
-  terminal->setVisible();
-  Application::processEvents();
-
-  video.driver(settings->video.driver);
-  video.set(Video::Handle, presentation->viewport.handle());
-  video.set(Video::Synchronize, settings->video.synchronize);
-  video.set(Video::Filter, Video::FilterNearest);
-  if(video.init() == false) { video.driver("None"); video.init(); }
-
-  audio.driver(settings->audio.driver);
-  audio.set(Audio::Handle, presentation->viewport.handle());
-  audio.set(Audio::Synchronize, settings->audio.synchronize);
-  audio.set(Audio::Frequency, 48000u);
-  if(audio.init() == false) { audio.driver("None"); audio.init(); }
-
-  input.driver(settings->input.driver);
-  input.set(Input::Handle, presentation->viewport.handle());
-  if(input.init() == false) { input.driver("None"); input.init(); }
-  input.onChange = {&Terminal::inputEvent, terminal};
-
-  dspaudio.setPrecision(16);
-  dspaudio.setBalance(0.0);
-  dspaudio.setFrequency(32000);
-  dspaudio.setResampler(DSP::ResampleEngine::Hermite);
-  dspaudio.setResamplerFrequency(48000);
-
-  presentation->showSplash();
-
-  inputManager->load();
-  interface->load(pathname);
-  debugger->load();
-  terminal->load();
-
-  Application::main = {&Program::main, this};
-  Application::run();
-
-  terminal->unload();
-  debugger->unload();
-  interface->unload();
-  inputManager->unload();
-  settings->unload();
-}
-
-int main(int argc, char** argv) {
-  #if defined(PLATFORM_WINDOWS)
-  utf8_args(argc, argv);
-  #endif
-
-  if(argc != 2 || !directory::exists(argv[1])) {
-    print("loki v", Emulator::Version, "\n");
-    print("usage: loki /path/game.sfc/\n");
-    return 0;
-  }
-
-  Application::setName("loki");
-  new Program(argv[1]);
-  delete program;
-
-  return 0;
-}
diff --git a/target-loki/loki.hpp b/target-loki/loki.hpp
deleted file mode 100644
index 293b21ea..00000000
--- a/target-loki/loki.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <emulator/emulator.hpp>
-#include <sfc/sfc.hpp>
-namespace SFC = SuperFamicom;
-
-#include <nall/platform.hpp>
-#include <nall/bitvector.hpp>
-#include <nall/config.hpp>
-#include <nall/directory.hpp>
-#include <nall/dsp.hpp>
-#include <nall/invoke.hpp>
-#include <nall/map.hpp>
-#include <nall/stream/file.hpp>
-#include <nall/stream/memory.hpp>
-#include <nall/stream/mmap.hpp>
-#include <nall/stream/vector.hpp>
-using namespace nall;
-
-#include <ruby/ruby.hpp>
-using namespace ruby;
-
-#include <phoenix/phoenix.hpp>
-using namespace phoenix;
-
-#include "settings/settings.hpp"
-#include "input/input.hpp"
-#include "interface/interface.hpp"
-#include "debugger/debugger.hpp"
-#include "presentation/presentation.hpp"
-#include "terminal/terminal.hpp"
-#include "resource/resource.hpp"
-
-struct Program {
-  string basepath;
-  string userpath;
-  string sharedpath;
-
-  string path(string name);
-  void main();
-  Program(string pathname);
-};
-
-template<typename... Args> void echo(Args&&... args) {
-  terminal->print({std::forward<Args>(args)...});
-}
-
-extern Program* program;
-extern DSP dspaudio;
diff --git a/target-loki/presentation/presentation.cpp b/target-loki/presentation/presentation.cpp
deleted file mode 100644
index 00a85862..00000000
--- a/target-loki/presentation/presentation.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "../loki.hpp"
-Presentation* presentation = nullptr;
-
-Presentation::Presentation() {
-  presentation = this;
-  if(settings->geometry.presentation.empty()) {
-    settings->geometry.presentation = "64,64,512,480";
-  }
-
-  setTitle({"loki v", Emulator::Version});
-  setGeometry(settings->geometry.presentation);
-  setResizable(false);
-
-  layout.append(viewport, {0, 0, 512, 480});
-  append(layout);
-
-  onClose = &Application::quit;
-
-  splash.allocate(512, 480);
-  splash.verticalGradient(0xff00005f, 0xff000000, 512, 480, 256, 0);
-  nall::image floor;
-  floor.allocate(512, 480);
-  floor.radialGradient(0xffff0000, 0x00000000, 384, 240, 256, 415);
-  splash.impose(image::blend::sourceAlpha, 0, 0, floor, 0, 0, floor.width, floor.height);
-  nall::image loki(resource::loki, sizeof resource::loki);
-  splash.impose(image::blend::sourceAlpha, (512 - loki.width) / 2, (480 - loki.height) / 2, loki, 0, 0, loki.width, loki.height);
-}
-
-void Presentation::showSplash() {
-  uint32_t* data;
-  unsigned pitch;
-  if(video.lock(data, pitch, 512, 480)) {
-    for(unsigned y = 0; y < 480; y++) {
-      memcpy((uint8_t*)data + y * pitch, splash.data + y * splash.pitch, 512 * sizeof(uint32_t));
-    }
-    video.unlock();
-    video.refresh();
-  }
-}
diff --git a/target-loki/presentation/presentation.hpp b/target-loki/presentation/presentation.hpp
deleted file mode 100644
index f5094b11..00000000
--- a/target-loki/presentation/presentation.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-struct Presentation : Window {
-  Presentation();
-  void showSplash();
-
-  FixedLayout layout;
-  Viewport viewport;
-
-  nall::image splash;
-};
-
-extern Presentation* presentation;
diff --git a/target-loki/resource/loki.png b/target-loki/resource/loki.png
deleted file mode 100644
index 7b031d9c..00000000
Binary files a/target-loki/resource/loki.png and /dev/null differ
diff --git a/target-loki/resource/resource.bml b/target-loki/resource/resource.bml
deleted file mode 100644
index 9d8482c8..00000000
--- a/target-loki/resource/resource.bml
+++ /dev/null
@@ -1,2 +0,0 @@
-resource name=resource
-  binary id=loki name=loki.png
diff --git a/target-loki/resource/resource.cpp b/target-loki/resource/resource.cpp
deleted file mode 100644
index 5f8cffbf..00000000
--- a/target-loki/resource/resource.cpp
+++ /dev/null
@@ -1,3816 +0,0 @@
-namespace resource {
-
-const uint8_t loki[121905] = {
-  137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,1,32,0,0,1,104,8,6,0,0,0,247,136,38,
-  129,0,0,0,9,112,72,89,115,0,0,11,19,0,0,11,19,1,0,154,156,24,0,0,0,4,103,65,77,65,0,0,
-  177,142,124,251,81,147,0,0,0,32,99,72,82,77,0,0,122,37,0,0,128,131,0,0,249,255,0,0,128,233,0,0,
-  117,48,0,0,234,96,0,0,58,152,0,0,23,111,146,95,197,70,0,1,219,167,73,68,65,84,120,218,236,253,119,152,
-  100,215,117,222,11,255,246,62,161,114,85,231,220,51,61,57,103,12,114,14,36,2,193,28,36,81,129,162,2,149,124,229,
-  251,73,150,174,172,123,45,75,126,244,93,203,178,37,75,178,44,90,209,164,40,81,34,197,12,18,32,0,34,199,193,0,
-  147,83,247,116,206,93,93,93,185,234,196,189,239,31,85,51,24,128,32,197,128,72,246,122,158,126,122,166,186,234,156,83,
-  59,188,123,173,119,37,81,187,239,125,188,26,98,10,137,18,146,55,131,68,226,22,255,242,224,4,31,248,143,79,240,86,
-  144,255,1,244,0,255,127,224,111,129,191,4,54,3,231,129,63,251,46,175,245,21,96,14,56,2,124,28,24,5,2,224,
-  52,176,23,184,23,216,222,252,187,15,104,192,0,20,208,14,180,129,184,14,228,81,48,218,65,100,33,92,128,160,23,184,
-  237,251,252,158,117,96,6,216,4,252,28,176,3,200,1,78,243,153,126,5,168,189,9,231,231,131,192,6,192,2,126,23,
-  248,123,224,16,240,54,32,211,124,246,231,154,99,56,12,172,109,142,101,25,120,7,112,79,115,156,199,128,46,96,13,112,
-  13,240,123,205,107,143,0,133,230,56,76,0,235,128,241,230,123,119,1,207,3,155,119,238,163,239,193,23,48,61,221,188,
-  218,171,35,251,6,223,216,61,107,178,42,63,116,34,155,160,147,105,44,0,209,9,134,9,118,20,226,2,18,199,32,19,
-  129,214,18,200,24,44,118,73,57,211,166,84,185,38,132,54,163,81,64,124,119,55,20,2,195,247,80,190,191,58,248,171,
-  178,10,64,63,140,18,54,79,216,13,64,43,24,123,192,182,32,17,66,26,104,23,141,3,183,79,66,111,20,186,93,41,
-  123,124,132,182,85,248,76,196,180,239,245,60,231,220,161,214,142,96,241,255,248,61,116,52,14,74,125,199,247,246,147,105,
-  54,223,251,207,180,220,251,25,172,213,169,88,149,85,0,250,225,17,221,52,125,210,32,135,193,238,128,84,30,58,5,244,
-  43,88,35,97,16,24,112,160,191,98,199,122,150,251,250,187,138,61,93,169,83,139,179,241,3,83,51,179,59,96,26,207,
-  137,152,169,140,40,253,135,255,73,213,144,72,253,237,77,0,45,4,82,26,152,145,40,174,214,4,158,71,128,32,4,236,
-  213,41,89,149,85,0,250,225,208,120,2,96,17,140,50,36,98,66,116,197,96,77,168,245,6,27,54,123,66,110,204,167,
-  50,107,179,241,84,231,98,212,200,208,149,78,248,29,237,178,218,213,75,110,100,148,173,217,229,153,29,97,248,152,128,103,
-  77,88,52,93,39,236,125,236,107,44,93,125,27,202,52,17,175,0,66,66,26,24,177,56,218,169,81,203,103,25,57,252,
-  56,239,172,85,120,252,208,163,164,42,165,213,73,89,149,85,0,250,65,151,0,72,0,187,64,62,6,113,3,250,226,176,
-  205,210,122,47,66,236,200,153,246,186,137,150,206,129,165,77,27,90,229,186,158,72,58,41,201,196,124,124,43,198,145,227,
-  211,180,125,249,158,218,187,87,138,103,58,225,17,15,30,76,194,177,9,88,238,242,92,117,205,125,159,229,212,229,55,224,
-  89,54,232,240,2,234,16,88,54,88,22,193,242,2,147,143,126,149,59,206,30,69,140,156,226,241,192,167,79,107,20,223,
-  53,107,180,42,171,0,180,42,111,69,137,3,62,216,87,64,187,11,59,12,184,218,133,203,207,39,90,182,77,172,219,208,
-  163,183,15,196,90,59,99,162,95,57,224,148,136,138,12,211,101,120,254,201,231,184,250,220,248,220,53,120,135,52,60,236,
-  193,83,14,140,236,129,242,28,168,218,37,54,157,22,2,109,152,4,150,141,85,175,48,116,234,24,222,217,227,44,31,126,
-  156,133,82,30,5,92,160,170,245,234,148,172,202,42,0,189,53,197,6,142,211,112,237,14,54,205,42,247,95,209,38,34,
-  96,172,129,30,23,110,0,110,155,138,197,47,159,61,112,217,218,112,109,38,154,76,199,241,125,77,101,113,133,186,116,137,
-  181,165,152,40,20,201,62,252,130,247,225,197,236,240,32,250,81,23,30,52,225,72,20,22,142,129,59,1,108,107,242,72,
-  8,129,147,72,129,20,36,10,57,182,61,245,32,226,241,175,179,185,94,229,108,224,147,93,213,116,94,19,209,128,48,4,
-  58,13,162,40,208,1,160,245,42,0,173,202,107,43,17,224,33,94,140,141,201,1,7,128,202,183,249,140,4,211,131,117,
-  26,222,54,223,59,240,54,245,193,187,186,230,74,211,20,231,75,164,242,138,164,89,39,19,9,49,91,211,156,154,205,211,
-  245,240,243,249,31,175,212,143,217,240,144,11,143,104,56,101,64,241,147,16,246,53,65,79,92,0,22,207,101,207,3,159,
-  167,115,252,28,189,195,39,48,125,143,17,173,145,171,83,245,154,138,0,188,153,25,236,143,255,111,130,187,62,128,221,26,
-  193,180,77,188,10,232,239,194,27,249,102,148,213,181,243,22,56,33,82,64,43,141,128,181,86,96,99,83,43,217,242,178,
-  159,237,64,6,12,5,9,7,186,23,174,189,188,99,232,186,253,144,93,166,152,155,39,52,107,36,45,73,38,222,206,241,
-  51,203,36,30,58,154,191,171,82,127,214,128,127,9,224,115,2,158,47,65,254,72,3,119,88,15,172,0,109,205,251,77,
-  250,30,251,191,242,15,12,158,60,140,233,185,63,48,167,240,91,1,128,252,220,18,230,191,251,57,190,116,160,151,242,47,
-  254,12,143,254,237,39,8,125,7,157,150,72,67,34,132,120,203,174,239,85,121,139,136,75,35,58,186,11,56,215,60,61,
-  76,26,241,61,23,248,150,24,4,109,144,79,9,57,91,59,114,186,242,84,127,75,250,221,239,184,150,165,130,199,225,179,
-  227,100,11,101,142,141,47,208,114,236,92,237,71,235,181,243,62,60,166,225,97,31,70,226,224,215,155,38,223,94,224,102,
-  160,175,105,126,117,94,48,195,86,229,141,3,162,48,192,171,148,153,253,220,39,121,254,115,159,164,255,207,126,159,53,191,
-  252,127,225,190,227,131,136,182,40,182,48,9,235,26,241,22,98,222,86,53,160,183,152,186,42,129,98,243,255,123,155,192,
-  240,110,224,110,224,125,192,65,240,21,76,249,90,61,125,197,244,228,11,149,39,143,120,231,23,114,244,36,76,126,241,253,
-  239,226,224,109,55,48,93,46,176,195,169,123,166,82,10,48,52,100,108,104,53,33,214,6,70,6,68,186,121,143,36,141,
-  20,3,86,249,157,55,149,214,96,2,245,241,17,82,191,249,49,126,251,178,62,30,252,185,159,36,56,122,146,72,82,144,
-  202,72,132,148,171,0,180,42,175,157,74,158,108,114,67,135,155,154,143,251,162,137,166,5,100,5,60,17,117,235,95,217,
-  255,252,137,231,22,159,92,116,203,50,202,217,51,79,115,249,222,65,62,241,55,255,133,142,159,252,137,248,145,84,219,80,
-  5,110,143,194,135,37,188,199,129,235,99,176,235,122,232,235,130,68,30,228,170,145,245,230,20,125,225,64,10,3,170,149,
-  50,222,151,62,77,225,206,3,212,126,238,39,249,235,191,250,59,148,91,195,72,75,120,147,155,103,171,0,244,22,151,11,
-  218,201,2,112,31,240,32,240,0,120,45,48,234,194,215,59,85,240,249,29,223,120,248,208,241,7,158,115,236,161,29,76,
-  61,51,130,63,126,158,119,255,231,143,217,87,63,248,143,221,227,63,242,227,151,63,148,234,254,192,188,48,63,22,192,47,
-  75,248,185,24,124,64,194,117,243,176,201,129,54,7,236,101,16,106,117,184,223,212,27,217,242,61,114,159,251,123,254,224,
-  215,63,202,115,55,239,162,246,241,191,68,151,43,184,118,136,142,201,55,165,14,187,10,64,63,64,11,48,108,254,30,2,
-  178,224,250,66,140,251,82,62,210,165,220,175,111,250,242,163,39,31,253,187,111,248,201,29,219,8,85,129,83,247,126,134,
-  193,190,8,63,251,233,63,177,14,124,246,239,186,30,185,237,242,125,159,217,216,241,246,175,182,118,254,200,73,97,253,156,
-  134,159,181,225,199,21,220,169,224,178,121,88,51,3,153,115,13,48,146,149,75,78,226,87,55,63,123,85,190,31,205,248,
-  2,196,20,39,199,168,254,246,47,51,117,112,128,23,126,225,35,116,29,59,73,152,20,68,51,18,195,120,243,108,251,85,
-  18,250,45,174,130,155,52,74,92,196,64,238,132,72,28,98,123,32,82,6,43,166,117,2,104,245,161,214,175,130,197,200,
-  215,190,158,251,219,185,185,204,109,191,241,145,216,181,155,54,51,253,220,33,18,99,51,188,237,230,183,113,217,13,255,204,
-  3,95,254,188,125,254,145,39,218,78,141,140,183,77,14,207,173,237,158,205,30,88,27,250,51,105,56,111,194,176,130,241,
-  89,152,93,128,101,160,96,66,197,6,119,1,130,16,148,209,220,0,171,154,210,155,3,140,172,48,192,43,23,73,126,225,
-  31,232,187,231,179,100,223,249,35,28,186,246,22,54,189,235,3,116,181,197,168,85,87,1,104,85,190,7,147,75,52,249,
-  31,11,228,46,136,204,67,70,65,71,10,122,5,12,248,208,158,128,24,208,82,211,186,199,133,78,29,141,13,4,134,109,
-  4,147,11,222,63,255,201,39,98,238,237,215,176,251,250,189,200,184,201,233,71,191,64,207,192,90,62,116,199,157,204,95,
-  113,37,199,159,57,204,212,11,47,36,138,249,108,226,212,236,210,96,231,124,97,103,219,124,33,107,46,101,23,98,42,156,
-  181,27,152,55,227,55,126,22,134,97,217,130,194,58,168,70,192,115,33,176,65,201,85,205,232,205,35,190,71,199,231,62,
-  201,103,63,247,73,158,250,147,255,196,47,125,236,215,121,215,135,126,178,113,116,173,2,208,170,124,167,38,86,20,164,15,
-  86,39,196,103,161,43,6,131,186,81,227,107,115,29,134,10,210,30,200,71,51,173,37,83,70,74,17,25,9,186,59,147,
-  173,67,221,150,238,50,69,44,147,17,59,77,67,72,175,74,110,250,12,167,31,94,161,119,247,78,118,92,125,3,238,204,
-  56,179,11,103,176,218,58,185,226,242,13,108,90,223,203,244,240,56,194,194,240,188,82,139,151,93,105,145,217,202,250,197,
-  147,147,110,253,228,104,41,62,183,180,210,230,123,75,9,21,206,217,48,19,194,76,0,179,157,176,16,192,130,3,203,239,
-  132,90,5,130,55,39,251,240,195,107,162,229,39,206,243,251,191,245,11,124,226,227,127,200,244,196,249,85,0,90,149,111,
-  109,102,105,26,9,166,221,192,187,32,102,67,151,128,53,26,214,89,13,208,217,90,74,36,55,102,215,172,233,245,183,108,
-  204,68,55,14,68,186,134,58,89,159,18,152,182,65,38,145,36,21,73,160,113,241,106,101,242,217,34,147,19,243,20,125,
-  65,247,224,6,218,163,54,165,209,195,164,59,123,145,145,78,170,217,5,170,179,11,88,118,11,194,173,48,57,50,77,204,
-  142,99,72,65,203,134,30,99,199,85,59,227,66,136,120,113,33,219,179,112,122,118,235,194,185,37,71,143,206,151,219,114,
-  139,185,120,105,101,49,230,121,51,17,24,14,224,76,59,140,45,193,220,46,40,218,224,85,65,197,86,167,245,77,35,51,
-  147,163,171,38,216,170,124,235,211,202,1,118,131,116,32,146,131,150,40,108,240,225,160,134,253,158,97,172,243,182,111,31,
-  140,220,124,85,231,186,171,46,139,237,219,181,149,174,222,86,18,134,143,187,188,64,173,176,66,189,158,167,90,46,82,90,
-  89,96,113,198,225,252,200,12,11,139,115,84,42,21,52,154,186,231,113,199,157,55,35,60,147,249,177,73,2,165,145,94,
-  72,173,92,226,212,233,103,57,121,102,129,68,42,131,45,43,172,223,48,196,145,103,198,153,158,157,97,227,166,245,108,220,
-  188,150,125,119,94,38,115,55,86,226,227,243,115,241,220,66,177,123,102,198,223,102,78,228,170,241,231,143,45,117,230,151,
-  198,82,112,86,192,169,8,156,155,131,73,96,73,64,77,129,10,87,167,120,85,86,1,232,205,169,245,132,52,106,68,239,
-  130,104,4,186,13,88,175,97,91,8,251,253,100,122,175,185,127,199,154,246,119,95,149,105,191,249,114,187,189,187,7,83,
-  24,248,65,141,218,114,142,114,177,74,173,152,101,121,113,145,233,137,44,83,19,5,242,185,2,158,23,224,123,62,8,69,
-  123,71,59,123,246,110,231,236,185,83,204,206,76,49,48,48,64,16,212,241,157,58,197,124,141,83,167,39,152,153,207,99,
-  24,6,61,61,25,182,110,216,134,38,228,236,240,89,234,53,135,177,225,57,166,207,205,146,157,88,162,163,43,142,116,86,
-  216,56,48,128,215,37,197,226,166,174,100,112,195,254,228,236,249,197,65,253,204,11,187,219,199,206,78,119,248,222,176,132,
-  19,33,28,43,193,185,71,96,97,23,212,98,205,162,30,171,230,217,42,0,173,202,155,68,106,192,187,193,176,26,209,201,
-  155,3,184,50,132,203,107,241,196,22,174,61,56,212,255,222,107,91,218,247,14,200,120,103,154,170,227,178,56,126,26,171,
-  226,227,107,147,92,169,192,252,248,20,115,99,43,44,47,151,240,3,23,173,3,12,35,74,166,37,138,97,71,232,237,237,
-  225,178,131,251,104,105,73,176,148,27,101,165,48,75,60,33,72,216,38,181,98,153,51,39,167,153,157,171,98,88,38,187,
-  246,13,176,255,192,46,38,207,45,114,236,216,57,240,99,152,34,32,244,53,169,68,146,241,51,227,84,115,109,184,190,75,
-  118,230,44,67,27,6,8,22,179,184,161,195,208,149,91,173,232,29,63,222,85,24,47,116,142,223,255,220,38,243,201,39,
-  246,245,122,245,51,54,28,10,225,153,235,225,204,52,228,98,16,60,191,10,66,171,0,180,42,111,172,214,163,128,61,64,
-  90,136,72,0,253,66,235,253,2,110,168,152,230,149,165,131,187,55,164,223,182,163,165,123,231,128,72,102,98,84,150,179,
-  20,23,75,132,118,18,95,105,130,208,34,59,49,202,244,217,17,148,19,16,141,9,6,7,44,42,53,240,189,24,169,84,
-  146,104,90,209,183,166,151,173,155,119,83,173,214,153,158,158,102,112,112,128,149,149,121,226,49,73,173,84,228,236,153,9,
-  102,23,92,214,172,31,228,186,27,119,208,213,157,230,228,145,179,156,56,122,14,161,109,148,167,48,36,40,237,17,232,50,
-  209,152,65,177,178,140,101,69,113,106,117,70,199,203,236,217,188,159,82,54,207,241,7,159,164,173,183,155,129,157,59,197,
-  246,95,123,111,122,225,3,55,167,143,127,254,145,181,173,143,62,188,121,200,247,54,85,225,145,78,120,110,16,166,179,80,
-  75,128,94,117,223,175,2,208,170,188,1,18,0,189,192,207,64,52,167,245,122,19,110,242,17,111,31,235,235,61,160,110,
-  220,215,53,176,175,203,108,77,197,17,37,141,99,69,209,173,237,76,230,28,202,203,130,173,125,157,84,230,158,69,85,23,
-  88,55,216,70,185,88,162,144,175,83,42,7,152,81,73,215,64,156,150,76,43,155,182,109,32,153,206,48,62,58,207,83,
-  79,29,2,2,238,120,251,245,12,116,247,18,4,46,163,99,203,100,75,1,7,175,219,205,101,151,239,161,179,35,201,241,
-  231,143,112,234,216,73,146,169,52,197,66,158,100,44,192,13,20,117,87,19,104,137,176,99,8,229,35,76,77,75,164,21,
-  237,4,76,143,143,113,229,213,87,209,222,223,201,161,67,71,88,122,228,41,226,137,35,108,220,185,143,107,255,175,95,138,
-  21,127,244,253,219,167,254,248,127,116,182,157,60,54,152,130,161,50,60,121,13,156,42,195,202,213,224,63,180,186,28,86,
-  1,104,85,94,63,105,7,182,130,140,66,186,10,155,13,184,169,140,184,227,252,229,59,47,107,185,110,107,162,45,29,65,
-  40,73,45,189,134,192,202,4,167,142,79,250,247,62,241,141,232,141,7,47,19,63,241,206,43,200,207,60,71,57,59,73,
-  107,122,144,220,92,153,153,233,105,12,43,164,107,160,157,68,107,154,150,246,46,246,236,217,137,235,132,60,245,196,17,158,
-  63,124,140,82,185,204,174,93,27,105,107,109,165,94,54,120,225,217,51,44,45,213,185,233,174,119,48,56,212,74,123,71,
-  23,99,167,71,120,254,217,23,216,180,126,13,249,124,157,82,14,82,137,54,42,110,133,114,181,130,10,45,76,51,130,37,
-  192,182,21,137,136,6,83,226,122,112,236,244,121,174,184,230,50,2,35,224,244,209,83,184,229,128,83,79,61,195,209,67,
-  207,178,227,170,155,197,158,63,255,175,93,147,159,250,236,53,211,159,248,68,255,160,231,110,240,225,27,54,60,245,23,48,
-  245,235,224,127,101,117,89,172,2,208,170,188,246,210,10,252,21,200,86,104,173,194,126,19,222,158,179,172,155,231,110,56,
-  176,99,224,170,117,182,169,29,202,178,133,149,160,63,116,62,243,76,221,120,224,193,194,76,44,94,252,240,31,254,135,77,
-  63,118,215,94,251,196,67,95,66,121,154,109,91,110,225,241,71,30,36,191,56,67,166,61,195,192,218,181,40,203,164,115,
-  160,143,189,7,14,50,63,183,192,151,63,127,63,227,195,83,248,65,72,58,147,226,186,27,174,194,176,52,167,78,159,32,
-  183,92,226,246,119,223,141,213,210,202,82,193,67,137,26,143,63,246,48,251,118,239,197,64,114,238,236,51,248,129,139,20,
-  96,154,2,165,60,16,17,194,208,37,147,180,137,89,18,97,10,68,194,64,59,117,202,245,5,206,159,63,198,222,93,187,
-  112,75,101,150,230,178,8,105,83,118,2,158,254,250,151,24,31,57,205,59,126,225,23,227,139,183,222,180,237,153,127,247,
-  219,157,187,167,198,58,99,144,88,129,111,252,58,76,206,128,119,100,117,121,172,2,208,91,215,166,81,12,245,167,248,104,
-  204,36,94,15,222,116,143,39,104,212,245,249,40,200,53,208,86,134,131,6,188,103,50,154,184,173,240,174,221,107,217,176,
-  70,156,158,245,48,74,34,104,157,122,161,218,57,242,47,225,96,62,111,44,173,221,144,254,137,191,253,179,142,157,87,174,
-  181,135,31,252,2,169,100,23,117,87,241,141,175,63,64,177,176,196,174,45,107,104,233,236,166,26,73,17,177,108,118,221,
-  120,51,185,5,135,251,62,251,48,147,19,179,8,13,65,205,231,170,59,111,97,243,229,87,240,232,87,63,67,33,183,192,
-  141,31,248,8,166,213,201,99,143,223,199,187,127,228,102,14,223,243,5,246,93,117,128,181,93,67,60,248,185,127,33,12,
-  67,236,72,132,13,59,58,41,29,117,113,67,129,182,5,161,31,96,96,99,199,45,132,37,136,89,54,150,105,226,4,62,
-  197,133,25,178,109,105,182,236,187,140,74,253,49,98,65,149,221,215,220,196,218,66,200,163,159,249,60,255,244,7,255,149,
-  219,126,226,131,226,214,207,252,69,231,151,127,241,223,95,183,239,200,243,86,43,24,2,30,252,67,152,248,3,112,31,88,
-  221,159,171,0,244,86,20,215,11,217,179,181,141,191,89,147,134,115,43,111,202,103,60,2,194,133,150,50,92,110,192,123,
-  78,183,244,222,185,242,246,253,125,139,97,9,239,203,71,195,93,203,197,149,205,229,106,37,89,173,24,85,116,98,106,203,
-  118,251,170,79,252,81,116,211,238,1,49,124,228,27,196,218,7,153,31,30,231,200,83,143,97,101,210,92,119,235,53,152,
-  134,137,136,167,73,122,16,198,147,212,42,240,249,79,124,146,233,233,81,226,210,100,185,234,209,218,155,228,250,119,222,206,
-  204,244,34,217,51,103,185,250,182,187,17,45,29,252,239,63,249,115,222,255,225,119,49,125,242,24,27,247,236,102,104,112,
-  3,15,127,249,171,204,231,107,24,74,210,217,155,34,149,72,160,252,0,219,180,16,66,161,209,4,174,70,9,69,58,147,
-  33,168,58,180,102,146,172,84,106,24,210,96,126,126,129,237,3,91,217,188,125,43,51,195,39,153,62,119,148,93,55,125,
-  16,251,125,119,243,240,61,247,242,197,191,254,43,110,184,253,38,222,243,201,191,74,125,237,55,126,239,234,161,123,191,104,
-  12,128,136,195,253,127,14,19,191,10,222,189,172,22,183,95,5,160,183,160,8,165,121,108,99,43,75,231,86,136,190,137,
-  22,176,160,209,147,61,6,86,20,54,90,240,246,231,83,233,183,141,92,182,182,79,59,14,137,35,147,206,221,83,211,167,
-  19,232,9,5,201,10,172,29,219,185,43,115,197,159,255,135,104,95,166,34,150,142,61,65,88,79,241,220,11,143,48,55,
-  114,130,205,219,46,99,221,206,109,84,107,53,226,189,27,33,48,56,126,232,16,55,223,125,19,159,251,248,167,152,25,61,
-  135,21,53,113,11,62,90,187,236,58,120,45,86,75,154,35,159,253,59,118,238,191,150,68,219,102,254,244,79,255,148,157,
-  251,182,210,158,12,9,173,14,98,177,56,227,195,167,153,60,55,73,24,104,122,6,51,244,182,165,113,156,0,29,64,196,
-  18,72,20,66,8,66,109,162,181,162,165,189,29,199,42,35,181,207,218,129,126,22,151,22,49,172,24,11,51,227,108,222,
-  190,147,234,202,50,209,120,148,51,71,30,229,192,141,119,80,169,228,152,57,113,130,35,143,60,70,53,95,227,93,127,242,
-  159,18,255,24,56,87,69,30,184,79,117,128,154,129,7,126,23,38,62,2,254,111,209,232,171,190,42,171,0,244,150,17,
-  165,225,234,15,110,229,239,31,158,98,164,230,191,105,190,104,72,131,120,222,3,182,11,253,121,196,174,177,173,91,7,172,
-  84,20,207,180,216,92,115,22,82,232,23,60,152,171,192,238,225,237,91,219,238,252,227,255,51,221,221,150,19,249,108,157,
-  225,163,11,44,156,58,139,142,150,185,250,237,55,210,187,102,19,179,217,58,235,182,239,167,88,11,249,234,151,191,200,93,
-  239,126,39,143,124,241,203,204,158,62,135,29,143,147,207,175,32,137,144,110,177,184,230,230,59,152,124,246,16,118,44,32,
-  185,237,0,255,248,231,159,33,29,137,112,249,21,91,177,35,9,102,22,230,233,145,146,179,199,39,88,41,86,24,90,223,
-  67,166,45,74,87,111,23,83,99,203,24,40,44,41,8,3,31,35,158,64,99,128,14,8,8,73,182,183,82,93,154,39,
-  157,78,81,172,214,73,36,227,148,139,203,184,129,199,250,237,91,25,31,27,167,191,191,155,147,207,61,206,45,239,125,39,
-  15,212,139,36,39,22,88,56,241,12,45,189,157,252,236,223,254,89,236,239,223,247,225,203,15,28,58,164,18,16,58,112,
-  95,15,204,232,102,141,234,55,242,208,88,149,85,0,250,174,68,123,33,230,134,22,254,231,250,22,14,159,204,190,169,158,
-  109,29,240,73,16,6,68,235,66,196,172,142,148,236,137,39,24,173,72,10,94,24,24,96,215,97,221,83,91,215,94,254,
-  142,255,250,163,157,29,93,54,197,34,60,241,224,163,228,38,230,24,236,235,224,218,155,238,68,117,116,48,182,82,99,247,
-  238,93,228,230,10,124,225,239,255,137,107,111,189,129,201,225,97,78,28,122,148,68,36,66,177,6,181,186,65,34,226,178,
-  102,251,110,162,49,201,137,227,135,216,115,203,29,124,233,11,159,167,184,56,201,157,239,127,39,61,237,157,140,13,79,48,
-  122,234,44,108,217,196,137,23,70,24,92,219,199,198,109,189,136,48,164,88,113,153,95,88,70,10,69,212,50,241,116,136,
-  52,37,94,224,227,7,26,215,169,211,223,223,15,78,21,45,52,107,214,111,96,102,114,148,254,158,118,102,103,103,89,179,
-  115,43,177,229,28,137,68,27,197,106,141,220,194,20,215,188,251,71,120,232,111,254,28,195,142,114,254,169,199,25,220,188,
-  133,31,255,244,223,165,255,238,166,187,46,191,126,106,162,106,64,54,132,242,255,128,149,95,162,145,203,241,70,200,157,192,
-  41,88,237,107,191,10,64,223,165,248,154,63,248,217,221,124,242,183,30,37,246,38,33,163,3,26,133,222,45,208,30,4,
-  150,86,126,88,169,106,217,153,18,118,232,144,174,149,45,31,118,63,188,161,171,247,186,255,240,190,238,205,61,67,148,115,
-  33,247,221,251,56,243,147,75,92,181,111,43,123,174,28,162,158,238,97,97,185,194,222,77,67,76,159,25,231,51,159,254,
-  10,67,107,186,104,75,68,120,226,203,247,97,9,15,55,2,249,233,10,145,120,10,83,21,184,236,178,131,76,15,31,98,
-  253,238,237,28,125,126,134,217,23,142,177,123,215,102,246,237,219,193,82,110,153,227,143,62,66,186,173,155,71,30,121,1,
-  133,98,223,101,123,8,205,26,38,146,167,159,126,26,161,37,201,148,77,213,115,41,87,20,194,0,67,132,40,213,208,130,
-  34,81,155,84,71,23,245,90,149,206,174,62,140,120,130,120,58,195,114,190,64,93,197,24,216,186,157,217,99,103,216,116,
-  217,181,28,125,242,235,92,255,142,237,236,184,253,29,156,190,231,11,68,8,121,244,179,127,207,135,126,245,223,115,213,199,
-  255,180,229,217,119,126,224,224,245,129,59,173,97,54,1,199,174,3,231,93,52,26,47,190,94,230,180,160,209,2,105,8,
-  56,182,138,21,171,0,244,93,147,209,126,200,205,7,123,137,189,237,114,106,247,188,64,50,116,222,112,46,72,52,65,168,
-  42,68,96,65,190,69,235,101,39,183,236,22,183,182,69,141,88,192,11,102,44,49,220,18,111,217,245,203,239,200,28,88,
-  183,153,226,66,137,175,127,229,81,102,22,23,185,226,170,131,236,191,238,50,220,120,156,249,133,49,54,246,244,50,117,102,
-  152,47,125,238,33,12,13,151,95,125,5,79,220,255,16,94,165,66,36,146,96,58,183,130,17,51,8,252,26,157,131,189,
-  180,24,14,11,229,34,210,90,195,243,247,127,129,174,182,118,174,185,237,42,234,213,37,14,61,248,32,182,25,99,114,106,
-  150,243,227,243,188,253,230,27,105,233,109,69,136,62,190,113,207,131,228,203,30,189,157,237,40,175,68,60,22,197,205,150,
-  145,134,36,26,19,4,190,34,172,215,240,92,151,100,71,55,70,97,9,199,41,211,187,102,136,82,181,72,111,79,47,185,
-  185,101,54,238,216,72,177,99,28,183,84,99,219,142,203,24,126,225,105,118,222,240,46,102,78,30,71,207,12,163,170,121,
-  30,254,204,95,243,206,95,253,247,156,254,165,159,239,27,255,211,63,187,122,8,198,235,144,253,29,152,52,32,124,130,70,
-  195,198,215,235,176,24,4,54,208,168,185,157,92,197,139,85,0,250,174,57,151,186,199,169,107,174,71,143,57,140,158,120,
-  1,255,77,96,211,251,192,101,90,251,235,96,62,2,19,3,211,139,165,178,179,62,26,139,216,60,222,25,239,248,137,15,
-  94,203,173,91,118,144,159,94,228,254,135,30,165,180,156,103,247,142,141,92,113,235,1,114,126,146,194,236,50,157,145,8,
-  165,185,105,238,255,234,19,44,231,86,184,235,125,111,103,122,114,150,236,204,50,118,36,194,74,197,195,175,128,142,249,216,
-  150,205,150,13,107,169,150,230,232,236,216,194,3,247,61,142,233,229,217,125,219,173,88,173,157,60,125,207,61,88,245,34,
-  185,178,199,212,76,158,206,84,156,43,174,222,67,188,175,135,67,247,60,196,249,115,211,180,175,93,71,166,205,164,152,45,
-  227,122,53,66,101,82,173,213,24,236,238,164,82,244,33,240,241,156,58,118,75,15,201,150,12,142,227,144,238,236,98,170,
-  88,160,181,163,139,252,228,28,245,250,38,122,182,236,99,250,244,17,6,247,188,141,153,153,5,170,217,105,246,222,113,39,
-  135,254,106,132,104,194,98,105,250,28,71,238,253,28,63,250,91,255,70,254,229,211,207,109,237,125,238,153,27,44,152,172,
-  192,202,151,160,240,31,223,0,243,235,83,171,56,177,10,64,223,215,151,172,85,57,115,203,173,76,207,79,161,151,151,223,
-  112,0,114,128,187,33,148,141,190,127,83,221,229,242,242,241,115,11,93,253,253,107,185,243,134,237,28,216,219,207,92,174,
-  192,177,195,231,153,155,42,177,123,247,102,246,94,127,13,243,139,57,168,101,145,229,28,178,179,147,251,31,58,198,236,82,
-  137,221,7,54,211,221,191,134,175,252,195,23,176,67,240,112,112,235,62,166,157,164,84,175,208,219,31,37,29,171,98,70,
-  58,56,127,106,140,194,236,18,173,3,61,108,220,212,205,217,135,31,32,63,151,101,197,181,152,207,151,241,116,145,187,110,
-  123,15,61,59,246,113,244,249,195,28,127,238,24,101,4,87,239,219,128,63,49,73,162,127,55,231,166,142,18,143,72,44,
-  21,18,79,39,41,151,74,88,126,26,211,55,128,16,51,211,69,186,93,18,250,54,237,221,189,148,36,116,173,233,39,63,
-  55,204,224,222,91,137,45,230,41,85,86,216,182,107,23,147,135,159,102,251,29,31,160,99,215,30,166,158,123,142,142,214,
-  22,142,63,253,36,155,118,109,225,154,223,253,88,114,228,67,35,251,118,149,115,35,37,24,91,7,181,35,224,101,120,125,
-  88,105,5,44,241,237,59,209,174,202,42,0,253,235,11,73,107,34,134,201,71,15,94,206,173,247,126,237,77,241,76,115,
-  192,104,3,132,252,65,100,125,238,244,233,176,163,199,54,222,113,240,6,220,162,195,217,137,19,140,156,153,103,253,166,126,
-  54,31,220,199,82,190,68,220,180,152,31,155,96,221,224,32,207,29,58,66,118,49,71,34,30,227,134,235,175,225,209,71,
-  159,32,168,87,144,134,164,232,128,235,75,66,29,176,156,93,228,178,45,173,88,145,24,133,178,203,201,99,47,96,9,159,
-  3,187,247,48,123,126,130,241,241,25,10,165,144,133,114,13,79,5,220,176,239,74,174,121,251,141,204,204,143,243,212,231,
-  239,35,87,118,233,93,59,64,202,148,4,153,52,35,51,57,188,208,37,149,177,80,190,38,29,79,82,107,113,209,66,35,
-  180,194,150,6,134,48,201,116,247,176,188,84,33,221,217,141,31,84,105,27,218,74,233,228,11,248,165,69,186,55,239,101,
-  114,244,20,235,55,109,66,159,51,88,26,61,78,255,182,45,204,140,156,34,148,146,238,246,14,158,253,210,61,92,251,225,
-  31,99,226,195,239,238,47,126,252,111,174,72,192,153,93,48,119,24,178,255,9,72,188,198,243,83,5,222,9,252,6,171,
-  49,72,171,0,244,42,240,46,132,33,19,93,221,124,218,178,89,241,189,55,172,29,136,15,220,14,44,131,169,160,219,132,
-  161,69,51,222,150,234,77,27,87,237,90,131,95,90,98,97,197,229,204,232,44,169,22,131,45,91,215,177,82,45,210,158,
-  106,225,220,11,71,88,191,110,29,163,99,83,156,59,55,73,221,117,185,254,198,107,40,23,74,204,143,76,96,138,144,58,
-  154,92,197,64,72,141,83,89,161,45,105,145,140,154,216,137,14,142,157,28,70,121,21,134,134,122,49,60,135,83,103,207,
-  145,173,4,148,28,73,221,115,88,219,222,198,53,183,221,130,39,3,30,253,199,127,162,92,172,178,88,169,112,199,193,189,
-  212,22,166,168,213,234,156,159,24,39,17,129,84,202,164,86,242,177,132,69,91,103,148,122,177,142,48,4,182,52,49,13,
-  139,74,185,142,176,162,216,81,129,87,168,33,19,29,116,12,109,165,180,56,78,251,142,107,73,183,117,224,84,43,172,217,
-  178,141,133,201,115,180,68,147,108,234,236,97,120,108,134,190,129,77,172,20,203,44,12,31,103,237,135,111,52,143,125,249,
-  129,173,87,207,77,237,43,194,209,1,200,223,7,129,249,26,3,67,216,156,171,202,235,0,118,171,0,244,67,32,86,24,
-  144,235,238,230,119,58,58,152,155,159,123,67,159,229,19,192,46,200,4,176,51,64,238,249,106,202,236,187,226,134,237,36,
-  133,65,185,230,113,106,116,158,122,32,217,185,103,16,39,8,232,203,100,152,30,25,37,98,88,168,80,113,246,236,56,97,
-  40,73,38,227,108,217,184,134,71,30,126,12,225,73,66,9,139,213,34,136,86,68,80,98,176,39,131,16,73,218,59,186,
-  40,149,92,150,231,150,104,111,73,16,77,70,56,59,60,74,97,217,165,162,13,102,23,151,232,73,89,236,220,187,145,68,
-  87,146,199,62,255,53,102,39,150,88,88,42,177,101,221,26,218,18,154,211,249,5,230,151,125,156,74,157,222,182,56,134,
-  45,201,86,28,234,46,116,244,180,177,232,47,162,77,11,165,13,34,241,20,147,75,43,180,246,174,193,76,216,216,174,139,
-  87,117,137,247,108,36,91,88,193,173,20,72,181,245,81,201,205,211,214,222,67,108,126,138,154,83,198,111,73,18,233,74,
-  113,100,248,40,151,93,113,45,165,185,57,182,239,216,197,147,59,55,181,85,231,166,182,153,176,81,192,120,59,20,186,94,
-  7,243,235,12,171,29,62,86,1,232,85,18,13,104,165,249,191,59,58,152,158,159,123,67,120,160,0,216,12,108,4,171,
-  2,67,17,184,242,1,219,220,25,63,56,24,217,209,217,142,19,106,78,141,44,49,53,190,200,190,131,67,4,129,164,22,
-  132,196,80,120,133,34,93,93,253,156,56,113,22,215,215,84,42,21,174,223,127,128,153,233,41,230,103,22,64,167,240,13,
-  73,197,201,97,186,43,108,93,147,36,157,73,178,82,170,210,218,217,199,185,115,19,68,132,198,178,34,228,235,30,11,203,
-  53,2,199,96,62,159,197,196,101,215,206,77,236,186,124,43,39,158,121,146,177,211,147,76,45,215,48,165,224,138,141,189,
-  44,206,156,97,227,101,59,89,250,198,8,218,13,72,153,54,75,69,151,137,133,89,10,117,197,134,76,55,42,12,48,19,
-  73,132,29,199,136,165,176,18,6,161,17,39,146,72,224,213,92,66,167,140,76,117,17,233,88,71,80,88,38,210,190,6,
-  59,209,142,178,76,82,109,93,248,229,28,6,81,58,18,9,86,86,86,24,30,59,203,229,251,47,35,234,4,180,29,92,
-  99,14,63,104,108,220,171,194,221,10,142,157,134,210,92,35,214,244,53,3,159,12,141,70,0,171,230,215,42,0,189,106,
-  98,75,193,51,155,55,243,201,19,199,223,176,103,248,101,96,7,100,52,236,42,10,246,159,236,74,116,95,181,126,19,117,
-  223,228,204,236,44,99,83,89,118,109,92,79,151,105,83,93,41,83,143,39,201,23,138,132,74,51,183,144,37,183,82,161,
-  238,134,196,226,22,107,215,246,243,252,115,207,163,148,137,231,43,166,150,151,209,166,77,198,172,115,235,53,151,115,242,236,
-  44,126,42,201,66,54,79,49,151,39,110,71,112,125,200,45,85,169,249,38,78,221,163,176,178,194,205,215,238,98,231,190,
-  173,76,143,141,115,252,133,147,12,143,45,163,3,147,104,218,192,165,68,79,87,15,78,32,25,29,31,165,163,163,133,72,
-  36,205,212,217,19,244,118,181,81,169,215,137,39,186,112,131,58,50,30,131,104,156,192,138,16,104,159,194,74,145,129,238,
-  22,108,211,196,115,202,68,188,58,177,76,59,70,190,138,174,150,136,165,219,81,161,67,44,213,66,224,213,105,233,236,97,
-  246,204,89,34,194,102,105,108,146,197,158,33,182,116,247,179,255,170,45,252,195,96,186,123,247,100,126,187,9,131,117,152,
-  106,17,56,81,33,94,19,13,69,105,152,209,154,204,42,62,172,2,208,247,203,253,24,198,75,207,48,17,188,113,1,137,
-  221,192,219,32,226,55,58,90,92,118,38,30,223,52,176,111,187,140,26,38,103,198,102,153,43,58,164,50,22,173,29,38,
-  132,146,114,121,133,206,53,3,228,242,14,133,74,149,192,119,240,85,132,82,117,145,171,175,216,69,185,88,100,97,33,71,
-  46,95,197,247,171,140,77,76,33,77,155,15,254,232,213,180,183,218,228,75,30,118,42,206,202,194,28,6,224,32,169,150,
-  92,28,79,224,7,146,185,236,60,91,54,12,178,117,219,58,68,16,48,114,102,154,231,207,205,19,81,81,76,229,208,215,
-  213,70,251,230,13,68,35,113,30,252,204,3,216,194,161,103,176,143,177,209,60,166,17,161,167,53,73,110,57,139,136,167,
-  177,220,4,177,136,69,44,147,38,16,26,21,106,178,243,51,208,151,193,10,125,180,1,97,117,9,51,22,129,104,2,183,
-  84,37,154,105,69,105,133,178,227,84,61,159,214,181,189,56,118,43,149,165,9,16,130,211,39,78,209,51,212,199,230,129,
-  94,50,87,110,139,205,77,62,53,216,7,107,210,112,52,121,93,143,211,241,43,219,161,250,42,207,103,212,192,123,122,137,
-  194,159,158,186,216,131,109,85,222,34,0,36,66,31,180,70,232,111,61,115,90,74,180,52,95,23,240,177,45,197,83,195,
-  105,230,10,54,166,161,241,124,159,161,161,117,252,175,206,78,98,217,236,235,110,223,39,128,118,104,9,97,79,89,136,253,
-  75,67,67,157,3,125,61,156,30,31,70,210,78,61,95,98,235,254,65,60,81,198,140,183,80,203,105,100,80,103,110,170,
-  140,235,9,28,207,163,238,64,34,149,102,211,230,33,158,125,242,41,234,53,143,68,186,149,192,119,217,180,113,136,233,233,
-  28,189,61,173,148,42,69,66,34,40,165,241,74,43,24,34,77,222,245,169,187,33,102,96,145,93,92,32,22,179,217,180,
-  174,159,84,58,198,217,115,195,156,56,54,131,242,19,24,17,131,206,76,72,207,218,78,90,218,123,120,232,75,223,96,126,
-  126,133,158,158,40,21,191,200,200,194,18,59,214,246,210,106,249,148,242,5,202,158,75,34,221,2,97,128,157,138,227,4,
-  33,137,104,140,229,233,89,156,245,157,4,202,193,204,164,240,43,57,68,221,71,71,186,112,148,32,82,93,70,91,22,102,
-  44,129,246,125,132,25,167,111,231,65,206,31,57,73,172,59,73,189,144,229,196,11,135,232,28,90,207,142,45,59,152,146,
-  207,244,174,81,106,75,21,186,71,94,200,21,150,126,227,185,240,33,165,95,213,192,68,71,10,150,170,62,31,1,188,85,
-  124,120,139,105,64,226,146,51,67,235,111,77,198,188,142,224,115,255,137,86,44,67,53,74,58,104,141,25,137,112,109,95,
-  31,219,179,175,111,126,152,6,38,192,156,134,62,19,246,12,183,182,110,180,183,247,202,217,233,69,164,72,115,108,228,60,
-  107,122,227,180,91,9,92,55,68,91,130,48,208,120,245,144,229,165,44,210,202,80,174,130,235,20,217,190,181,135,202,74,
-  129,226,138,75,36,146,34,48,67,178,185,34,61,241,24,187,175,218,198,99,207,159,100,239,182,1,234,94,29,101,36,16,
-  50,66,185,28,195,49,60,108,51,32,87,9,169,73,159,125,235,186,233,236,78,115,234,220,4,199,143,156,163,94,49,216,
-  50,180,150,120,220,199,169,251,108,221,188,145,83,135,142,114,250,244,52,233,214,20,157,61,93,156,56,57,65,95,107,134,
-  84,139,32,29,137,145,155,206,147,43,213,105,221,49,72,117,105,5,225,25,216,82,147,72,26,212,202,5,74,249,25,44,
-  11,12,67,16,202,40,166,246,9,107,115,196,226,105,38,199,102,105,235,233,38,38,77,210,153,36,243,231,79,179,118,235,
-  62,156,76,27,178,90,163,61,109,176,60,91,162,183,31,114,97,157,195,241,72,251,213,149,250,78,19,182,234,138,63,251,
-  217,138,95,252,143,175,193,92,221,13,68,86,1,232,117,17,249,90,236,126,45,5,136,55,70,121,125,37,240,145,151,60,
-  74,8,28,217,188,229,117,127,174,57,96,188,193,253,108,211,49,123,215,242,166,13,237,174,136,178,130,226,232,124,141,137,
-  185,101,54,111,28,196,169,214,136,69,18,228,139,53,234,174,67,97,165,132,33,13,180,8,8,117,29,116,141,193,254,78,
-  74,249,2,201,88,6,169,35,184,149,0,165,5,151,239,31,226,206,107,215,83,171,133,140,205,150,9,181,135,246,3,74,
-  85,143,26,101,12,195,162,80,87,228,114,69,214,36,91,217,186,126,128,74,81,113,228,217,89,170,213,40,123,47,223,192,
-  238,61,237,84,75,33,189,131,59,89,200,213,121,230,217,83,164,18,146,189,123,214,178,176,156,39,8,108,90,210,22,201,
-  136,164,88,83,76,46,174,176,48,51,71,186,181,3,35,18,199,173,86,241,106,14,241,168,137,227,212,88,94,92,194,173,
-  150,168,151,114,104,183,138,10,61,138,217,5,76,29,176,56,61,205,242,236,12,97,173,68,52,18,165,86,40,99,20,231,
-  233,104,109,161,230,133,132,190,198,201,151,88,158,93,228,150,107,246,225,108,236,140,157,128,173,54,28,4,214,108,7,235,
-  5,26,197,221,134,95,133,159,49,224,126,26,45,145,86,193,231,45,206,1,105,41,26,10,145,126,125,253,8,134,161,191,
-  37,248,0,72,165,112,98,49,78,89,22,117,223,127,221,80,190,4,166,108,104,63,251,167,6,82,91,11,67,182,204,21,
-  10,248,85,55,44,158,56,231,236,218,191,62,222,22,141,9,207,169,32,140,56,213,186,143,101,155,132,94,136,129,196,113,
-  107,4,94,157,246,182,4,17,203,96,33,191,66,168,20,129,22,44,231,74,68,146,130,237,155,187,232,138,105,134,122,186,
-  153,205,86,17,134,133,143,166,226,107,204,168,71,224,184,104,124,210,41,69,87,123,134,207,63,114,152,145,169,42,217,108,
-  137,116,91,138,150,13,67,140,207,140,210,155,137,17,181,202,28,121,106,4,203,15,184,249,166,61,228,10,75,44,47,231,
-  73,164,186,104,77,43,80,146,217,229,58,21,108,38,70,70,160,126,43,201,76,6,191,94,129,168,73,128,160,173,53,73,
-  110,49,75,210,110,35,84,154,168,22,141,14,32,245,58,181,220,50,150,10,88,30,31,165,51,38,48,149,34,157,105,97,
-  242,204,11,108,236,111,103,228,228,9,90,99,157,196,82,6,227,231,206,49,184,161,143,95,251,141,159,230,19,191,249,103,
-  131,3,211,43,87,167,97,100,16,150,214,193,98,203,171,56,87,47,52,181,159,85,239,215,15,0,9,253,189,130,80,163,
-  135,245,119,175,65,89,134,230,252,82,140,175,31,111,197,54,191,25,124,0,204,32,96,182,171,151,199,110,184,140,205,76,
-  17,190,150,124,148,33,16,121,23,255,153,121,52,216,41,24,148,182,177,109,177,61,222,94,10,125,188,154,230,170,195,167,
-  244,181,42,112,39,135,122,35,97,221,51,237,136,77,165,230,80,119,5,145,168,141,82,26,83,154,132,174,34,12,13,58,
-  90,218,169,151,235,56,117,15,199,245,89,42,20,81,166,207,186,53,93,156,58,125,142,53,215,237,166,61,29,50,53,94,
-  71,102,58,200,57,117,164,97,163,188,24,78,221,99,219,150,1,114,115,115,36,99,146,122,201,32,105,11,118,95,213,133,
-  165,5,143,127,245,97,202,150,73,255,80,146,222,169,4,235,162,105,222,127,215,78,18,241,20,71,142,157,37,145,76,98,
-  133,1,177,136,77,190,92,35,87,241,137,198,50,44,205,44,82,92,204,17,239,104,195,117,106,16,106,194,32,164,163,53,
-  69,118,120,140,206,140,32,40,231,105,109,15,176,35,49,34,82,80,90,156,167,51,19,227,228,209,179,248,67,29,132,190,
-  166,165,181,149,233,169,97,34,134,69,111,58,205,226,114,137,100,87,27,237,49,131,211,207,31,231,234,155,174,226,182,95,
-  125,159,253,248,127,252,196,142,187,43,222,213,30,156,125,26,10,55,128,27,127,149,166,44,191,10,62,63,56,0,244,61,
-  129,144,10,27,149,114,190,23,11,78,128,231,55,62,40,191,205,231,181,227,211,122,199,229,220,114,211,32,248,175,97,76,
-  116,212,132,99,89,170,207,204,243,28,68,4,116,85,226,86,207,76,44,98,230,243,130,203,207,12,115,192,113,228,61,109,
-  233,84,34,106,154,245,114,133,68,50,73,174,92,197,215,6,81,105,162,128,192,15,81,74,18,4,10,195,20,184,174,71,
-  185,226,178,82,113,8,12,131,206,238,65,182,236,220,197,220,169,231,169,215,61,44,83,35,13,131,154,235,226,6,130,56,
-  54,149,165,69,174,189,241,0,43,203,51,100,90,90,153,93,206,210,27,85,252,232,29,87,115,195,45,123,120,250,201,163,
-  12,182,230,241,80,140,206,79,115,126,120,153,229,214,58,219,74,91,145,211,231,169,121,33,65,8,237,209,128,90,5,38,
-  22,11,84,93,69,75,170,149,236,66,145,243,231,199,217,158,138,160,188,42,166,157,34,116,125,210,9,155,145,92,158,226,
-  114,156,72,202,194,169,228,81,161,143,82,38,197,66,133,150,142,54,234,149,2,203,217,44,173,201,4,182,109,2,22,174,
-  87,34,211,102,227,87,163,76,205,231,88,187,111,3,229,92,150,217,153,37,174,62,176,149,133,15,94,219,62,241,183,15,
-  237,29,130,189,192,248,121,152,223,253,166,229,36,86,229,13,5,160,151,130,144,250,215,193,231,251,229,128,196,119,130,93,
-  2,225,121,80,246,112,188,215,112,201,121,33,86,205,191,48,208,73,9,221,217,184,108,61,47,163,12,78,140,115,121,174,
-  160,179,160,139,29,29,70,171,52,241,132,192,215,6,78,40,240,131,0,132,65,168,160,230,6,248,210,34,212,14,97,88,
-  193,113,13,20,49,140,88,130,236,236,28,207,158,56,204,87,30,120,150,119,223,184,19,66,133,34,69,16,113,240,189,50,
-  86,24,163,86,169,113,112,111,134,158,78,205,200,249,42,158,105,176,88,45,242,254,219,182,113,227,141,251,120,228,248,56,
-  71,79,77,144,136,117,82,43,215,56,184,125,11,191,245,43,123,89,88,28,231,190,135,143,112,236,252,18,189,3,253,12,
-  181,37,136,69,5,147,179,37,114,249,18,177,84,138,68,196,162,88,23,12,159,27,103,243,246,117,84,86,150,200,152,38,
-  129,231,19,248,46,149,98,157,165,133,101,50,97,28,211,180,0,19,203,142,224,212,139,4,142,133,20,33,203,75,203,196,
-  227,54,34,8,233,78,119,49,154,205,163,18,96,135,17,90,204,8,139,75,75,108,221,190,133,236,114,129,254,181,29,92,
-  125,231,205,60,246,208,185,245,3,19,179,151,197,225,248,60,228,55,128,243,253,164,76,132,77,243,107,21,128,126,0,1,
-  232,69,16,18,175,41,248,124,199,167,156,212,20,170,38,165,186,137,149,146,4,175,209,178,211,9,139,200,87,38,200,129,
-  225,67,91,28,6,71,205,68,107,109,46,199,205,139,133,170,173,9,231,77,17,9,123,218,109,223,13,81,82,80,115,53,
-  1,6,86,196,32,12,21,1,10,95,73,28,213,232,187,5,18,199,215,56,129,96,38,91,224,200,240,24,185,66,25,37,
-  35,60,118,124,152,95,184,235,50,42,85,240,80,132,161,70,121,117,218,146,46,215,223,114,61,95,252,234,97,42,53,147,
-  66,121,158,171,118,173,231,242,61,219,152,61,63,193,200,211,199,137,201,22,150,75,5,122,6,91,120,247,123,222,70,123,
-  52,164,191,67,163,61,77,189,22,97,114,102,153,254,116,130,106,96,144,47,231,233,110,73,163,109,139,154,83,197,83,154,
-  209,243,147,120,181,18,94,181,128,114,91,49,16,212,42,21,220,80,83,173,56,196,226,38,97,50,164,232,172,16,73,196,
-  80,126,29,191,90,34,30,143,145,95,46,210,217,213,142,235,86,137,69,34,8,211,192,182,18,232,160,206,192,96,55,11,
-  217,9,178,133,42,81,203,98,108,100,134,13,219,183,179,233,67,55,181,204,252,193,167,246,12,193,30,15,198,70,27,90,
-  208,247,108,61,105,26,9,168,171,113,63,63,160,0,212,0,33,121,33,39,226,101,127,120,125,173,110,83,106,22,10,17,
-  62,127,184,135,247,113,156,104,169,134,146,175,1,8,69,12,196,112,158,42,88,26,186,149,45,215,140,214,189,212,45,181,
-  122,169,191,234,157,243,193,46,180,199,214,183,116,219,17,199,113,176,19,54,78,45,192,247,3,220,48,192,22,22,142,227,
-  161,100,148,98,65,81,200,149,184,108,219,16,229,98,141,108,190,196,249,169,41,234,129,203,109,119,221,198,35,79,62,195,
-  120,182,192,216,146,162,82,171,98,40,31,29,196,112,41,243,158,119,30,96,41,95,226,252,116,1,167,88,227,198,171,215,
-  178,119,251,58,254,215,23,159,64,27,22,182,52,153,95,158,227,224,181,59,184,237,214,171,201,196,77,202,203,115,56,85,
-  56,125,122,150,182,182,24,109,109,235,89,90,92,97,37,167,217,176,113,13,182,97,51,54,91,160,98,40,180,33,41,172,
-  20,88,158,159,195,112,107,204,207,205,146,238,237,195,148,146,88,52,69,177,148,39,98,67,220,78,96,199,99,148,253,2,
-  58,244,41,23,86,144,192,226,252,50,155,214,15,225,84,170,24,177,4,117,215,33,226,219,40,167,128,163,11,116,117,118,
-  50,61,58,207,198,93,235,88,156,89,198,52,206,177,229,109,123,197,99,95,124,118,157,119,110,228,50,27,142,47,195,74,
-  245,251,208,130,70,128,5,126,112,34,115,47,180,251,190,180,163,136,15,108,7,10,64,180,249,218,109,66,80,3,254,127,
-  64,43,200,19,64,7,176,95,32,42,32,109,13,69,208,25,96,14,212,156,144,106,141,148,220,22,190,10,1,160,90,191,
-  1,227,45,52,66,135,47,49,135,244,27,112,238,216,166,98,44,151,160,240,143,231,25,92,124,237,146,83,235,192,50,68,
-  109,232,94,50,69,111,143,172,235,43,115,225,41,87,115,196,132,181,174,157,88,23,137,199,168,20,115,152,106,128,114,169,
-  140,138,40,100,16,226,25,18,169,34,248,182,197,220,202,40,145,192,71,134,1,110,16,16,218,138,80,123,220,125,235,205,
-  12,172,93,207,195,135,158,192,117,66,166,86,106,212,61,129,210,38,117,127,133,91,175,92,199,182,141,235,248,155,207,60,
-  207,242,138,195,205,151,247,115,231,141,59,8,48,145,113,139,231,78,206,177,110,176,139,247,189,255,26,246,31,216,141,54,
-  193,119,92,12,51,198,244,226,18,115,139,85,140,68,18,148,79,38,99,114,228,236,57,118,238,186,3,175,84,197,50,12,
-  240,192,50,4,150,116,152,157,203,179,169,51,197,236,196,4,177,182,86,132,147,37,244,28,150,43,144,72,24,20,202,117,
-  218,13,16,150,73,40,12,106,78,128,169,161,88,88,161,144,207,34,157,18,113,101,131,39,241,66,80,210,164,156,119,88,
-  211,27,67,198,45,10,181,10,45,221,105,38,78,158,101,115,75,130,214,247,92,223,58,247,159,71,246,174,133,61,53,24,
-  27,129,185,189,223,163,22,228,208,112,189,191,85,0,72,240,205,137,178,33,141,96,215,43,105,148,253,213,96,244,129,92,
-  0,121,4,44,31,34,239,1,227,253,52,98,133,47,92,39,214,32,223,77,31,34,151,131,84,160,115,166,140,251,82,38,
-  2,95,99,106,37,114,90,7,49,33,150,93,59,146,141,168,176,74,248,253,171,13,226,141,31,239,55,6,124,46,136,101,
-  194,211,251,246,96,222,55,135,255,26,45,18,191,177,80,82,22,244,22,44,187,115,171,171,151,173,192,59,18,192,88,96,
-  136,181,180,167,109,173,4,146,8,190,239,98,68,108,44,219,100,114,97,146,245,29,237,248,41,201,161,83,195,108,236,234,
-  197,162,68,169,236,82,117,52,245,42,172,233,105,227,238,187,174,227,239,62,251,69,214,12,117,147,27,89,96,169,84,66,
-  186,62,158,27,210,55,144,225,166,171,247,144,205,213,120,250,216,48,91,182,36,121,239,59,118,33,148,197,3,143,156,96,
-  102,106,137,24,54,43,139,33,209,222,237,24,9,69,80,145,232,48,192,208,14,51,195,141,54,207,74,91,72,71,34,173,
-  50,251,247,108,98,97,106,12,167,226,35,147,73,132,43,81,94,149,164,29,101,102,114,142,205,125,187,201,231,87,200,228,
-  86,136,25,38,90,249,56,1,84,29,15,179,34,136,26,130,138,227,208,209,223,141,31,56,104,173,240,253,144,66,161,72,
-  92,56,216,118,128,16,6,85,167,2,72,42,101,7,217,103,82,173,46,51,20,31,228,233,51,227,108,90,51,200,233,103,
-  159,99,205,174,203,121,114,99,223,134,129,243,115,7,34,112,44,7,185,58,56,177,239,97,174,14,243,230,46,187,161,94,
-  166,221,12,2,105,16,43,96,252,58,136,182,6,125,21,5,236,63,7,233,131,229,8,50,129,38,213,9,209,18,180,216,
-  208,33,26,101,181,69,243,154,34,124,209,244,180,37,196,107,96,9,80,218,87,41,80,45,102,195,159,35,45,112,208,250,
-  240,86,207,125,112,9,38,254,30,130,239,119,231,254,248,27,11,64,111,44,248,64,195,37,63,190,117,59,127,240,252,243,
-  156,207,46,189,234,93,15,170,192,31,129,56,8,137,58,244,120,97,16,27,10,229,168,15,167,36,132,117,73,138,76,204,
-  244,157,0,219,74,162,148,196,138,25,60,125,122,150,106,181,196,250,193,54,74,51,211,129,59,54,73,164,175,197,172,59,
-  121,132,53,128,227,135,204,78,101,121,231,59,175,35,26,79,226,155,9,182,110,106,103,206,181,176,45,3,223,146,4,58,
-  228,142,155,174,34,30,143,113,232,244,56,233,4,252,244,135,175,198,204,68,121,232,171,243,140,158,90,226,230,245,189,108,
-  218,210,199,200,82,158,175,125,250,51,56,239,184,145,253,251,215,32,178,14,245,108,141,137,169,105,100,50,129,17,212,49,
-  227,81,106,142,230,242,29,107,89,219,187,158,143,255,229,103,136,217,22,130,8,17,4,81,108,166,70,39,152,223,182,6,
-  43,26,101,250,220,8,109,49,147,88,196,98,178,94,161,221,183,136,56,6,21,17,48,187,148,195,76,164,72,196,37,10,
-  141,109,90,44,45,46,211,223,17,197,245,92,164,105,226,251,1,166,105,224,120,14,110,208,56,221,205,192,97,118,116,78,
-  205,44,150,197,157,7,7,197,194,232,4,246,149,187,83,203,231,231,118,117,195,182,16,206,253,41,56,135,248,206,235,70,
-  95,168,251,220,10,108,105,254,255,205,96,62,133,151,252,187,187,9,54,117,48,222,222,0,3,179,15,18,26,210,37,104,
-  233,129,136,130,88,8,93,10,218,234,13,16,137,26,154,238,0,58,60,136,185,208,186,212,232,194,107,215,65,42,137,148,
-  45,81,211,76,181,27,153,116,43,73,219,22,178,37,110,215,45,195,202,248,4,90,41,171,144,138,70,77,5,195,139,139,
-  166,58,57,86,221,83,175,216,105,173,142,181,193,212,171,69,154,152,63,172,224,115,193,6,245,108,155,223,236,235,101,83,
-  118,233,85,215,130,4,144,23,24,37,77,204,144,36,83,17,225,91,43,238,24,130,73,67,211,89,71,91,85,203,48,133,
-  150,40,45,48,12,240,74,94,232,77,158,87,221,91,215,90,132,161,190,245,196,72,184,211,136,232,115,182,52,116,61,46,
-  92,71,83,171,6,4,129,203,219,111,191,150,19,203,57,58,187,50,244,245,174,97,75,79,63,253,201,144,163,243,37,6,
-  54,15,178,177,183,13,87,216,76,45,151,249,185,15,221,202,96,91,43,15,30,26,102,106,118,146,159,124,255,21,12,118,
-  39,0,159,237,7,215,114,71,165,204,67,135,206,240,130,146,236,31,138,48,159,203,147,243,125,44,171,149,208,119,49,35,
-  30,137,68,156,253,59,215,178,178,88,162,119,176,143,201,98,153,122,221,167,63,29,37,34,76,74,181,42,103,71,167,25,
-  234,76,178,48,62,67,172,173,133,68,34,138,167,53,133,82,141,132,29,161,46,52,232,40,217,197,60,201,161,118,10,43,
-  43,152,194,160,82,170,19,25,108,167,92,173,16,248,1,82,8,132,144,4,74,162,13,19,97,219,148,124,135,43,194,186,
-  119,246,159,159,50,102,7,127,204,138,71,193,78,217,204,36,173,129,158,138,191,222,134,150,20,100,243,160,163,223,169,163,
-  178,201,121,116,189,193,128,115,1,116,90,26,38,145,184,12,100,103,195,237,16,217,12,81,175,209,164,50,19,133,120,0,
-  45,1,244,3,253,17,232,213,144,240,33,234,67,187,111,209,90,4,171,22,137,70,172,206,174,132,234,104,137,120,221,153,
-  136,234,107,183,253,100,218,112,181,166,181,107,128,150,206,62,58,122,162,100,50,17,146,241,56,81,163,5,105,36,209,70,
-  17,219,174,97,153,41,86,170,113,142,63,119,26,249,169,79,234,76,224,23,98,176,160,161,178,8,218,121,149,8,123,243,
-  135,22,124,154,98,40,197,244,129,203,24,56,125,154,136,239,191,234,9,170,105,67,26,62,196,170,40,35,29,181,23,12,
-  237,159,83,66,47,40,65,107,104,72,225,106,139,184,97,97,154,6,158,82,12,30,59,18,252,70,165,194,61,118,82,119,
-  158,154,100,99,37,176,166,90,147,74,70,219,132,81,215,40,63,164,86,170,177,125,91,55,109,3,41,138,217,113,166,38,
-  167,184,245,230,247,112,253,190,62,22,31,253,18,207,157,112,56,120,112,7,17,91,242,252,212,50,201,100,140,237,123,182,
-  114,98,116,146,104,107,47,107,247,71,248,203,7,79,50,50,159,67,71,12,58,91,219,185,253,242,157,220,118,211,110,142,
-  29,158,225,72,49,65,217,247,177,12,19,35,16,32,21,53,167,206,186,161,94,122,58,51,140,158,155,64,218,38,137,76,
-  11,139,217,89,54,247,15,161,180,34,17,77,50,57,189,200,250,190,118,68,168,41,230,202,68,35,22,210,128,82,205,163,
-  43,16,184,82,35,164,77,110,185,76,119,103,10,161,37,82,107,170,85,15,207,83,212,106,53,84,24,98,154,38,94,16,
-  18,4,10,164,69,44,153,196,215,96,91,166,184,9,170,71,254,238,254,136,252,229,59,98,209,164,36,155,238,75,233,202,
-  228,128,7,29,59,97,124,59,248,198,119,184,241,101,147,47,249,223,188,49,1,136,94,227,190,114,8,100,9,204,86,136,
-  104,72,26,208,26,64,135,134,30,7,58,13,232,50,161,39,132,148,215,40,102,215,225,24,180,228,108,145,172,181,182,89,
-  254,154,193,104,106,219,58,187,107,235,70,57,180,109,155,76,116,117,138,104,212,198,52,20,49,91,99,104,141,22,105,36,
-  33,82,7,168,48,68,249,17,194,48,66,72,13,109,186,248,9,7,233,183,177,48,86,228,236,51,95,231,133,127,190,63,
-  232,62,125,50,123,64,135,103,227,240,180,47,196,67,62,204,174,211,58,216,218,164,23,222,98,0,244,230,2,31,104,148,
-  231,40,119,118,17,219,190,131,125,199,142,190,234,215,31,214,90,204,106,148,138,26,37,85,113,79,43,244,9,132,88,17,
-  154,80,41,45,221,58,196,132,198,140,69,113,206,142,168,173,243,203,86,214,142,74,237,150,217,183,152,45,187,160,118,106,
-  109,158,206,150,68,89,229,13,39,180,240,117,157,253,91,118,147,232,72,227,10,143,227,71,207,49,113,126,148,31,185,177,
-  155,66,220,160,187,183,147,205,125,237,132,150,199,240,216,12,87,238,221,78,61,168,208,211,222,205,174,246,36,247,124,227,
-  81,254,249,171,207,51,91,174,33,12,31,17,154,124,234,179,143,241,238,183,93,197,31,254,219,219,153,152,56,199,83,15,
-  204,16,77,117,224,133,11,36,4,84,20,236,191,108,23,158,231,83,174,186,132,134,65,224,185,116,180,36,169,86,242,100,
-  58,90,49,132,201,217,19,231,216,177,161,151,104,60,73,53,95,194,72,68,73,197,108,242,185,18,43,249,42,177,206,24,
-  134,101,81,47,7,20,11,14,182,105,99,89,30,85,87,129,54,240,61,15,161,12,208,16,134,10,195,176,168,212,125,218,
-  210,9,132,72,50,231,4,34,3,229,253,185,229,250,125,159,255,134,61,112,215,141,70,216,151,140,150,231,232,137,67,71,
-  180,145,31,230,231,1,227,95,1,31,1,252,20,175,95,239,121,113,137,217,87,5,174,3,115,25,98,45,208,38,160,189,
-  21,90,52,116,27,13,237,102,64,65,191,7,93,158,160,213,137,154,73,63,30,139,217,93,157,102,56,212,111,235,161,129,
-  72,106,235,6,99,239,222,93,70,219,192,26,209,218,217,73,38,149,66,16,82,175,22,168,101,243,248,117,151,208,87,4,
-  21,141,175,5,210,168,32,209,104,165,64,40,180,89,193,76,152,8,43,67,49,43,41,63,242,156,90,186,255,1,167,242,
-  212,211,133,200,114,121,225,54,152,141,194,233,80,136,163,14,156,10,17,83,81,173,202,31,3,45,94,5,13,232,200,235,
-  11,64,111,62,240,185,168,5,133,33,79,31,56,192,142,211,167,176,95,229,252,176,64,19,134,144,53,148,126,94,6,202,
-  214,48,44,108,163,38,125,5,90,9,29,8,76,219,162,92,171,211,123,98,76,183,131,113,175,101,177,117,122,182,208,94,
-  241,206,250,16,169,71,229,134,211,99,19,178,187,59,70,190,110,99,38,51,12,244,102,48,204,56,185,149,2,29,107,44,
-  54,175,143,66,105,145,186,97,51,56,56,64,75,20,42,174,102,247,182,45,12,118,166,17,212,104,137,181,162,29,201,173,
-  183,222,196,143,158,26,99,105,161,78,58,157,161,230,154,156,158,153,226,51,15,63,201,137,209,113,254,228,255,252,32,142,
-  243,2,174,204,16,143,165,8,209,8,2,34,150,162,86,15,89,90,174,163,181,160,94,172,209,221,154,32,102,42,106,190,
-  131,23,64,91,60,69,97,165,198,218,222,78,22,23,115,68,45,131,164,13,181,88,138,124,169,78,91,166,14,18,66,5,
-  165,98,64,79,87,138,48,200,17,120,62,165,146,131,82,26,165,154,155,68,107,180,214,184,190,135,29,77,81,11,193,43,
-  23,116,4,194,8,136,202,100,62,124,230,208,113,99,109,187,37,11,130,84,139,38,238,129,241,88,195,243,248,109,23,183,
-  4,114,192,187,128,206,215,208,13,78,243,250,57,16,251,192,168,64,36,128,216,97,65,194,214,180,133,176,198,128,109,192,
-  122,23,58,106,208,153,53,232,46,116,180,182,6,3,125,241,204,150,13,102,199,230,33,177,249,178,125,180,111,218,104,196,
-  58,219,101,178,165,21,75,36,46,49,222,52,42,112,169,45,173,80,45,228,241,106,53,36,33,162,153,24,174,12,133,22,
-  2,67,75,2,237,35,34,18,43,222,142,83,55,89,58,57,198,202,163,247,184,197,123,238,91,142,140,78,207,38,52,227,
-  25,56,175,96,220,145,114,174,36,141,25,59,12,103,187,181,42,181,161,131,231,128,9,160,244,214,226,128,222,188,224,115,
-  129,140,206,117,118,243,55,111,123,59,153,175,222,131,241,42,46,194,37,165,131,107,97,161,210,150,122,82,85,92,218,12,
-  149,117,163,134,233,59,1,50,98,72,211,48,16,150,141,51,126,92,111,41,86,228,162,132,32,226,132,151,175,168,115,46,
-  188,32,97,104,74,249,155,111,247,139,65,49,140,235,101,207,176,167,23,23,200,180,40,158,126,230,52,165,37,201,7,126,
-  242,110,90,35,37,194,48,141,76,117,176,101,83,59,200,0,203,136,176,177,63,133,214,117,164,178,209,198,50,129,17,98,
-  248,109,252,252,135,63,74,217,180,241,204,58,170,170,57,117,116,130,209,145,191,224,200,68,150,31,253,157,79,240,222,253,
-  235,137,214,86,168,233,126,180,21,98,72,77,181,144,195,21,105,170,149,144,106,197,37,42,108,18,134,32,22,139,80,168,
-  87,113,61,69,79,107,7,249,124,153,174,206,118,132,101,225,185,30,49,91,34,165,65,165,90,160,86,47,17,139,199,177,
-  172,8,181,170,143,235,132,160,53,174,227,82,173,214,137,217,18,199,11,209,65,136,144,2,75,218,4,94,128,17,161,145,
-  152,187,82,213,113,136,78,65,196,221,188,217,76,34,88,10,20,197,88,44,161,107,245,88,25,172,143,55,72,219,111,239,
-  85,122,223,32,191,117,52,79,251,104,229,85,39,158,85,211,180,138,131,60,7,70,8,209,168,20,45,10,186,18,74,247,
-  74,232,69,211,229,193,128,7,67,43,146,161,74,79,107,135,222,183,59,97,239,217,26,233,216,185,93,30,216,178,141,158,
-  53,253,116,180,247,33,69,180,121,213,0,8,209,58,68,43,7,80,132,126,136,83,174,80,206,47,35,188,34,190,82,96,
-  74,66,105,128,2,83,128,169,65,10,141,101,219,120,86,154,249,165,50,163,95,254,186,170,220,123,111,53,250,252,241,92,
-  186,86,63,223,213,104,254,122,74,193,152,50,228,156,134,124,183,210,213,245,97,224,58,90,171,58,47,150,169,125,85,247,
-  222,155,20,124,36,223,75,61,112,161,249,94,227,9,109,207,101,97,243,22,254,219,232,40,163,103,207,188,154,3,172,127,
-  10,106,63,23,132,211,194,13,245,147,155,219,212,238,146,151,150,5,87,4,166,33,61,51,66,197,149,172,29,158,162,29,
-  56,19,143,178,207,241,231,227,65,120,216,131,49,23,6,214,87,235,42,81,15,245,189,107,135,228,201,241,37,242,11,211,
-  74,162,228,23,255,225,94,238,121,226,24,29,109,30,239,253,237,95,2,87,178,166,205,192,146,146,208,215,152,26,148,14,
-  240,132,129,52,53,132,38,10,65,96,215,233,222,121,128,132,175,41,101,115,56,134,207,76,253,36,50,97,208,167,109,150,
-  202,43,124,237,148,207,71,175,184,130,201,229,89,98,233,20,113,13,201,214,4,139,229,10,181,64,17,56,117,162,113,131,
-  192,52,145,70,4,215,171,18,132,154,76,50,65,189,86,103,114,114,154,214,100,138,74,190,136,33,162,216,162,74,93,88,
-  44,149,20,27,226,6,66,249,20,234,14,29,42,13,150,69,32,45,10,133,26,209,142,40,158,14,240,180,137,36,64,154,
-  10,39,80,180,119,246,82,158,93,193,46,84,132,134,212,177,238,54,59,181,166,93,82,243,88,200,57,132,158,27,54,220,
-  10,255,250,98,211,29,49,42,31,222,192,232,137,195,175,10,216,248,77,18,59,11,98,47,24,181,70,220,87,42,2,109,
-  115,130,22,67,138,238,152,102,67,160,244,134,26,172,45,27,244,122,93,45,45,198,150,237,233,250,250,181,137,158,235,175,
-  180,46,191,250,160,232,29,218,68,220,74,54,129,198,107,130,141,131,82,30,34,84,8,165,192,48,17,134,129,10,60,74,
-  133,101,156,82,25,28,15,67,105,124,35,64,10,11,48,16,10,4,10,211,16,88,241,24,165,186,203,243,39,198,152,249,
-  198,131,97,237,222,135,138,61,83,75,115,107,97,202,128,147,2,94,240,225,84,8,243,62,84,66,165,188,130,70,175,208,
-  72,182,211,52,226,217,94,147,195,255,77,170,148,124,215,38,185,105,104,202,117,131,39,207,165,190,169,12,235,119,180,144,
-  180,38,33,37,31,187,235,29,124,74,41,142,15,159,123,117,76,48,224,48,232,15,47,85,131,54,224,137,152,197,254,177,
-  162,246,5,58,162,66,165,13,137,51,59,205,166,21,175,230,131,231,137,192,90,231,233,211,30,60,35,165,88,118,37,107,
-  211,62,103,133,98,192,175,185,93,29,53,167,124,176,37,145,237,50,195,181,119,175,247,141,53,102,63,67,91,214,179,182,
-  51,133,242,67,82,182,137,144,146,80,11,132,48,80,65,136,41,52,104,208,194,64,104,133,22,81,140,84,11,45,166,69,
-  75,166,135,170,91,226,252,223,252,21,3,235,123,200,46,45,162,86,66,114,75,14,247,157,26,230,198,61,131,12,15,143,
-  113,249,213,7,233,239,239,226,137,135,14,81,114,5,158,103,210,154,138,18,137,74,202,78,185,65,88,155,38,166,109,224,
-  148,170,96,4,164,162,49,180,144,13,198,32,240,137,167,146,148,234,101,92,47,196,48,36,65,16,82,173,59,8,36,65,
-  8,94,160,48,140,8,16,160,53,24,166,129,86,30,160,72,180,164,8,135,71,117,199,114,77,30,49,236,232,226,142,205,
-  70,151,225,224,36,35,172,61,114,162,180,51,84,199,125,152,78,130,243,175,45,106,239,238,181,232,206,24,120,234,123,6,
-  157,11,191,195,134,185,103,196,32,102,65,70,64,103,18,6,12,88,167,96,157,214,116,215,66,221,145,179,228,96,126,235,
-  186,238,244,181,87,166,214,221,118,139,53,180,255,0,109,125,253,36,162,9,26,141,159,125,52,146,80,151,26,166,19,205,
-  138,16,186,81,23,66,27,141,240,10,25,132,56,203,57,156,124,145,192,245,208,194,71,9,159,64,42,108,47,5,34,68,
-  154,130,72,52,129,39,108,166,39,23,56,253,232,99,122,241,190,251,234,177,19,199,178,61,117,127,178,3,206,1,195,72,
-  70,208,98,212,215,122,78,64,201,129,224,113,224,61,26,166,121,125,58,130,252,64,0,144,105,104,170,174,228,211,79,117,
-  50,151,183,137,88,223,219,208,133,74,97,72,201,143,223,253,78,254,241,171,247,112,244,85,210,132,4,141,72,219,94,224,
-  231,78,47,19,184,65,16,66,45,229,134,165,104,189,70,218,93,10,218,20,35,161,41,103,91,133,142,24,129,122,18,33,
-  142,99,136,101,195,70,201,90,184,16,129,219,163,179,227,173,183,153,134,185,46,214,18,141,218,49,125,112,255,70,54,236,
-  18,100,29,133,19,4,216,34,138,208,32,48,27,105,47,82,162,9,137,232,16,95,25,104,17,34,9,49,100,43,161,142,
-  162,133,194,138,90,204,44,207,209,179,166,133,236,82,1,35,19,161,51,235,50,59,186,204,211,19,83,116,182,152,116,182,
-  182,18,105,137,96,104,193,244,228,34,133,90,136,29,137,147,180,45,132,17,176,92,171,209,22,111,193,182,77,74,181,50,
-  150,105,18,145,38,217,165,28,137,100,26,195,240,240,125,31,76,80,65,72,177,82,35,22,75,130,10,240,92,31,59,26,
-  37,240,67,10,165,42,126,79,11,90,52,180,102,67,10,124,191,17,174,175,77,129,187,88,81,158,130,83,219,55,25,109,
-  221,157,72,81,161,245,169,99,229,107,151,139,15,75,205,215,93,56,187,5,234,223,174,52,135,247,158,117,184,63,181,5,
-  81,169,126,207,139,50,13,226,86,48,218,32,226,65,42,215,160,121,134,162,176,41,132,141,14,12,229,161,223,237,104,235,
-  52,246,239,79,198,47,219,27,93,119,195,126,235,250,253,91,232,239,24,68,98,161,81,132,40,66,85,67,104,1,72,132,
-  12,154,247,145,23,217,29,33,77,36,2,237,123,132,133,2,197,210,50,190,83,193,8,105,104,59,161,64,10,19,33,21,
-  102,74,19,216,105,178,217,42,231,159,124,150,153,175,223,235,170,39,159,46,116,46,21,103,246,193,168,13,231,20,156,85,
-  112,62,148,204,213,52,133,184,214,78,11,4,9,96,180,73,142,27,188,126,57,113,111,249,212,151,11,224,243,143,223,39,
-  248,188,28,132,62,252,142,187,1,94,21,16,18,77,210,238,44,80,116,2,98,16,68,32,23,13,89,138,230,178,193,90,
-  51,168,200,144,231,107,113,158,73,36,76,95,149,194,147,113,173,39,59,4,229,41,41,29,52,241,16,174,75,73,33,54,
-  184,42,26,109,209,162,98,184,242,175,254,247,33,14,94,115,53,7,111,222,77,170,173,23,93,175,67,165,136,212,10,80,
-  4,42,68,72,141,8,0,105,130,112,145,42,64,34,8,13,19,45,192,194,199,17,37,60,25,80,175,251,152,50,78,36,
-  19,101,253,174,24,213,179,30,143,156,154,225,221,215,238,102,231,154,126,142,29,27,103,169,172,8,84,149,116,84,17,141,
-  71,88,40,214,49,68,2,91,154,164,19,113,150,86,150,72,71,99,72,37,41,22,74,212,3,72,91,54,182,33,113,195,
-  0,41,37,139,133,50,131,177,52,182,105,82,42,215,72,201,4,142,227,99,91,49,234,174,143,235,249,104,4,90,43,76,
-  41,48,165,198,64,112,104,50,43,135,147,105,177,123,87,15,126,80,163,235,208,169,194,129,201,149,135,77,248,188,7,143,
-  9,88,252,57,80,83,175,176,176,47,144,194,159,122,247,58,182,3,206,119,145,72,16,208,168,146,40,64,60,3,137,46,
-  104,87,208,173,161,39,10,107,21,108,168,194,166,9,201,250,234,250,53,93,173,55,220,156,28,186,253,22,123,224,224,54,
-  186,6,250,137,26,177,198,167,117,136,14,61,180,172,163,209,13,62,69,112,17,128,16,2,113,9,101,33,53,72,29,224,
-  215,29,138,185,37,130,74,30,194,56,182,106,107,104,77,162,138,145,180,9,173,12,149,130,207,216,115,231,89,122,232,33,
-  183,250,192,131,249,216,212,220,194,122,173,231,18,48,34,224,76,0,195,10,166,21,228,138,80,233,85,248,109,155,218,208,
-  107,91,201,156,88,196,91,172,124,91,208,17,151,104,124,171,0,244,26,129,207,165,32,4,188,106,32,244,203,192,7,47,
-  57,85,206,65,48,39,200,107,197,66,87,33,91,233,180,236,101,31,206,200,152,124,50,210,106,231,163,243,110,126,113,119,
-  135,255,111,71,11,234,63,7,218,5,12,101,202,248,160,8,141,116,45,192,137,39,164,83,13,196,154,238,30,222,113,227,
-  94,84,34,137,236,221,140,118,43,184,99,103,208,78,1,105,134,8,161,64,75,2,109,160,81,104,29,66,24,130,159,195,
-  84,30,138,4,161,174,179,152,159,229,217,167,143,147,157,112,9,253,16,105,91,236,220,63,200,14,175,151,199,14,157,103,
-  114,165,198,117,251,47,227,11,95,251,26,11,101,72,89,38,173,73,131,188,83,161,92,9,105,77,165,176,76,240,156,26,
-  82,74,52,130,114,217,37,87,172,19,213,146,88,139,77,60,158,160,86,113,136,68,44,170,181,128,186,235,97,1,126,168,
-  113,253,16,203,182,9,148,64,55,141,15,21,42,164,173,209,66,19,49,36,65,224,51,177,52,45,250,111,56,32,156,154,
-  71,234,169,163,197,131,185,242,55,20,124,78,193,227,54,44,236,132,96,188,49,190,223,82,220,122,128,252,46,58,138,57,
-  64,1,140,27,32,42,161,189,14,91,76,56,32,97,83,21,250,114,208,95,237,77,119,26,119,222,210,214,255,238,119,219,
-  187,14,238,166,167,187,239,197,130,122,90,65,232,190,168,210,75,129,198,110,150,44,22,141,121,145,205,26,50,58,104,2,
-  147,198,64,163,3,159,90,62,71,181,144,39,244,61,148,148,88,113,15,195,20,40,146,84,43,41,150,94,56,173,86,238,
-  255,107,103,249,137,103,202,242,252,84,182,37,8,167,6,224,172,132,17,13,83,33,76,187,176,216,1,165,4,184,213,102,
-  16,225,105,224,39,223,191,3,239,195,123,80,227,121,156,223,127,148,241,103,102,144,188,114,63,52,221,32,213,169,1,87,
-  208,104,91,109,3,191,14,148,127,24,1,232,181,2,159,151,131,208,7,222,113,55,119,172,228,48,151,150,190,39,155,56,
-  108,14,242,151,154,150,126,243,52,209,221,154,98,0,103,215,19,30,201,132,65,46,132,225,120,38,154,45,38,35,229,167,
-  41,187,31,61,216,205,246,243,5,162,245,144,58,40,165,116,208,231,135,174,4,229,24,22,237,153,148,216,183,206,198,155,
-  29,65,119,15,96,244,121,205,218,209,141,133,44,66,191,17,243,97,68,80,134,36,212,33,66,107,164,214,232,242,28,42,
-  55,134,213,127,128,197,149,2,133,98,145,160,102,224,86,61,28,63,143,233,38,41,45,151,233,27,104,99,123,161,151,115,
-  195,19,60,119,252,28,75,43,57,20,6,61,233,54,12,83,242,194,201,83,172,31,88,135,109,248,24,182,65,173,236,17,
-  139,197,240,67,152,203,21,241,180,69,80,13,40,152,117,44,59,130,161,170,152,210,66,152,17,60,165,176,100,35,70,193,
-  241,21,150,29,65,171,11,44,178,64,235,16,83,154,212,252,144,68,50,70,185,232,48,212,191,14,119,121,153,190,67,71,
-  151,175,170,235,71,52,124,46,3,143,231,96,225,73,8,175,249,22,188,197,26,26,45,150,87,190,67,126,71,54,41,224,
-  69,48,92,136,71,160,91,194,70,9,59,44,216,191,4,187,178,157,173,61,246,45,55,166,215,190,251,174,200,198,107,175,
-  164,191,127,8,3,7,133,36,84,1,154,0,83,6,77,156,177,65,11,2,195,69,96,33,154,126,86,173,21,66,132,205,
-  140,44,141,198,70,160,144,58,36,112,234,212,242,121,220,90,5,75,8,146,169,52,117,223,101,100,58,203,249,163,167,117,
-  238,240,11,202,59,114,156,158,201,217,122,79,213,153,26,128,51,18,134,53,156,15,13,57,42,18,246,12,117,63,231,250,
-  97,61,222,116,159,141,2,22,80,4,6,219,162,176,191,135,112,190,140,217,147,36,104,143,243,56,141,250,218,127,13,220,
-  8,204,3,159,166,17,41,30,3,102,129,25,224,87,155,224,35,224,251,246,24,155,111,85,240,169,56,146,79,63,253,218,
-  128,207,75,64,200,48,88,115,227,77,188,251,139,95,32,230,121,223,181,203,246,2,255,19,52,255,45,27,155,65,143,192,
-  138,13,79,164,82,166,111,133,70,217,135,227,53,105,84,122,126,108,155,90,99,12,163,86,28,222,237,134,20,32,136,194,
-  34,154,163,49,211,138,104,188,158,146,231,244,137,104,210,238,234,74,82,169,46,99,85,210,88,179,35,4,218,67,121,69,
-  164,169,209,161,64,138,6,151,16,202,16,169,12,164,150,168,208,199,93,94,164,92,122,148,129,206,61,124,227,235,207,50,
-  188,120,158,254,158,22,70,14,159,193,78,70,113,189,42,239,184,227,61,108,220,220,13,127,253,87,220,55,58,205,231,30,
-  126,154,181,45,22,25,225,145,73,165,56,61,179,130,167,45,226,49,11,59,34,40,148,203,88,42,130,64,80,40,87,8,
-  35,81,220,154,130,138,199,162,95,96,77,127,111,147,12,215,72,51,66,185,82,37,145,137,226,169,0,169,163,40,165,9,
-  252,0,167,238,160,1,211,50,26,174,99,203,196,178,77,102,167,230,88,58,62,172,110,28,159,154,217,21,232,39,92,248,
-  178,5,143,215,97,241,167,32,188,254,91,140,127,23,240,251,192,31,127,7,0,228,0,237,32,111,4,99,14,98,2,186,
-  227,176,197,130,125,62,236,27,131,77,51,189,237,125,153,15,190,51,115,195,207,127,212,216,186,125,23,70,83,95,80,65,
-  0,74,54,10,226,153,54,10,137,175,13,164,110,120,35,5,96,104,11,180,68,160,154,161,124,32,154,142,109,45,20,129,
-  14,49,5,248,213,42,94,169,220,56,44,148,193,233,177,9,61,252,194,201,202,200,195,207,80,123,230,233,200,186,170,195,
-  0,200,76,163,118,40,2,124,13,117,5,66,66,135,161,180,208,37,39,161,160,104,129,31,64,181,8,197,86,168,215,165,
-  168,215,162,150,155,142,219,33,125,113,162,90,32,164,226,191,41,197,127,190,100,204,126,189,73,25,156,164,81,35,91,54,
-  249,33,221,244,250,25,175,2,248,188,37,1,232,34,225,252,116,39,179,121,155,168,245,218,114,245,194,247,89,90,191,129,
-  71,222,243,94,222,255,133,207,97,122,223,127,160,226,116,99,114,235,183,69,140,243,149,76,36,107,44,57,254,12,148,31,
-  245,116,240,95,119,119,232,61,55,244,178,248,27,79,179,59,212,76,54,22,208,121,91,235,207,163,25,5,174,15,131,242,
-  109,245,96,62,190,41,99,81,171,135,24,122,5,51,63,140,162,225,110,87,194,66,9,19,161,5,82,11,180,240,17,218,
-  68,8,65,173,86,197,114,3,130,202,12,71,31,125,132,103,158,62,195,228,226,89,174,189,102,39,243,163,179,44,44,58,
-  184,70,141,88,58,193,237,119,126,152,193,182,36,167,206,255,22,95,123,246,40,31,190,114,63,189,182,196,48,235,200,116,
-  146,142,238,54,108,83,19,6,18,47,52,136,199,108,10,217,28,190,180,193,142,35,92,31,25,72,156,176,74,177,90,38,
-  147,140,83,116,92,76,59,78,88,115,80,97,128,52,76,234,78,195,68,137,68,34,56,110,13,105,68,16,194,0,67,99,
-  219,54,42,12,56,117,126,146,53,35,147,185,125,240,100,21,190,160,225,73,15,150,254,13,132,179,205,77,242,74,114,35,
-  112,217,37,218,231,43,29,16,186,249,249,24,68,90,27,7,126,79,0,107,34,176,211,135,253,39,96,199,200,64,79,223,
-  192,143,191,47,254,158,143,252,132,216,186,101,55,160,80,65,133,80,7,200,166,9,21,24,33,146,8,50,144,72,84,67,
-  213,69,19,52,1,72,42,137,144,52,52,30,169,80,104,20,18,133,1,72,44,1,213,252,10,203,179,179,172,100,151,56,
-  119,118,148,211,195,227,12,143,78,213,219,202,245,177,141,90,119,237,187,246,42,203,72,197,140,138,97,136,98,224,235,48,
-  244,162,241,233,169,181,153,66,61,93,23,166,23,56,245,48,146,205,151,109,215,95,1,42,17,168,75,200,9,88,12,96,
-  209,132,145,13,112,54,168,122,43,39,191,58,170,73,88,252,247,167,166,249,167,135,39,94,98,110,5,151,104,147,226,18,
-  109,231,213,38,167,205,183,34,248,252,227,83,175,15,248,92,16,203,113,56,53,180,1,235,3,239,228,125,99,15,17,126,
-  31,173,240,140,230,98,223,234,43,189,231,150,1,103,204,85,110,252,217,69,253,216,92,153,251,128,63,245,5,110,41,160,
-  227,182,53,24,15,76,243,44,40,3,150,251,160,232,151,189,37,13,45,201,124,241,64,97,50,223,174,135,6,8,98,53,
-  100,52,131,54,35,40,229,129,110,122,80,154,43,37,208,26,17,154,52,104,93,3,59,146,68,219,53,122,12,77,245,252,
-  189,204,62,241,53,198,202,6,27,182,237,230,142,159,186,129,92,161,64,164,213,192,110,171,224,215,231,217,177,115,31,215,
-  238,189,10,127,41,135,44,251,120,105,151,157,123,55,49,249,213,51,244,39,210,8,83,80,173,215,73,71,226,44,101,107,
-  212,125,3,43,30,37,112,93,16,62,174,112,49,66,73,110,197,193,232,76,128,168,147,208,30,85,101,82,114,5,118,84,
-  129,23,16,134,62,177,72,28,223,53,209,166,66,168,0,180,66,74,3,97,180,147,61,123,154,235,97,198,133,167,52,60,
-  187,17,22,127,20,212,201,239,194,109,14,32,163,38,36,44,4,166,64,52,74,227,185,192,14,136,182,195,218,0,174,181,
-  224,114,9,67,71,97,232,204,186,129,193,117,31,249,96,236,23,126,242,67,108,24,218,222,216,154,170,4,90,32,37,104,
-  109,162,164,70,8,129,208,2,161,53,200,176,89,112,71,160,181,70,135,10,132,64,27,18,33,100,67,79,17,23,66,221,
-  4,66,55,12,191,153,185,5,22,230,231,177,165,36,217,219,207,149,107,214,115,203,123,83,36,146,201,184,29,139,239,209,
-  134,13,72,12,36,47,50,72,46,245,114,177,197,171,214,90,10,117,23,191,82,68,45,206,18,22,115,202,169,213,241,207,
-  77,121,198,200,100,85,79,206,151,39,78,141,228,218,43,149,175,217,53,111,101,33,30,47,236,94,219,17,78,254,221,243,
-  252,237,51,211,111,88,33,254,183,12,0,73,169,169,56,6,255,244,116,199,235,10,62,23,36,230,212,57,219,191,158,23,
-  222,21,103,207,224,10,142,255,189,15,93,10,184,29,112,37,152,83,85,109,28,93,126,105,43,24,5,65,103,12,131,139,
-  33,105,234,65,112,175,145,98,73,134,122,36,153,11,102,103,78,206,175,223,179,101,139,97,184,14,66,216,104,105,54,106,
-  249,43,208,170,233,71,17,128,144,104,45,176,8,27,175,197,146,208,35,80,78,133,117,25,205,239,255,230,79,243,223,63,
-  241,0,95,254,155,47,208,189,126,45,45,237,25,122,58,225,206,159,234,32,48,210,140,29,127,134,31,185,170,157,167,14,
-  173,176,84,14,249,233,219,110,66,21,171,104,39,32,154,49,240,202,37,226,182,73,169,90,163,84,171,34,236,8,126,189,
-  78,24,134,168,208,199,50,13,132,18,184,158,71,161,84,35,21,181,9,125,15,41,44,252,80,96,95,168,146,41,26,62,
-  22,199,181,65,123,196,164,66,40,27,33,5,149,208,35,117,126,182,218,11,163,46,156,201,64,182,23,212,210,165,135,68,
-  243,247,191,118,52,84,158,93,196,88,168,49,125,182,160,83,19,53,158,7,43,14,173,29,176,14,184,70,194,109,163,176,
-  251,112,103,111,91,219,135,223,17,253,165,255,227,167,216,178,110,91,99,187,171,202,139,228,178,80,104,125,1,232,27,222,
-  43,137,113,81,235,65,92,40,252,41,144,23,163,99,197,37,241,61,141,185,185,168,81,104,77,107,75,43,61,221,61,205,
-  218,217,151,178,136,23,156,242,206,139,196,182,208,132,190,143,87,173,18,120,46,202,115,233,242,60,252,132,129,183,113,29,
-  138,77,50,97,69,176,238,136,68,199,231,115,209,111,252,243,231,218,35,217,201,68,119,133,152,6,217,107,74,230,239,59,
-  71,223,201,197,215,45,23,238,45,13,64,2,248,226,225,54,166,87,108,98,182,122,253,239,47,64,187,138,175,62,209,133,
-  216,235,179,99,160,72,16,126,111,10,169,110,146,156,134,37,209,190,250,166,217,215,52,194,231,1,54,55,189,12,247,1,
-  251,108,195,73,251,225,116,38,208,163,39,159,24,222,51,122,203,158,214,13,17,137,112,125,116,52,104,156,194,77,125,89,
-  8,217,44,89,39,80,8,164,14,145,40,28,105,160,34,73,100,196,198,8,3,54,182,69,249,179,223,253,8,95,253,198,
-  211,252,197,23,158,225,204,211,19,108,191,233,74,118,245,95,137,191,178,64,75,238,8,190,214,28,159,154,226,231,255,237,
-  47,176,53,99,240,197,47,61,65,213,138,96,248,14,9,45,176,60,159,217,66,141,64,130,170,151,137,70,19,248,42,36,
-  12,66,108,9,210,48,208,90,81,171,59,180,36,91,0,141,129,129,235,213,73,136,20,74,132,23,249,49,47,208,8,35,
-  36,105,106,162,86,156,72,92,82,172,102,105,203,22,139,2,198,108,152,93,6,239,31,128,247,210,136,208,213,77,222,226,
-  159,128,31,5,254,27,141,246,58,175,36,243,127,113,138,243,13,173,199,254,8,36,21,244,107,216,103,194,149,57,56,240,
-  92,38,186,185,252,182,235,91,126,254,119,255,31,118,108,219,117,209,35,39,116,136,16,47,166,96,94,244,226,11,209,124,
-  253,197,72,33,33,26,154,143,66,53,56,33,33,64,74,228,203,154,117,138,38,128,9,209,0,166,104,44,142,16,16,40,
-  191,241,94,193,37,224,3,82,27,160,20,129,23,80,43,22,168,151,75,104,223,107,120,205,164,164,174,53,194,87,68,34,
-  16,8,139,225,137,37,142,220,255,117,74,95,255,146,187,113,98,101,166,99,185,244,164,47,197,115,166,214,121,103,169,34,
-  70,191,112,134,4,112,249,37,252,100,0,180,93,98,154,138,75,158,64,252,48,3,144,210,130,170,39,177,140,55,174,107,
-  147,20,154,32,148,60,63,209,198,206,129,2,175,85,184,86,212,146,252,250,87,199,249,23,26,245,97,46,40,235,27,220,
-  208,191,75,50,231,193,153,158,137,229,153,195,143,30,107,25,186,243,42,33,170,69,140,116,10,161,26,17,221,90,92,136,
-  49,105,28,195,18,8,165,198,71,55,204,17,213,56,173,165,246,9,253,10,66,86,121,247,157,251,184,245,246,203,152,171,
-  175,161,84,5,127,101,154,72,249,56,9,237,240,201,167,207,179,239,134,155,216,217,223,78,121,110,154,179,217,60,42,106,
-  96,213,29,66,219,68,116,245,241,182,91,15,96,164,18,4,149,10,231,207,141,112,242,228,41,108,211,38,240,124,44,219,
-  38,150,136,19,6,30,149,154,75,50,150,64,185,1,74,131,235,42,180,18,132,126,8,177,0,95,5,68,13,3,33,52,
-  133,124,158,190,150,30,150,103,150,216,228,185,89,5,99,38,228,74,160,254,170,65,24,95,228,39,198,129,71,129,39,155,
-  174,226,87,138,229,105,3,214,129,156,109,100,157,175,51,96,155,13,187,43,112,224,217,72,100,187,250,192,29,157,55,124,
-  244,253,198,222,125,7,104,111,25,36,84,46,26,7,137,66,72,235,69,187,182,233,78,191,0,60,151,174,72,253,34,50,
-  33,164,184,228,35,226,21,15,213,11,181,81,181,166,193,9,105,133,209,220,246,141,178,133,205,164,92,21,226,59,37,220,
-  90,21,183,90,195,119,29,164,166,241,94,173,145,82,225,27,30,190,7,163,39,199,56,252,181,7,116,237,193,135,217,60,
-  189,236,109,119,152,83,232,99,190,33,206,34,113,3,45,6,129,150,13,232,218,10,184,127,4,161,120,233,183,99,17,194,
-  12,184,123,193,179,32,12,64,249,47,139,169,250,161,35,161,141,55,65,62,171,16,188,230,32,24,26,130,143,20,61,238,
-  184,100,146,21,208,171,180,174,41,86,2,33,142,116,184,122,199,210,61,71,187,135,247,108,236,218,220,219,134,84,33,88,
-  54,66,169,139,39,150,210,205,152,90,13,218,104,20,61,147,74,35,84,136,8,131,134,166,36,37,129,80,4,53,133,37,
-  124,54,12,118,34,186,119,146,127,254,211,104,97,242,213,19,101,162,235,54,242,209,95,253,21,152,59,193,76,161,140,87,
-  171,96,187,62,182,109,114,224,174,91,184,249,199,126,134,100,162,163,177,165,66,31,183,84,228,191,253,225,127,229,196,147,
-  135,73,70,163,32,124,148,104,84,223,241,125,240,35,26,69,64,168,66,156,186,79,52,102,33,165,137,105,90,13,86,68,
-  131,198,34,91,152,165,199,238,167,101,116,165,214,171,24,13,27,26,80,229,243,77,183,240,119,218,62,217,111,186,226,255,
-  75,35,182,175,67,192,65,75,112,139,167,217,55,97,152,67,11,183,222,216,187,235,223,124,36,114,227,109,183,17,179,227,
-  64,149,144,21,16,2,67,91,13,186,70,7,23,13,189,75,181,150,11,187,86,107,253,178,230,10,151,232,12,23,114,43,
-  46,1,41,33,154,237,203,47,124,166,169,237,8,33,64,105,132,214,104,215,39,112,61,188,106,21,223,119,80,65,13,165,
-  21,82,9,162,182,133,214,18,33,12,234,142,199,244,232,212,202,249,103,158,173,231,30,122,34,33,142,156,74,116,85,125,
-  99,176,57,148,181,198,50,234,36,212,251,129,109,2,28,1,69,9,5,11,106,10,92,1,74,188,148,120,118,4,228,226,
-  176,124,225,189,2,138,187,193,139,64,88,7,21,252,176,1,208,15,131,104,91,162,198,138,236,124,102,129,157,175,240,247,
-  28,212,206,74,206,248,33,15,175,159,173,245,157,251,242,179,215,247,254,252,237,49,103,114,140,193,205,59,81,58,64,43,
-  133,48,26,133,190,80,141,83,212,84,178,1,224,34,36,148,33,161,80,8,37,65,75,164,144,24,202,68,11,77,205,157,
-  33,230,117,147,220,112,5,207,191,112,138,106,172,206,199,126,249,231,177,109,131,122,60,67,49,48,192,243,177,162,54,183,
-  252,196,7,185,254,157,119,83,170,58,100,179,83,8,199,37,101,26,68,186,58,120,255,135,127,132,225,23,78,18,11,37,
-  117,63,192,48,13,148,1,90,11,66,165,9,181,7,66,82,171,55,178,186,209,18,207,13,208,134,194,16,18,165,45,234,
-  8,188,186,75,102,116,54,31,133,17,31,166,11,224,118,2,73,94,204,204,22,52,130,228,254,238,21,180,159,11,81,232,
-  191,14,70,7,116,20,225,74,27,222,181,160,185,102,118,247,214,190,173,255,207,111,37,223,119,247,123,72,68,108,80,121,
-  116,152,3,33,144,58,14,202,108,84,87,150,77,72,191,164,146,187,18,23,186,247,234,139,224,35,94,118,95,160,145,70,
-  113,129,43,186,128,53,66,188,168,246,104,221,12,74,108,2,91,216,32,166,67,207,163,144,93,194,171,214,136,72,3,80,
-  120,74,224,123,138,48,208,100,179,139,12,143,142,177,148,91,97,118,126,145,229,233,133,133,100,32,196,254,116,50,189,115,
-  239,118,77,44,174,114,235,7,68,189,61,99,25,129,209,237,213,85,71,44,212,202,46,231,67,170,101,229,85,92,191,90,
-  169,251,245,252,138,175,114,139,158,44,150,125,233,163,162,26,221,56,154,240,58,97,57,218,0,160,172,1,19,6,140,11,
-  200,234,134,133,155,141,67,97,63,132,23,28,43,122,21,128,222,250,98,8,65,169,30,242,84,197,127,69,3,79,129,78,
-  135,58,107,194,33,19,214,25,79,12,175,27,191,109,231,102,34,17,74,238,41,182,109,223,214,168,193,32,4,58,84,77,
-  30,186,97,138,53,56,135,176,201,75,152,141,214,209,161,192,8,65,201,198,194,143,155,105,130,68,18,17,239,96,243,110,
-  147,3,215,223,128,21,145,248,165,37,100,162,143,147,211,57,252,100,156,15,253,155,95,97,219,21,151,113,242,153,103,9,
-  150,39,57,55,50,130,244,32,25,143,178,249,242,253,244,110,222,75,223,64,55,213,241,5,194,80,225,41,143,132,25,197,
-  196,196,247,61,164,33,16,1,4,190,38,176,66,98,182,133,101,198,240,194,42,49,105,83,117,92,42,218,162,54,187,172,
-  251,242,249,121,224,188,132,229,35,160,206,54,53,160,75,221,232,15,210,136,83,121,185,172,7,210,141,154,201,253,21,56,
-  40,224,206,179,177,216,245,241,95,251,216,192,7,254,237,207,152,189,237,27,208,97,136,242,139,96,248,72,17,111,92,81,
-  104,180,104,128,164,86,118,195,172,21,193,69,176,160,105,46,93,170,25,139,139,252,144,190,200,241,160,117,243,35,47,155,
-  77,253,34,183,115,1,188,164,110,0,22,90,81,171,84,241,195,16,105,155,248,42,100,37,191,130,171,26,190,76,180,192,
-  72,217,108,219,187,157,3,233,20,109,157,93,100,90,90,183,155,182,73,16,58,72,195,215,150,180,69,163,40,137,5,40,
-  67,19,52,41,122,31,240,240,21,56,110,72,165,88,164,146,91,160,90,206,227,187,10,157,247,244,232,249,113,49,49,114,
-  142,147,199,143,121,198,153,243,181,158,98,173,218,129,206,103,96,33,10,75,22,76,73,120,54,132,103,255,35,44,29,132,
-  48,250,61,174,247,215,182,55,60,224,155,175,108,41,26,97,35,42,119,85,94,58,96,66,10,178,17,131,187,191,205,219,
-  254,12,130,43,4,51,30,28,238,172,232,125,39,191,114,120,240,237,191,253,177,88,109,121,153,51,103,207,178,105,203,38,
-  164,33,47,122,107,80,154,64,4,104,209,152,112,137,1,170,113,44,107,173,80,4,104,12,124,105,147,72,116,99,41,1,
-  190,75,71,199,26,148,89,37,12,29,172,84,148,231,30,61,198,11,231,103,249,149,223,252,117,182,238,185,154,165,241,73,
-  142,126,253,97,238,190,121,35,235,46,91,203,200,209,17,138,133,69,178,227,22,189,107,215,178,121,93,31,207,156,62,127,
-  209,235,92,119,234,196,205,24,50,116,73,183,37,41,4,30,166,105,96,26,18,203,50,65,90,136,64,99,73,197,210,242,
-  10,21,97,161,71,243,245,54,152,212,48,62,15,149,71,155,91,217,123,153,137,245,74,43,233,99,192,251,33,150,135,141,
-  6,220,92,135,91,206,174,25,56,184,247,47,255,168,231,250,183,223,10,202,71,5,117,144,117,148,233,131,136,53,202,180,
-  106,141,32,4,35,68,8,137,194,126,9,67,114,193,84,185,148,152,109,240,34,26,201,37,164,180,190,100,94,181,190,120,
-  137,11,36,117,131,211,126,209,12,187,152,34,34,4,241,116,146,88,50,70,168,124,164,212,116,172,29,64,88,241,134,163,
-  1,205,139,81,57,170,121,139,0,180,198,178,98,104,146,194,215,160,117,163,252,170,8,21,130,16,101,149,16,161,137,192,
-  192,68,145,136,106,82,177,22,68,79,95,19,156,2,180,46,136,203,220,27,81,126,137,165,229,37,123,124,100,218,158,56,
-  53,217,50,115,242,201,190,167,78,205,110,159,159,153,11,47,203,174,100,175,242,130,62,95,136,26,90,63,245,52,84,204,
-  239,129,19,186,250,181,4,160,6,248,152,104,241,202,196,77,96,10,204,64,173,130,80,115,172,12,192,52,37,248,138,223,
-  249,219,211,0,252,39,224,237,47,91,232,33,176,9,24,211,212,29,193,112,20,14,103,14,79,110,63,241,232,195,155,111,
-  189,245,38,150,28,147,233,249,60,107,6,50,8,25,34,60,139,128,128,80,106,164,54,208,1,152,90,96,8,133,47,2,
-  66,83,161,5,72,101,96,198,90,32,29,33,20,1,194,20,72,93,69,134,10,204,8,94,16,97,105,185,204,175,253,246,
-  255,197,208,230,54,116,101,30,63,63,131,95,25,103,236,120,129,84,170,131,45,155,182,50,54,55,66,169,94,34,162,53,
-  45,157,173,20,164,75,155,25,69,184,146,80,155,248,202,71,138,16,229,42,44,173,209,134,79,77,185,216,97,28,195,53,
-  208,194,2,17,103,108,101,12,153,233,192,158,95,40,70,97,44,132,57,19,188,193,151,141,71,4,248,228,43,152,94,31,
-  3,254,45,68,231,96,83,4,238,40,192,93,243,219,55,236,184,227,31,254,170,109,211,222,3,40,85,110,212,163,110,210,
-  169,66,73,180,16,141,252,57,104,166,138,54,204,167,70,113,29,129,18,242,165,4,242,37,218,80,35,133,228,165,38,21,
-  82,188,196,116,19,205,247,93,228,122,208,223,212,148,243,2,191,36,45,11,129,141,41,227,23,175,161,155,238,251,134,229,
-  118,137,215,243,130,141,215,44,183,34,132,110,130,84,67,147,19,6,141,239,170,26,190,46,37,53,161,50,26,26,93,104,
-  225,185,43,56,37,7,191,80,196,113,243,132,74,161,101,0,66,178,113,168,159,221,59,118,224,127,232,78,113,244,244,130,
-  248,252,23,191,36,191,241,207,159,235,219,148,203,109,237,210,97,183,130,72,47,84,206,164,187,56,157,238,194,250,46,58,
-  28,191,102,0,244,175,129,207,139,32,36,127,168,65,72,235,198,26,141,26,130,34,48,50,87,102,231,207,220,207,169,137,
-  70,193,203,141,52,90,198,116,188,194,68,41,80,207,106,22,44,120,161,187,194,222,209,175,30,233,155,218,190,33,217,223,
-  189,150,176,179,11,45,42,24,161,196,23,17,164,12,145,218,65,41,19,141,68,73,3,36,72,97,98,162,145,26,234,42,
-  70,164,109,29,218,72,17,18,109,20,118,11,125,192,68,135,54,74,187,188,237,157,183,99,10,141,90,153,99,250,201,71,
-  153,57,245,12,111,223,218,66,107,68,50,90,158,103,161,146,96,253,198,253,28,25,27,195,72,182,146,74,119,17,53,50,
-  248,218,197,51,66,2,45,64,248,16,106,44,71,18,145,113,194,208,165,90,211,84,188,16,171,37,192,54,5,185,170,207,
-  249,108,133,93,137,20,125,149,106,46,132,169,40,172,168,102,224,97,178,185,198,50,205,177,41,188,108,108,126,17,248,101,
-  136,102,97,115,12,238,152,129,119,46,222,124,213,238,187,255,231,31,37,215,110,217,76,232,151,144,82,54,188,84,13,134,
-  254,130,241,132,190,36,81,245,34,81,252,175,122,59,5,82,128,80,47,227,116,154,38,154,104,106,57,23,201,231,75,212,
-  163,23,95,19,40,173,26,102,156,108,0,141,210,186,17,30,213,52,238,68,211,101,166,185,96,214,93,2,110,23,59,125,
-  53,126,139,230,231,27,32,212,128,35,37,18,8,17,34,133,66,234,8,110,177,68,41,55,131,235,44,162,194,70,235,116,
-  221,76,71,141,68,50,8,105,48,59,151,227,216,225,167,56,250,196,115,228,15,31,14,183,100,151,10,183,132,225,84,43,
-  234,217,16,166,5,184,115,210,224,233,76,47,75,118,20,227,187,220,203,230,171,15,62,226,59,2,159,183,44,8,105,144,
-  82,96,197,76,172,240,251,44,80,105,75,44,41,248,171,165,58,127,12,224,134,23,193,135,75,52,158,11,114,164,233,74,
-  94,75,163,0,205,54,168,77,195,25,19,158,106,63,182,188,254,248,163,231,246,246,191,175,95,202,176,68,104,24,16,74,
-  180,89,65,134,38,82,69,81,210,71,75,8,80,205,69,220,168,19,44,125,144,169,86,204,206,126,132,180,48,0,77,72,
-  104,40,12,173,144,8,108,161,17,202,3,41,240,234,30,231,78,62,207,80,74,146,136,36,168,135,2,179,189,141,84,95,
-  39,83,243,179,172,219,119,57,164,251,41,151,42,196,81,132,36,208,162,134,208,149,198,169,110,219,84,106,5,236,84,26,
-  180,198,112,53,1,18,199,171,147,74,38,89,56,63,227,222,136,161,188,92,33,154,168,57,57,96,97,9,106,91,175,238,
-  230,137,159,218,2,213,0,210,22,249,71,231,248,155,191,31,249,38,205,231,23,4,177,5,205,198,40,220,62,13,119,79,
-  255,216,123,15,252,212,255,250,147,72,71,50,142,242,29,164,97,18,170,0,169,5,82,24,232,70,78,250,75,72,226,11,
-  224,163,255,213,67,228,82,80,225,18,142,232,69,13,233,155,61,100,47,242,68,23,249,34,241,210,251,138,151,93,227,69,
-  222,232,165,69,244,245,75,72,111,31,173,26,209,214,186,25,230,173,208,72,83,54,25,251,16,130,16,175,190,76,117,165,
-  68,173,84,68,251,145,134,6,108,6,104,43,133,105,25,56,78,192,209,199,159,231,248,253,79,233,236,227,207,184,169,165,
-  197,202,94,29,228,214,192,76,2,206,134,112,50,128,19,81,56,87,131,250,54,33,249,188,48,176,3,255,187,46,217,250,
-  170,1,144,18,160,132,73,104,200,239,186,4,235,91,9,132,44,75,82,174,133,124,249,177,57,180,254,254,34,33,226,17,
-  131,191,252,202,20,255,114,50,247,29,189,255,15,128,231,155,38,199,85,64,23,168,17,88,72,192,179,173,138,109,35,15,
-  28,91,179,124,251,21,29,3,177,40,53,203,192,51,92,162,4,160,147,40,34,23,205,12,68,67,77,215,90,19,72,192,
-  22,68,187,251,17,102,10,2,129,209,12,156,107,228,41,53,242,201,132,50,192,148,84,189,18,86,58,65,203,250,181,100,
-  23,134,9,170,33,45,109,45,12,14,12,16,90,25,18,91,214,147,216,178,155,186,87,229,212,240,9,132,165,17,210,199,
-  10,67,162,50,141,210,49,20,1,142,237,81,244,202,72,25,65,38,45,180,97,161,2,143,186,23,232,245,231,167,117,183,
-  143,253,180,239,122,17,205,18,176,92,4,239,100,87,140,93,215,246,64,45,96,97,161,198,244,179,217,151,228,120,125,12,
-  248,37,136,102,53,155,162,112,251,18,220,189,252,145,247,31,248,217,143,255,69,36,99,27,232,192,105,144,202,8,164,52,
-  27,0,163,245,75,65,231,82,96,121,249,38,255,22,135,234,203,65,72,55,181,157,11,17,208,23,92,246,226,18,144,186,
-  8,52,151,120,194,94,84,142,244,55,1,20,90,55,211,60,94,10,112,23,238,117,33,250,186,81,73,160,25,151,45,27,
-  166,152,20,33,161,239,226,87,203,148,86,86,168,87,138,232,192,66,26,62,210,118,49,173,22,16,113,150,23,151,57,249,
-  216,51,122,226,95,190,236,196,142,159,92,217,164,84,238,58,88,176,97,76,193,48,48,225,55,126,230,4,20,43,141,96,
-  126,173,208,24,250,66,100,208,119,169,1,133,230,171,83,130,61,16,223,95,144,206,91,1,132,76,195,32,84,1,127,250,
-  233,251,57,55,49,254,186,223,63,74,163,92,194,245,192,205,192,127,111,188,230,8,24,213,112,168,115,108,101,231,225,199,
-  143,180,245,127,232,46,105,251,80,55,163,104,21,160,241,209,70,216,136,105,1,12,45,65,105,148,33,168,98,17,105,111,
-  67,180,182,17,202,176,113,34,53,131,25,181,8,9,180,198,212,141,2,21,210,84,60,248,229,71,57,176,251,74,174,120,
-  207,207,224,44,157,34,168,186,152,210,196,136,8,140,88,43,178,109,51,136,56,95,248,235,191,101,252,228,56,41,195,66,
-  227,16,183,163,120,142,34,12,11,152,102,140,152,145,198,11,138,88,145,58,166,78,17,170,8,102,44,193,252,217,97,125,
-  101,177,110,22,34,166,209,234,82,54,149,88,240,208,57,27,252,142,71,231,248,210,135,30,192,2,126,63,231,242,239,231,
-  107,23,83,47,126,190,201,249,204,195,230,8,220,145,131,187,202,63,251,35,251,127,228,47,254,75,52,35,3,180,47,26,
-  109,254,154,192,162,155,224,160,105,132,41,188,28,92,132,248,230,141,46,148,254,166,224,195,11,57,237,66,235,151,106,42,
-  223,230,122,92,226,134,191,232,194,215,47,94,77,41,133,144,47,166,105,232,151,152,117,77,83,238,2,184,73,9,234,66,
-  90,136,213,236,117,214,76,111,65,131,87,167,88,204,81,45,21,209,117,69,160,52,194,210,216,73,3,100,7,229,130,199,
-  248,179,99,156,252,250,87,66,241,248,179,229,182,133,133,236,229,48,18,131,51,26,198,53,204,186,48,29,194,130,130,82,
-  28,234,62,4,223,0,222,71,163,204,201,247,181,167,180,120,243,116,171,184,0,66,198,155,22,124,66,254,247,23,190,240,
-  134,128,207,75,198,9,184,31,184,6,248,91,96,3,228,115,112,188,21,142,156,249,210,161,141,35,55,95,213,185,185,179,
-  27,25,104,124,67,99,74,15,41,20,74,54,226,130,100,8,82,24,248,158,137,209,210,65,180,123,35,202,202,160,49,27,
-  166,25,141,124,178,6,51,18,130,82,72,21,131,64,176,105,176,131,99,135,238,199,176,174,165,181,117,39,86,107,2,31,
-  23,63,212,232,64,50,127,122,156,39,238,125,152,67,247,63,74,90,138,70,175,119,51,66,32,162,104,211,196,240,52,182,
-  229,163,85,21,233,196,176,68,12,175,150,35,18,143,82,168,197,25,152,92,168,119,67,165,40,85,116,200,146,185,0,61,
-  159,132,210,147,160,254,40,239,145,202,123,204,240,98,35,116,23,248,8,240,107,16,93,130,205,86,3,124,238,142,255,230,
-  175,238,187,245,255,253,141,120,4,80,126,163,220,169,104,166,168,92,64,16,45,154,197,183,4,8,169,95,22,11,252,82,
-  142,231,229,4,179,184,84,59,121,41,17,211,244,84,125,11,210,79,136,70,116,243,203,194,8,26,227,254,82,79,219,75,
-  52,160,230,139,2,113,193,94,123,17,188,46,128,83,216,224,134,132,14,9,61,135,122,169,68,113,101,137,192,119,209,161,
-  143,25,49,136,91,3,120,161,102,230,204,48,211,15,254,189,95,123,228,201,146,30,158,91,94,239,185,11,29,48,35,96,
-  84,193,49,13,231,12,200,22,161,166,193,217,6,193,68,147,236,183,105,212,20,50,249,254,115,1,222,116,113,64,129,217,
-  200,97,122,121,176,177,16,224,171,111,50,165,95,23,17,134,65,94,133,124,234,11,95,96,250,117,6,31,69,35,144,46,
-  70,163,44,232,102,26,233,25,208,232,125,53,3,236,2,47,7,227,10,14,245,143,230,183,63,245,215,247,93,179,230,247,
-  126,206,138,6,117,234,129,129,86,54,134,80,4,194,71,107,129,106,146,158,166,105,16,73,165,27,190,36,175,142,33,156,
-  198,30,242,66,68,224,161,107,101,66,191,134,87,201,33,162,109,196,122,183,177,125,251,126,122,51,41,102,39,158,161,112,
-  206,35,32,206,163,79,78,48,63,179,132,196,163,180,180,140,80,62,137,164,69,72,140,74,13,100,110,89,235,90,41,52,
-  106,126,32,67,165,137,72,115,70,154,150,159,106,161,175,175,149,195,195,139,236,223,179,155,244,153,17,239,186,154,55,18,
-  192,233,152,169,83,25,211,204,107,130,19,75,80,218,14,252,51,141,158,84,191,220,228,198,42,192,30,16,183,65,44,11,
-  27,5,220,177,40,197,187,210,127,240,127,31,184,241,215,255,157,109,170,58,218,23,40,67,130,244,154,113,80,13,243,228,
-  98,154,213,5,66,247,21,192,231,82,205,67,136,70,106,197,203,65,225,165,154,211,139,92,207,43,154,109,151,182,108,121,
-  217,53,190,245,53,95,38,82,54,92,250,151,106,79,82,54,146,97,195,128,192,173,83,40,229,169,22,11,232,48,68,106,
-  136,153,49,140,68,27,185,21,143,227,79,62,194,210,253,159,173,89,79,189,176,216,86,114,38,58,27,241,85,35,192,68,
-  8,115,10,22,53,100,79,64,165,27,194,103,129,187,155,196,127,132,239,188,27,170,6,2,33,47,86,87,12,223,42,0,
-  4,141,196,148,80,188,20,105,76,169,185,98,107,137,123,15,183,163,20,223,115,251,157,239,158,219,146,180,6,1,43,95,
-  254,34,91,38,198,217,247,58,220,83,2,75,128,182,164,204,133,58,122,94,233,164,103,10,243,46,41,195,119,5,42,20,
-  13,247,6,18,168,131,154,6,95,75,81,87,112,44,173,244,99,181,47,62,211,250,229,142,142,109,215,255,236,221,118,79,
-  74,53,74,154,41,129,173,3,66,35,68,94,112,211,74,129,242,11,184,5,141,33,163,16,4,44,229,114,84,10,121,234,
-  217,101,130,252,50,125,109,49,90,50,146,32,221,65,208,222,142,21,73,144,236,105,103,125,122,27,102,118,25,137,226,176,
-  59,195,202,228,36,137,100,138,54,43,202,178,142,113,106,122,130,182,153,69,255,250,124,57,59,224,7,11,113,33,22,163,
-  176,108,8,173,2,33,187,39,165,222,248,181,232,194,154,229,32,176,215,72,35,156,93,92,52,110,155,95,206,103,224,89,
-  15,238,71,136,168,237,135,69,109,136,99,29,154,202,101,77,239,212,85,52,114,194,135,128,110,144,46,180,120,176,85,195,
-  205,5,203,184,115,224,79,254,223,3,7,127,241,151,237,80,23,9,67,11,41,45,132,40,53,245,198,104,195,236,121,169,
-  114,211,124,77,55,184,147,151,113,65,151,186,217,47,104,27,175,8,16,23,52,155,75,226,122,94,66,66,211,136,5,122,
-  185,105,166,47,189,230,183,57,92,47,130,84,211,92,212,77,206,220,144,141,64,2,167,84,196,205,47,83,169,148,1,133,
-  105,128,29,143,17,10,155,217,185,44,195,79,62,64,238,159,254,177,212,121,98,106,122,72,233,51,86,131,72,62,235,195,
-  152,104,244,124,47,246,129,51,6,126,28,244,87,163,49,18,157,221,120,90,83,93,156,67,124,71,13,59,53,90,52,146,
-  159,109,173,25,170,23,41,211,104,42,208,253,45,64,232,45,19,9,29,40,193,238,161,42,2,248,234,115,237,160,244,235,
-  2,66,129,109,33,142,156,228,87,199,199,95,183,78,1,178,169,234,206,24,50,86,86,225,86,83,176,91,6,186,37,36,
-  244,116,179,157,184,209,212,142,44,8,60,40,10,88,70,32,3,152,239,134,115,185,191,190,39,253,249,167,158,239,237,184,
-  251,230,72,98,221,58,145,140,42,210,26,173,48,133,111,134,184,161,135,225,88,218,43,141,170,108,181,196,204,236,10,142,
-  17,149,55,92,153,22,29,122,137,184,15,201,168,141,45,91,9,172,110,66,105,160,52,120,118,10,203,52,48,34,73,252,
-  232,0,166,91,162,119,211,54,156,51,179,36,13,155,66,96,48,63,124,42,188,241,252,228,242,21,138,145,36,28,11,225,
-  180,175,245,148,7,203,74,163,109,84,255,166,144,3,63,175,195,131,103,142,157,221,184,54,157,202,220,95,25,201,108,243,
-  194,115,30,60,99,192,161,182,254,164,173,114,94,205,174,135,185,205,126,35,15,242,62,224,134,102,120,194,143,131,84,208,
-  41,225,50,1,111,91,138,152,215,117,253,241,239,238,184,252,23,127,222,86,170,130,129,68,75,5,194,71,72,187,225,48,
-  208,47,166,79,188,4,129,180,108,242,42,250,197,236,214,75,114,192,47,132,254,93,40,167,193,203,226,12,47,18,196,23,
-  254,205,203,210,45,46,209,166,46,37,182,95,9,232,46,60,128,66,94,124,14,217,4,64,133,68,233,16,83,138,6,177,
-  236,186,84,107,53,202,165,50,65,181,78,52,128,84,50,130,138,155,20,29,201,169,211,211,76,220,123,79,24,60,240,245,
-  98,215,88,118,118,87,168,207,72,120,193,131,163,65,35,177,55,107,65,117,26,252,253,52,226,203,114,141,67,141,13,169,
-  12,195,137,164,80,82,234,149,74,25,163,176,194,203,113,59,108,174,85,129,192,23,18,87,154,36,125,135,117,229,69,226,
-  245,18,31,240,29,30,66,211,10,220,250,86,210,128,190,149,56,190,100,215,80,35,224,254,245,0,33,37,4,237,142,195,
-  214,231,15,115,150,215,183,102,74,40,132,136,43,221,166,67,125,205,68,103,199,135,229,64,111,111,91,189,86,243,157,122,
-  232,170,0,63,187,18,132,245,64,73,8,19,80,76,42,157,141,66,77,66,84,64,123,23,120,45,167,231,157,249,211,255,
-  32,103,26,113,159,165,184,166,12,212,155,165,233,49,192,182,33,101,64,107,31,68,134,219,82,97,251,13,63,101,173,73,
-  116,136,208,55,49,77,129,157,105,67,68,18,248,178,145,48,170,149,9,97,163,57,158,145,178,32,30,101,112,219,110,110,
-  215,81,148,107,114,239,95,126,42,120,207,240,228,249,205,240,148,3,79,213,225,152,11,211,157,141,234,124,110,69,32,52,
-  156,117,53,195,118,160,94,216,11,91,188,98,105,207,59,90,205,206,104,160,190,161,224,249,0,22,91,93,41,242,131,105,
-  181,101,155,17,164,29,197,194,209,101,206,185,33,155,27,156,151,72,65,139,223,72,173,120,239,25,219,184,105,211,159,252,
-  222,154,235,62,246,75,82,133,85,208,18,100,179,96,24,97,83,49,49,26,69,249,95,209,181,45,154,193,123,92,244,40,
-  137,87,40,179,113,177,20,199,43,104,63,23,189,102,23,180,156,151,113,5,175,164,53,105,253,114,127,241,37,247,19,23,
-  32,175,17,115,45,180,194,192,71,5,62,181,178,131,91,171,18,184,46,6,146,184,105,163,91,82,20,203,14,211,35,243,
-  156,121,246,176,94,254,198,131,161,56,123,182,178,109,185,54,218,14,39,128,19,202,146,39,156,64,141,36,52,75,75,80,
-  63,11,250,61,52,42,116,206,211,136,171,26,107,237,224,241,84,11,37,41,177,131,64,187,166,201,5,191,182,201,133,72,
-  250,6,248,164,128,127,0,126,58,244,185,166,48,203,110,223,229,164,87,165,30,54,180,37,131,75,139,137,252,0,0,208,
-  235,13,66,17,219,230,75,95,249,50,255,146,91,126,93,137,241,58,240,81,173,217,233,135,241,10,172,217,248,177,119,110,
-  221,253,159,126,51,173,242,53,60,215,197,247,2,242,75,57,10,75,89,150,167,23,244,194,233,179,106,244,204,176,151,31,
-  159,13,168,212,84,111,161,236,183,57,129,104,7,107,109,131,52,116,149,38,231,193,148,110,180,127,154,145,141,212,163,245,
-  6,236,209,144,142,128,25,89,41,135,75,167,199,213,134,43,182,26,90,134,152,137,52,210,238,110,198,13,129,161,84,163,
-  22,22,70,115,163,74,2,12,182,237,223,71,199,166,237,156,56,57,74,223,216,112,105,3,60,235,192,191,120,112,164,31,
-  86,218,193,171,209,136,95,42,104,248,61,40,222,13,213,101,152,78,195,17,17,114,40,150,87,41,87,235,113,11,198,239,
-  7,255,151,198,10,180,188,127,29,173,255,237,26,62,254,63,78,210,255,236,34,9,160,3,100,26,90,3,216,39,225,142,
-  115,173,137,155,7,254,231,31,174,189,233,71,62,140,14,43,47,70,36,95,0,11,117,129,39,17,77,160,145,223,12,16,
-  77,240,81,77,19,234,149,57,152,198,86,18,151,16,216,47,183,192,190,213,78,123,49,243,253,165,87,187,96,71,189,36,
-  53,227,194,103,84,216,12,60,84,16,134,4,245,26,94,181,138,235,120,184,174,143,235,120,148,138,21,38,39,102,57,113,
-  242,44,249,145,145,124,109,108,216,88,51,189,16,235,207,215,228,53,224,90,48,229,55,0,253,89,97,136,83,102,204,156,
-  147,213,32,31,13,149,235,210,200,20,27,107,2,201,87,104,116,202,8,34,49,38,146,25,153,9,3,21,85,10,91,11,
-  30,235,236,71,6,138,71,42,69,190,4,124,148,70,221,165,5,224,243,13,109,148,27,74,139,244,53,189,180,21,190,243,
-  180,140,183,100,50,234,5,16,18,2,238,57,244,218,128,144,101,154,204,205,207,243,240,217,179,175,122,47,164,239,68,110,
-  0,246,104,68,8,102,44,217,47,147,244,161,50,85,132,104,20,62,239,93,115,177,129,138,0,109,184,97,57,182,148,155,
-  167,84,46,48,55,57,195,212,145,243,250,240,225,243,186,120,110,152,182,220,124,108,112,41,223,209,227,40,47,2,217,16,
-  106,33,152,6,164,198,132,104,31,110,105,137,22,93,47,92,87,171,230,90,243,229,20,200,132,52,162,200,148,133,107,229,
-  81,50,142,150,233,70,65,117,45,64,70,16,82,52,220,189,97,163,240,86,119,119,130,175,252,253,255,98,125,205,91,84,
-  112,220,133,227,253,176,176,17,152,4,18,23,52,187,38,121,158,131,224,48,20,63,0,101,11,22,203,161,50,214,128,251,
-  36,184,127,0,252,12,240,92,214,225,127,254,151,163,252,233,159,159,228,243,192,26,176,108,232,146,112,64,195,237,165,76,
-  250,166,171,254,254,207,7,118,220,245,126,130,160,210,228,182,196,69,23,117,131,179,109,186,219,149,122,17,124,94,142,28,
-  77,32,144,151,124,238,229,222,39,248,102,207,214,69,254,134,11,37,68,248,166,207,139,75,180,35,165,94,212,172,94,130,
-  87,47,139,148,110,120,220,26,122,170,239,57,84,74,121,220,74,25,252,128,149,149,60,181,186,71,173,238,49,49,51,207,
-  226,66,14,17,181,105,235,235,19,189,169,184,144,221,163,82,31,63,175,231,22,171,34,13,177,8,12,152,224,25,161,110,
-  11,74,94,86,66,182,0,139,253,144,107,129,66,20,106,43,224,183,130,142,0,134,231,97,132,74,186,161,86,82,8,162,
-  129,230,72,189,194,223,185,245,139,49,87,203,52,34,244,79,241,98,70,154,126,153,17,201,15,50,0,93,0,161,157,107,
-  27,154,208,107,1,66,134,97,240,216,225,231,8,85,248,134,124,191,11,147,106,130,206,215,124,237,19,34,195,26,72,147,
-  80,55,203,104,200,11,252,132,194,32,66,95,215,122,6,187,12,118,108,184,18,110,54,133,86,174,88,204,206,49,50,113,
-  46,58,118,236,68,244,208,163,79,182,183,221,247,116,251,246,149,122,175,9,98,12,54,157,123,207,245,131,215,255,226,7,
-  101,101,201,229,228,253,79,166,75,237,129,29,218,26,17,79,224,218,81,180,138,96,5,22,58,149,68,199,163,4,42,96,
-  121,110,129,165,133,121,226,153,86,58,186,218,105,207,100,240,171,121,206,61,244,164,190,22,202,33,20,53,184,6,141,34,
-  97,247,2,31,2,62,69,163,72,124,51,187,9,23,216,10,234,75,80,253,29,224,12,240,80,243,111,21,224,43,143,206,
-  243,196,163,243,252,52,208,11,177,42,12,41,184,90,195,45,243,107,123,174,61,248,169,143,15,108,184,246,122,17,6,43,
-  32,12,228,203,0,230,37,32,112,233,198,208,47,117,155,95,52,161,46,252,214,23,198,95,95,108,22,248,45,61,99,188,
-  88,84,76,124,139,200,233,134,199,170,1,112,250,146,32,67,33,68,3,24,47,137,235,185,16,6,129,144,132,161,71,62,
-  151,35,240,235,24,82,98,196,98,116,13,38,72,181,180,17,73,164,185,222,176,65,92,48,140,68,139,2,74,78,145,133,
-  233,49,157,31,57,23,45,28,25,94,59,251,220,217,94,255,220,240,101,198,212,120,49,94,243,243,169,70,137,141,89,5,
-  147,9,56,239,194,68,2,22,175,129,146,15,117,242,75,238,79,212,202,193,94,26,93,68,222,171,53,89,183,254,138,222,
-  217,239,62,236,240,7,8,128,46,130,208,80,21,165,5,95,123,174,237,85,3,33,211,182,25,93,92,228,216,185,179,111,
-  216,119,107,244,73,64,27,160,23,166,39,148,210,10,67,217,205,8,87,209,108,164,217,104,54,168,181,143,212,38,194,139,
-  162,133,73,64,136,22,43,72,233,209,211,221,70,79,247,219,184,238,138,183,227,124,244,39,204,111,124,250,203,107,206,252,
-  218,239,180,110,207,86,253,197,107,14,36,223,251,27,255,70,182,36,44,18,125,49,214,118,246,198,23,142,62,128,29,109,
-  69,71,99,120,120,216,118,20,75,219,84,133,205,137,211,195,148,11,62,137,136,69,75,58,73,173,94,225,216,209,89,186,
-  210,105,34,169,52,231,102,43,92,15,105,19,54,25,176,105,14,130,105,40,181,53,171,233,253,241,255,199,222,123,199,217,
-  117,149,247,222,223,181,214,46,167,77,239,51,234,93,150,229,38,119,227,130,11,54,166,183,64,2,132,155,158,155,155,114,
-  19,82,222,220,36,55,164,151,123,19,146,144,16,72,66,239,197,16,192,96,3,198,5,92,112,151,44,201,234,189,204,140,
-  166,207,169,187,172,181,222,63,214,62,103,70,178,76,53,224,228,230,240,25,70,26,143,102,206,217,251,236,103,63,207,239,
-  249,21,224,143,178,77,214,141,89,255,166,178,59,234,126,154,70,17,112,67,54,134,126,20,7,94,212,32,156,115,185,92,
-  183,104,184,249,208,170,193,243,174,250,196,191,244,173,190,248,50,108,163,138,240,36,66,52,64,228,78,103,17,47,46,26,
-  103,217,64,157,214,117,180,68,157,139,185,62,207,212,129,137,197,27,119,78,103,55,115,150,85,186,109,117,98,205,85,187,
-  61,189,131,82,226,244,231,213,114,57,51,72,207,167,167,175,223,5,22,26,139,242,218,16,129,223,250,54,99,156,10,222,
-  10,39,129,80,214,208,233,123,116,174,217,36,88,123,129,224,86,29,164,54,9,166,166,231,75,227,71,78,244,207,239,219,
-  97,15,222,247,96,90,127,124,71,173,180,125,247,100,123,61,62,90,114,145,205,135,44,28,247,225,96,193,218,221,126,163,
-  54,125,14,152,19,60,83,103,247,92,63,254,195,251,1,53,98,201,121,43,29,31,211,21,33,103,96,255,61,111,189,124,
-  159,32,142,89,121,223,189,252,182,214,92,147,157,4,153,93,32,63,140,77,152,118,107,102,155,56,70,66,67,237,60,80,
-  179,141,90,135,80,113,166,55,148,160,20,88,135,232,42,171,176,82,96,165,118,30,54,217,165,97,17,104,93,67,232,58,
-  200,54,124,227,113,205,45,151,240,15,95,190,176,112,223,3,59,226,107,126,243,141,254,72,103,137,100,190,1,185,10,51,
-  149,105,82,145,71,149,58,192,179,104,35,25,159,52,140,87,38,136,139,9,253,171,206,103,243,134,165,132,62,64,140,65,
-  161,141,71,101,106,146,131,251,15,226,13,15,138,79,31,216,191,244,245,85,115,99,55,248,17,220,187,28,182,141,192,88,
-  12,177,202,58,155,187,178,99,122,3,11,102,242,100,119,220,45,56,119,248,128,22,7,74,53,96,73,0,55,106,120,217,
-  193,229,131,231,110,249,216,187,187,214,95,124,9,54,106,32,68,30,43,34,231,111,36,220,138,221,102,184,138,92,188,137,
-  18,153,233,232,25,51,130,104,205,79,246,244,177,107,113,135,100,237,130,153,252,162,175,137,197,223,243,108,139,233,140,213,
-  236,192,158,133,21,250,130,127,116,214,79,88,155,165,124,45,220,130,132,144,168,112,33,167,212,0,198,198,11,99,165,176,
-  88,171,179,151,102,73,12,72,235,35,72,49,105,5,147,54,208,73,66,187,47,9,150,119,18,45,191,90,172,185,230,26,
-  191,62,55,221,49,182,107,95,199,232,35,79,174,60,252,213,175,111,233,220,115,96,186,189,90,31,247,45,143,214,225,54,
-  11,143,126,18,170,159,226,59,231,253,252,63,91,128,206,94,132,196,247,84,132,82,223,167,119,98,130,45,247,222,195,174,
-  195,135,152,1,150,224,200,127,202,1,160,4,217,65,251,65,110,196,4,206,136,126,171,243,216,58,37,199,71,203,211,83,
-  167,134,134,151,12,187,145,208,58,252,71,202,44,87,70,100,74,105,107,17,86,58,155,11,235,185,39,41,37,86,73,18,
-  98,60,17,208,102,122,88,114,254,11,213,93,245,32,247,115,231,93,32,210,106,131,88,37,132,94,142,106,165,138,232,132,
-  178,178,28,62,28,19,37,154,160,179,159,254,245,231,50,176,124,21,158,95,192,164,41,90,71,8,83,67,11,69,34,243,
-  116,116,117,112,225,149,23,242,190,15,255,35,159,255,244,237,197,79,191,231,195,23,108,126,244,233,158,75,97,169,117,24,
-  196,3,157,176,255,167,160,246,199,217,177,123,20,199,126,187,57,27,201,200,186,162,235,129,159,202,10,212,195,160,82,232,
-  15,224,242,20,110,28,95,59,180,241,198,79,125,168,115,249,249,91,72,245,60,248,46,10,71,154,28,66,164,153,227,181,
-  124,198,150,203,97,48,98,225,224,46,234,86,22,182,87,11,38,241,52,71,163,179,21,163,51,199,59,151,205,243,237,55,
-  170,198,208,36,97,187,95,223,36,19,102,4,199,69,40,182,3,184,155,223,172,48,24,176,38,243,139,150,217,18,64,160,
-  172,113,186,54,112,185,243,88,210,164,65,92,171,81,47,59,255,104,173,19,4,41,90,167,40,83,68,161,232,44,104,250,
-  46,185,128,117,87,94,46,143,191,233,21,237,247,127,230,193,246,79,189,243,125,43,94,54,118,212,95,6,79,106,216,94,
-  128,234,237,223,170,176,62,71,215,192,127,26,71,196,102,17,146,194,102,152,208,119,94,132,172,16,164,185,28,35,59,119,
-  114,197,237,159,39,111,12,59,23,241,28,118,186,98,192,239,3,219,112,36,65,47,187,59,152,31,80,1,74,220,239,174,
-  181,195,100,114,252,196,220,177,29,187,24,94,178,2,97,107,24,145,58,118,136,113,184,135,64,96,108,130,69,35,133,135,
-  161,233,13,227,184,47,210,10,148,104,160,100,66,234,41,214,174,91,71,185,81,19,161,215,78,213,215,20,138,37,198,199,
-  171,60,250,200,54,150,173,232,98,235,145,58,221,93,195,44,239,31,164,179,167,15,47,223,129,53,18,25,71,46,132,79,
-  133,24,225,82,30,148,17,164,24,76,221,163,88,232,226,205,191,240,38,110,120,213,203,212,39,223,253,158,21,159,251,135,
-  247,118,95,51,57,63,220,3,125,83,112,199,107,97,215,32,84,150,227,12,229,107,14,219,225,15,178,215,189,59,27,207,
-  60,32,132,92,5,134,115,112,89,12,47,217,183,105,213,197,183,124,234,125,189,203,55,158,143,73,107,40,17,100,57,242,
-  58,235,48,228,66,202,232,226,35,185,128,49,187,85,251,34,1,167,104,229,179,55,11,9,88,163,51,32,219,91,52,107,
-  89,7,190,159,229,60,181,198,182,197,74,245,69,242,136,5,89,70,246,217,52,123,41,133,200,182,139,194,58,206,143,22,
-  230,180,78,205,117,112,205,140,52,50,251,16,72,77,154,157,119,103,169,130,177,52,42,53,26,51,83,212,27,53,210,56,
-  205,70,190,172,177,242,2,194,66,1,63,240,137,26,121,118,238,59,202,190,109,247,219,209,199,183,210,216,179,71,148,199,
-  102,204,202,122,163,220,41,196,169,196,218,186,2,17,130,250,63,60,211,236,77,103,55,229,114,230,201,182,234,12,44,168,
-  105,88,47,255,95,43,64,205,34,116,238,138,197,192,244,183,47,66,34,8,16,81,196,11,254,253,179,12,236,219,235,132,
-  125,103,153,249,39,178,207,255,148,97,22,167,50,238,196,0,11,190,116,207,21,6,158,100,39,249,173,160,173,160,33,181,
-  173,31,221,185,219,94,118,203,75,132,53,166,101,116,229,248,42,10,147,70,40,21,128,244,1,141,182,137,243,162,17,46,
-  201,3,44,82,231,193,8,140,156,101,112,164,141,75,131,13,164,73,157,242,92,204,67,15,60,206,252,220,36,151,95,122,
-  62,107,215,174,160,88,204,161,60,176,182,65,121,126,10,85,169,208,72,52,86,10,148,231,161,2,31,63,204,17,230,66,
-  2,223,119,215,140,116,214,15,73,18,51,208,211,193,255,252,131,63,226,241,23,223,218,254,133,223,249,211,75,214,221,253,
-  245,226,114,80,83,160,207,133,167,87,66,35,204,198,175,147,184,248,223,197,111,240,216,53,154,171,10,130,27,107,150,155,
-  14,111,94,125,233,77,183,189,191,127,197,218,205,232,180,214,26,65,164,179,248,194,74,147,169,192,207,94,206,133,56,109,
-  249,125,246,49,11,50,31,30,177,136,202,99,159,85,22,33,207,32,26,182,186,38,33,206,218,21,181,48,166,69,187,34,
-  41,213,105,207,72,234,133,231,99,23,255,47,43,68,210,40,119,3,144,174,240,234,184,65,163,86,163,92,158,163,62,63,
-  143,72,98,60,95,225,135,62,202,247,177,210,39,137,148,173,156,154,177,213,99,227,118,238,233,189,118,247,3,247,137,104,
-  199,94,81,152,170,138,245,64,55,152,130,107,56,119,199,176,59,21,2,13,107,67,107,163,203,120,166,101,154,0,82,208,
-  101,168,21,161,252,135,144,196,160,79,66,50,13,137,2,221,6,38,255,29,222,156,255,211,121,66,127,55,69,200,203,229,
-  56,176,115,39,87,126,227,235,44,159,157,33,250,22,109,101,115,58,239,203,62,6,112,182,24,17,240,24,112,14,206,169,
-  59,120,142,230,102,237,126,118,26,90,102,187,96,122,108,235,211,141,196,146,23,214,115,70,85,66,225,171,144,79,127,228,
-  195,28,63,122,132,13,235,55,177,124,217,10,186,250,122,232,25,232,195,207,133,88,12,198,166,104,147,58,189,144,1,191,
-  212,206,192,224,48,189,253,3,84,171,49,255,244,143,239,226,154,23,188,136,91,94,116,51,158,151,144,196,17,82,64,210,
-  136,153,155,47,115,226,228,65,198,198,78,33,60,203,224,72,63,249,124,193,21,0,1,158,242,8,124,159,182,182,54,134,
-  134,151,81,104,239,67,36,33,210,88,140,173,177,229,194,11,89,253,185,127,13,222,243,91,127,112,65,250,174,79,38,171,
-  225,88,12,199,191,2,141,52,27,189,62,176,8,232,92,1,188,26,100,10,3,70,112,117,108,121,229,244,85,151,92,116,
-  235,135,255,185,99,100,197,26,76,82,119,29,139,177,45,60,70,32,78,207,16,62,91,9,18,11,89,94,205,172,180,103,
-  22,22,113,186,176,243,180,53,252,25,197,230,204,175,53,173,53,178,175,25,173,91,106,246,211,192,232,214,182,75,62,19,
-  44,183,32,116,54,216,72,225,230,193,38,25,81,10,231,243,147,26,226,90,157,70,173,74,220,168,18,69,53,226,70,21,
-  33,44,129,239,33,60,159,52,150,156,56,94,225,232,193,99,181,249,221,187,27,237,247,63,24,200,125,135,130,100,182,66,
-  1,228,250,236,61,42,28,198,72,12,102,206,245,92,5,13,107,141,181,253,205,198,167,154,221,84,253,236,254,146,45,69,
-  80,78,174,49,13,156,178,80,141,33,50,48,61,15,83,109,48,215,11,115,33,148,143,66,61,5,109,255,95,42,64,139,
-  139,144,177,130,59,31,239,122,198,2,68,10,65,152,207,115,106,231,14,158,254,226,23,184,241,12,117,242,183,43,12,205,
-  105,189,169,217,122,12,120,41,46,155,106,34,239,49,178,166,235,116,123,134,239,226,97,149,32,56,50,79,105,54,98,210,
-  221,85,102,67,152,50,7,142,214,211,68,231,67,233,33,50,165,110,84,171,114,226,232,81,174,191,250,42,2,9,141,202,
-  56,251,198,14,178,107,187,160,167,127,128,222,193,97,186,122,251,200,23,218,177,36,36,38,226,158,59,238,99,207,83,123,
-  152,153,153,97,116,116,140,185,185,42,71,70,15,49,246,249,67,228,11,1,70,67,181,82,229,232,209,227,212,235,17,155,
-  207,219,68,119,119,55,185,208,163,16,180,57,230,109,62,143,239,7,24,109,41,22,74,72,161,152,157,158,34,74,43,180,
-  151,150,99,180,101,98,242,40,137,31,19,180,181,211,104,31,100,20,186,215,67,91,3,188,82,246,198,155,4,30,204,94,
-  247,18,224,93,32,135,160,119,26,46,73,45,55,206,94,188,121,243,245,31,127,111,71,223,146,37,152,196,209,219,164,148,
-  216,166,230,234,180,49,122,225,236,61,147,193,252,29,30,123,219,12,116,124,38,194,97,207,98,217,193,153,22,28,205,247,
-  208,34,43,141,38,222,228,222,51,178,21,143,241,12,177,170,112,124,42,137,0,149,73,59,146,212,97,110,81,76,163,86,
-  165,209,40,147,36,26,147,106,172,181,40,165,176,169,100,102,102,134,19,199,71,57,118,98,130,157,59,247,114,236,248,41,
-  100,117,126,230,188,82,33,90,221,211,57,220,86,216,32,68,222,103,170,187,67,38,94,81,200,70,106,195,80,217,106,181,
-  74,106,36,22,221,102,73,54,8,21,172,71,41,26,141,72,128,37,73,18,102,102,102,169,149,231,173,72,82,163,163,216,
-  138,114,205,20,162,52,237,168,39,245,30,99,230,219,161,81,66,197,37,204,84,17,59,46,96,34,133,19,243,78,228,186,
-  215,131,19,29,110,169,249,255,78,1,106,22,161,11,86,85,104,196,146,59,31,239,166,16,58,62,143,167,20,115,105,202,
-  216,191,127,134,23,239,221,203,23,207,4,27,191,71,204,166,11,167,163,249,236,242,118,126,227,95,110,194,143,191,55,254,
-  144,95,244,121,234,111,31,231,182,143,238,226,34,80,121,8,44,228,166,106,243,42,142,171,228,242,33,214,24,148,242,136,
-  116,76,127,95,39,157,165,28,82,198,116,248,37,122,123,139,68,81,66,35,174,113,108,207,78,142,239,243,105,111,239,101,
-  104,213,8,119,221,253,21,118,60,250,4,235,86,173,97,205,218,53,116,118,94,68,71,103,27,158,132,66,49,159,69,5,
-  123,104,35,8,253,128,98,161,8,194,210,136,106,84,231,202,196,181,6,243,229,121,230,102,102,80,126,192,232,248,20,113,
-  108,41,182,117,209,223,219,195,240,210,110,10,109,167,8,75,121,196,84,157,180,10,239,254,147,191,182,249,247,125,226,212,
-  229,240,120,4,251,5,84,101,118,23,254,100,134,3,45,1,254,5,100,27,244,77,194,229,192,203,107,27,214,92,114,213,
-  135,255,173,187,119,201,8,54,169,158,54,224,74,41,91,163,137,88,148,225,105,91,91,36,123,58,59,78,52,61,155,23,
-  157,51,217,244,87,126,54,85,250,162,31,112,230,205,228,204,181,254,217,100,23,207,144,99,100,120,212,153,52,128,214,118,
-  13,164,48,196,113,76,60,87,67,232,148,180,214,32,169,55,144,218,162,109,138,13,5,129,167,176,74,50,51,61,203,238,
-  221,123,8,253,60,70,67,24,22,216,180,105,13,23,110,217,72,177,148,35,95,12,71,132,204,140,254,241,92,51,37,53,
-  18,31,161,82,129,172,57,199,196,36,15,86,97,68,226,104,29,89,113,183,214,217,177,196,73,76,181,82,197,88,75,221,
-  24,170,149,6,149,153,121,102,78,78,48,119,114,106,228,196,169,25,166,143,157,196,142,159,74,194,153,217,122,119,165,86,
-  95,153,234,153,165,82,236,193,232,47,47,133,59,103,224,232,39,224,25,254,161,175,195,5,184,252,72,30,70,255,224,227,
-  149,235,117,56,103,73,153,3,39,67,142,76,228,201,5,146,84,107,222,251,185,207,114,233,225,195,180,63,199,219,44,211,
-  4,144,51,159,25,15,136,98,227,188,130,191,83,80,206,64,116,235,74,62,253,209,93,92,14,129,130,254,41,24,106,44,
-  27,46,204,140,30,163,99,213,114,148,114,152,64,26,55,144,104,164,72,157,21,110,172,113,114,83,75,41,148,116,149,186,
-  136,19,131,177,53,190,246,185,207,177,103,207,62,126,241,45,111,194,164,53,151,248,160,4,82,248,120,34,64,42,167,239,
-  202,229,10,72,225,183,214,199,113,212,160,32,61,74,61,93,84,243,21,74,29,1,8,129,151,11,185,236,186,23,160,241,
-  57,126,226,20,251,159,62,200,216,214,61,120,187,83,134,7,214,177,97,213,18,238,254,250,109,76,124,232,19,245,159,129,
-  253,117,248,186,133,237,13,168,188,22,248,52,240,53,96,56,43,62,157,158,232,139,12,87,10,99,95,145,174,91,121,245,
-  21,159,124,207,72,223,250,245,50,142,43,120,50,231,138,135,52,45,213,248,66,195,35,154,104,24,167,105,181,206,204,8,
-  196,182,188,128,108,139,107,179,240,181,211,199,46,187,160,56,111,226,58,103,99,80,115,58,217,145,179,224,66,103,122,8,
-  61,243,214,229,176,186,184,94,35,154,153,164,94,111,96,180,37,244,3,16,6,63,244,73,227,6,105,146,50,51,17,51,
-  63,55,75,165,90,38,142,35,250,250,122,233,239,239,115,189,149,108,254,44,139,177,22,157,38,88,19,101,214,35,210,165,
-  117,24,133,145,21,164,148,24,43,49,198,98,237,188,3,186,149,36,206,250,17,37,133,99,109,43,133,47,125,58,218,58,
-  0,232,22,2,213,43,241,215,120,8,225,48,56,163,45,245,70,131,185,114,217,223,123,120,212,63,188,255,88,251,103,31,
-  120,108,32,124,242,233,85,255,173,82,169,97,205,182,1,56,177,226,89,70,49,239,71,149,74,111,127,8,198,62,218,66,
-  62,208,108,89,51,207,209,169,54,82,157,240,222,207,222,198,232,145,35,167,241,79,158,75,78,195,216,193,57,102,254,247,
-  253,116,221,58,194,145,216,176,118,67,59,81,79,248,109,38,225,69,88,83,209,227,158,247,238,227,45,110,19,212,158,194,
-  114,17,120,189,47,122,221,203,212,196,137,35,116,119,22,104,235,233,70,8,73,16,248,228,11,69,180,214,40,229,147,106,
-  23,195,172,178,14,193,96,16,158,160,179,189,13,165,36,183,222,114,11,161,95,64,11,133,205,6,122,223,243,201,249,65,
-  43,149,52,8,3,80,1,214,24,76,220,192,15,2,226,70,29,109,36,90,120,24,98,44,134,90,101,150,198,241,132,161,
-  165,171,89,181,118,41,171,86,172,70,235,6,163,19,163,28,218,55,198,87,239,126,136,7,31,222,77,106,50,127,245,108,
-  115,155,89,136,240,23,25,173,225,223,64,118,74,87,124,48,246,21,102,227,218,107,206,255,204,7,151,245,110,216,168,76,
-  92,193,19,57,119,36,68,218,210,120,157,185,144,90,40,62,226,204,170,115,218,96,109,23,173,226,77,179,81,90,228,241,
-  35,190,53,144,116,218,182,235,204,247,114,147,213,124,214,81,254,76,230,245,25,203,108,173,53,243,115,179,152,114,25,139,
-  98,126,174,78,185,50,1,194,80,173,206,81,174,76,18,71,17,129,234,164,171,171,157,129,254,126,218,218,138,72,153,117,
-  70,38,33,77,45,36,29,8,153,96,101,156,193,82,11,59,41,119,191,55,40,105,48,40,172,245,17,158,1,34,103,134,
-  111,3,60,147,45,44,172,192,147,18,155,166,136,108,27,39,132,192,170,4,29,107,76,170,144,66,160,13,72,233,33,164,
-  164,191,167,151,21,195,195,228,110,189,149,187,174,189,150,143,253,228,47,10,83,54,162,238,249,202,19,66,182,37,241,217,
-  237,56,188,192,249,57,252,103,125,24,235,66,248,82,173,249,192,23,110,99,255,145,35,63,80,254,78,45,214,200,175,28,
-  101,223,87,142,242,49,224,87,87,149,88,213,29,224,125,135,5,8,37,184,254,64,5,11,109,17,108,0,46,232,186,233,
-  170,193,145,139,207,147,113,35,98,114,124,140,84,8,186,186,7,200,181,117,208,214,222,197,216,232,56,35,75,70,72,227,
-  6,81,20,145,234,148,142,174,110,242,97,59,237,165,46,230,171,53,62,247,229,111,208,221,185,151,180,222,160,94,143,64,
-  74,130,66,30,229,41,194,48,164,45,151,167,179,179,157,68,167,228,139,5,218,74,69,138,249,60,197,66,145,238,174,46,
-  60,9,90,71,72,153,18,228,44,197,98,14,52,76,143,141,211,63,146,107,165,57,12,14,141,176,100,201,74,116,3,54,
-  156,191,137,47,118,22,115,15,190,243,67,171,207,171,214,174,242,224,104,8,179,95,132,185,50,240,49,129,236,245,101,255,
-  116,106,175,16,198,190,162,177,105,205,117,151,125,252,253,75,187,55,92,40,83,51,139,240,3,164,86,32,26,88,97,90,
-  68,189,103,52,55,77,173,150,149,173,228,136,133,235,126,209,72,181,88,166,145,117,54,38,43,26,226,140,17,204,158,225,
-  233,179,184,8,157,205,60,236,89,73,140,103,233,150,78,251,183,214,221,48,186,251,250,72,219,114,164,137,100,223,177,157,
-  124,226,195,159,68,199,13,222,252,230,215,177,106,233,74,2,31,60,63,135,16,160,83,141,78,235,88,233,54,124,86,11,
-  39,94,85,83,46,128,205,10,44,10,139,2,180,43,58,210,2,1,22,15,99,99,132,108,128,245,17,228,240,164,205,190,
-  22,46,104,213,178,96,75,99,12,82,72,180,53,72,17,34,164,235,55,53,218,33,211,153,191,184,240,45,211,26,190,242,
-  129,79,113,232,95,62,208,120,203,169,169,173,5,120,172,34,228,169,113,163,245,209,179,148,246,245,128,55,113,4,122,151,
-  130,209,63,26,183,193,31,104,151,5,4,158,59,9,239,255,236,103,56,114,226,200,15,252,119,202,69,7,58,0,142,29,
-  172,112,240,96,139,215,243,29,109,191,2,240,115,176,92,193,181,243,249,240,226,117,63,251,230,110,161,192,39,193,183,30,
-  181,83,167,40,4,57,114,237,29,108,222,178,133,251,238,189,135,250,241,83,248,74,224,133,62,29,221,61,116,13,47,161,
-  212,214,131,151,239,225,227,255,248,143,124,241,142,123,92,23,147,36,8,33,9,115,1,133,66,1,129,32,159,203,19,248,
-  10,172,161,82,174,210,213,221,73,62,95,160,171,171,135,174,174,46,70,70,70,104,47,21,9,67,143,36,137,72,211,136,
-  92,33,199,208,224,32,29,157,93,172,169,230,88,190,102,53,42,231,131,174,97,162,8,43,37,235,54,46,97,253,95,255,
-  145,188,239,134,235,134,143,252,247,223,186,98,213,161,227,187,18,216,185,18,230,254,28,100,81,138,254,138,229,10,105,236,
-  203,103,214,175,188,238,202,207,125,100,121,247,234,117,152,120,14,165,50,110,143,48,217,152,37,159,209,172,159,222,9,101,
-  209,128,173,137,71,156,177,164,18,103,124,77,46,98,173,216,22,31,200,46,96,194,173,49,236,76,133,215,217,138,82,211,
-  235,199,88,123,58,23,168,201,206,166,153,122,154,21,176,22,43,209,25,138,161,36,126,177,11,207,192,11,110,186,150,203,
-  175,125,1,79,111,219,202,201,163,71,153,173,86,88,190,116,8,33,12,70,155,140,116,154,249,159,73,137,149,25,243,219,
-  164,46,138,71,8,82,157,96,173,197,243,36,58,115,4,240,60,39,136,85,66,161,173,197,26,131,20,22,41,36,18,31,
-  35,220,88,230,150,112,205,110,83,162,17,88,233,82,86,180,141,64,198,40,233,226,180,149,108,35,159,239,230,169,253,199,
-  248,216,187,255,129,238,47,126,117,242,102,109,158,204,195,93,26,238,233,78,162,99,69,72,54,61,219,212,240,197,127,50,
-  108,185,85,112,254,13,130,164,241,159,167,8,89,32,231,43,106,145,225,215,254,246,171,28,57,49,246,67,127,14,205,4,
-  207,37,192,123,61,201,197,61,57,60,251,173,187,181,82,78,209,85,142,131,198,108,180,188,2,91,218,126,230,245,171,122,
-  47,63,223,75,102,38,65,130,18,18,171,83,230,167,38,9,11,5,122,6,7,121,205,143,191,17,226,136,204,113,222,141,
-  24,70,32,85,129,147,71,143,241,158,127,125,31,249,92,1,109,44,54,116,54,238,97,24,80,200,135,180,149,74,228,195,
-  28,190,239,49,95,158,99,249,242,141,116,119,119,211,104,68,88,11,245,122,141,253,251,247,209,211,211,203,208,208,32,109,
-  109,109,116,116,245,99,45,76,76,213,153,156,170,113,240,192,9,252,251,190,201,170,117,43,216,180,105,21,253,131,195,96,
-  12,169,174,33,76,204,181,55,191,146,167,254,199,193,254,147,191,249,123,75,3,232,172,64,110,8,122,99,195,21,70,155,
-  151,77,175,92,118,205,165,183,125,100,217,192,234,13,232,164,150,225,25,11,70,239,54,51,18,179,226,153,91,46,123,26,
-  5,206,62,203,46,115,113,78,215,194,184,214,210,128,101,69,200,62,99,228,90,92,184,236,194,38,236,89,116,95,205,63,
-  159,46,209,176,103,248,155,137,69,100,200,38,118,212,44,122,210,209,38,210,24,233,9,206,187,228,82,54,108,58,135,71,
-  30,124,136,173,59,246,48,52,208,198,178,101,75,65,27,140,78,81,153,190,204,100,111,30,65,224,2,8,108,83,162,225,
-  88,214,70,91,135,235,89,147,21,70,137,18,62,40,87,40,181,209,174,32,10,139,20,214,141,240,38,205,206,65,22,92,
-  153,141,245,158,113,131,180,47,115,228,138,93,236,59,122,138,79,125,226,227,76,124,246,206,198,117,19,83,135,54,195,55,
-  35,196,189,6,30,182,216,99,159,132,250,215,112,132,211,51,81,223,59,1,207,11,225,241,47,89,172,129,11,110,250,207,
-  81,132,154,197,39,74,52,175,253,253,251,185,235,209,177,31,217,115,17,56,191,149,39,82,195,223,157,170,125,219,39,190,
-  186,232,241,33,95,73,1,5,81,104,235,92,245,154,151,21,66,155,160,149,79,67,164,88,235,238,70,42,244,93,116,142,
-  54,8,153,137,112,173,6,227,236,163,132,244,104,212,26,252,217,31,255,9,211,83,243,25,224,232,86,183,74,42,10,197,
-  2,237,165,18,197,66,129,48,8,176,6,242,185,18,82,122,36,137,97,217,178,229,244,244,244,162,148,34,77,19,164,10,
-  8,130,144,66,62,143,151,145,15,131,48,68,74,137,167,20,137,182,236,223,115,152,93,79,109,103,205,154,117,92,117,237,
-  53,20,59,219,73,227,42,96,56,122,236,168,144,224,75,40,25,88,171,225,82,99,237,205,135,206,93,125,237,77,183,125,
-  164,127,233,186,115,176,105,109,209,78,235,116,132,199,32,90,23,108,243,179,64,60,163,112,60,91,186,197,89,87,235,54,
-  235,128,132,57,115,239,213,234,100,68,115,164,177,218,145,15,197,162,228,138,179,128,211,207,64,144,172,104,241,121,206,70,
-  108,180,103,254,173,89,192,44,152,180,129,31,6,188,224,198,27,168,204,76,179,111,223,14,14,31,31,37,80,138,37,67,
-  131,46,5,181,105,105,111,116,134,217,184,191,43,33,49,194,18,71,177,11,160,12,36,198,24,148,82,173,231,178,160,204,
-  23,72,97,220,22,12,135,43,45,144,61,101,11,196,51,50,165,224,231,105,203,117,113,104,106,142,79,127,232,147,236,252,
-  228,109,233,198,99,167,38,95,1,219,242,240,64,3,30,76,176,59,45,76,142,65,250,55,217,43,219,250,108,29,144,16,
-  224,133,240,196,157,238,80,252,71,47,66,139,139,207,171,255,215,55,184,251,241,241,231,197,115,138,128,53,22,46,195,185,
-  200,61,235,8,86,77,169,147,226,131,208,190,146,182,171,72,152,43,161,181,34,17,18,41,36,133,82,27,197,174,46,12,
-  2,161,156,233,150,149,2,76,118,113,74,15,33,139,252,223,191,250,99,190,246,213,123,41,22,219,169,79,79,35,84,118,
-  199,204,238,132,82,74,10,133,2,82,72,76,106,89,187,102,57,3,3,3,228,11,121,164,20,164,105,74,20,53,0,40,
-  22,115,88,43,72,82,237,18,208,133,179,44,73,18,67,181,218,32,200,21,40,22,187,8,59,187,56,114,248,4,135,143,
-  126,140,235,111,186,142,53,27,86,115,228,232,97,182,125,233,75,141,107,161,145,192,144,148,226,74,140,189,113,199,138,161,
-  11,174,251,224,59,123,151,174,91,139,73,235,136,51,182,86,139,35,142,229,194,226,170,213,251,156,214,113,156,134,241,156,
-  93,169,116,186,233,87,51,53,116,17,6,100,79,239,169,16,34,43,68,153,145,153,213,167,249,61,139,51,87,237,167,133,
-  197,219,103,84,25,113,166,6,77,136,211,10,174,177,22,217,180,4,201,248,78,214,106,48,150,82,103,59,23,94,122,21,
-  229,185,89,158,124,236,17,158,124,106,39,171,151,47,167,187,179,205,69,47,25,247,111,77,86,52,173,176,167,61,133,36,
-  77,241,148,122,70,113,182,214,180,70,86,105,2,172,0,109,53,40,137,49,41,82,8,164,20,228,253,128,92,208,203,225,
-  137,25,62,246,217,207,240,244,71,63,155,172,218,119,96,236,23,96,127,39,60,213,128,135,53,60,81,128,19,171,161,250,
-  105,119,180,190,163,197,13,255,89,138,208,243,177,248,156,129,237,240,30,224,167,127,250,28,254,249,68,133,79,127,249,232,
-  179,118,77,18,40,207,207,113,108,114,138,145,13,27,8,81,228,186,186,240,61,31,161,20,86,184,145,196,98,91,156,22,
-  132,194,104,139,231,151,184,255,158,123,249,220,103,110,167,175,119,144,169,233,153,86,212,142,177,6,41,37,249,124,158,70,
-  212,160,92,46,211,213,217,201,242,85,43,25,26,30,65,41,133,214,6,79,73,180,214,88,4,129,31,16,167,41,129,167,
-  72,180,211,34,133,65,224,198,34,9,243,149,57,162,169,41,124,21,16,4,138,193,193,62,180,213,124,254,115,183,115,197,
-  216,69,28,63,126,136,244,224,177,134,15,121,3,91,164,177,151,110,239,41,157,123,225,251,254,161,103,243,133,87,97,226,
-  8,35,125,20,49,173,128,10,177,136,216,144,137,106,91,113,196,44,26,139,22,37,139,158,141,168,124,118,236,198,44,152,
-  248,44,170,23,130,166,188,99,49,190,100,91,106,249,211,126,214,179,36,92,156,253,132,202,236,215,125,235,228,139,211,210,
-  80,155,5,106,145,135,144,177,150,182,246,78,174,185,225,197,156,56,124,128,29,79,62,70,173,86,101,96,176,15,169,124,
-  172,78,156,187,163,116,157,139,196,137,117,141,160,101,51,219,244,36,58,19,104,151,82,160,132,201,18,83,156,216,213,243,
-  218,200,231,138,196,218,112,224,224,17,30,189,253,54,115,244,246,59,162,129,227,99,227,111,128,61,109,240,84,140,216,170,
-  177,187,12,28,59,9,51,47,1,93,248,46,232,45,222,226,19,246,31,185,8,89,32,23,40,162,248,249,87,124,22,23,
-  161,16,184,126,77,7,87,252,220,70,110,89,219,193,7,255,113,123,139,85,157,224,108,75,11,2,147,130,198,218,116,207,
-  99,123,184,234,134,155,17,74,33,132,114,162,200,236,205,216,244,45,151,44,164,58,120,97,59,123,119,238,224,207,255,244,
-  47,73,34,77,20,149,169,71,141,76,81,237,70,135,56,142,137,26,17,197,66,158,217,217,89,134,6,7,89,189,122,53,
-  141,200,225,7,82,9,132,114,129,119,66,42,16,130,124,24,56,193,100,26,187,205,145,213,132,228,240,60,143,158,238,110,
-  166,166,103,104,52,34,246,238,61,200,206,157,154,205,231,111,102,233,210,21,60,245,212,46,190,126,199,109,220,34,17,90,
-  179,66,193,154,93,133,112,237,198,127,249,63,93,47,184,238,70,97,235,17,66,129,146,198,189,182,197,235,110,177,144,154,
-  213,74,156,104,74,48,90,235,47,113,70,247,97,22,9,65,57,109,252,18,139,198,161,179,93,33,11,0,180,131,36,68,
-  51,135,203,90,215,41,44,170,116,14,12,62,163,152,136,51,54,110,167,81,240,237,183,125,255,58,185,140,125,134,98,191,
-  181,224,104,254,44,157,50,178,98,53,3,67,67,236,222,241,20,7,143,29,99,184,127,128,66,224,101,231,72,103,218,56,
-  155,253,59,183,26,73,146,52,27,197,221,77,168,249,209,122,89,158,70,201,128,92,88,66,19,80,158,139,120,242,129,135,
-  217,245,239,95,76,204,35,143,151,71,102,231,38,46,132,189,121,216,26,193,182,58,98,143,192,158,28,133,242,11,32,249,
-  213,246,46,78,116,13,210,35,37,187,231,103,88,89,153,65,68,141,51,209,181,179,23,160,211,138,208,151,93,21,61,255,
-  122,65,18,61,255,139,80,171,243,137,29,230,243,124,44,62,139,31,73,61,197,11,37,111,186,164,159,6,112,17,206,119,
-  163,153,52,80,21,66,251,82,76,247,167,230,216,206,219,190,56,63,251,203,63,219,222,81,116,209,58,153,41,51,11,216,
-  165,27,208,173,1,233,121,76,141,141,242,119,255,247,239,153,24,159,33,151,203,51,53,51,77,106,82,87,160,76,243,46,
-  168,80,158,66,42,247,230,27,24,24,68,41,73,16,40,146,36,117,108,0,169,8,139,33,90,55,215,213,58,99,201,186,
-  255,102,141,197,106,139,198,141,114,189,61,221,204,206,206,49,56,56,192,142,157,219,184,247,190,123,88,182,108,13,155,206,
-  217,64,190,161,77,91,66,65,194,186,157,146,96,201,219,255,184,235,186,87,191,65,17,213,48,126,8,178,140,178,153,6,
-  158,179,103,168,59,178,176,205,152,68,89,1,70,184,162,156,145,17,207,228,215,44,12,84,153,207,134,205,112,35,97,207,
-  88,179,139,69,225,128,205,95,103,22,105,195,154,155,173,133,84,141,69,102,138,11,237,203,25,124,159,211,177,44,193,89,
-  85,206,139,190,195,216,102,176,32,173,32,196,133,232,101,50,239,203,108,28,212,110,175,122,238,150,203,232,29,26,100,235,
-  163,143,210,219,86,160,187,187,27,116,236,54,174,210,224,206,16,45,240,185,169,133,107,210,15,22,176,49,197,252,124,72,
-  181,86,101,108,239,54,83,126,106,135,174,60,242,68,213,223,181,127,236,2,99,143,117,192,9,224,64,2,59,234,206,184,
-  96,172,13,91,126,0,244,23,195,2,83,195,107,197,230,193,1,117,56,78,210,131,198,144,235,236,229,64,154,176,244,200,
-  211,36,90,63,171,168,215,59,27,223,202,11,224,241,59,50,96,250,198,231,119,17,122,62,143,93,223,242,121,27,203,124,
-  93,179,29,24,233,207,51,244,91,231,19,76,69,148,254,113,7,179,149,36,78,60,113,40,128,71,151,60,250,228,250,237,
-  159,252,218,5,87,255,212,155,149,17,211,8,225,2,121,164,112,111,71,163,53,202,186,214,186,50,59,207,7,223,251,126,
-  118,237,216,69,28,69,8,145,32,36,36,105,236,52,142,40,60,207,157,242,40,138,16,88,186,187,58,25,25,25,161,17,
-  213,17,66,226,251,30,105,170,73,146,132,102,250,152,203,145,119,22,31,82,186,11,192,24,67,163,17,83,44,134,24,13,
-  82,105,122,186,187,9,131,60,66,88,246,31,218,203,232,201,113,102,166,102,233,152,153,211,61,16,110,131,92,254,207,126,
-  39,184,229,231,127,73,88,61,143,246,173,179,165,208,165,140,163,96,22,21,133,179,24,132,53,49,27,187,80,114,172,93,
-  232,140,22,219,170,158,54,102,137,38,85,81,180,144,102,113,154,48,93,156,54,146,185,159,233,10,123,26,55,176,70,163,
-  60,31,225,133,153,98,158,211,114,220,23,35,207,86,44,162,1,180,86,238,238,247,138,111,119,13,157,153,174,209,98,124,
-  187,215,43,132,187,38,13,22,132,143,240,21,198,90,6,135,151,115,221,139,122,120,236,129,251,56,120,232,48,43,150,47,
-  67,90,131,78,154,192,57,24,235,50,227,23,119,108,98,17,160,142,128,219,190,248,37,238,255,200,199,245,155,70,103,234,
-  75,97,34,132,109,30,60,154,192,46,13,39,4,76,24,152,150,80,233,117,170,119,198,128,13,61,35,28,237,25,22,210,
-  198,166,45,204,228,170,64,136,207,79,159,119,233,183,165,173,240,108,69,232,137,47,91,182,222,101,241,195,111,107,252,246,
-  95,197,231,251,120,148,218,124,46,188,117,41,23,254,252,6,30,186,114,128,183,129,169,106,59,38,148,120,172,31,182,237,
-  254,195,63,153,62,180,103,39,82,228,49,153,49,149,37,197,24,231,88,100,165,162,49,55,199,71,223,243,30,226,90,68,
-  169,212,65,165,214,32,54,134,84,27,124,207,71,41,15,164,32,213,174,147,73,163,132,98,190,68,79,103,47,7,247,31,
-  68,9,69,224,5,8,43,240,164,162,144,203,103,120,129,115,220,83,158,36,181,9,81,26,97,208,104,171,209,104,82,82,
-  240,140,243,32,178,146,92,46,164,191,127,128,165,35,203,81,184,0,196,113,66,241,69,48,252,194,235,188,55,252,206,111,
-  8,146,170,243,50,178,30,210,26,164,204,66,126,229,226,150,66,186,213,176,84,8,225,33,240,129,16,107,189,150,105,152,
-  108,117,42,58,139,220,177,153,69,109,19,59,106,154,218,122,24,60,180,144,24,164,91,231,11,201,233,213,40,179,226,192,
-  229,198,91,33,17,82,144,70,117,202,19,167,152,31,27,165,50,126,146,104,242,20,233,236,12,186,86,197,54,195,250,172,
-  205,46,186,208,197,22,101,161,197,86,200,5,172,78,200,236,87,44,40,231,91,133,192,178,224,55,36,178,96,78,43,90,
-  126,207,2,167,231,50,210,71,120,190,27,155,76,132,213,117,210,198,28,141,218,4,158,109,112,197,37,151,81,204,151,120,
-  252,209,173,212,27,41,126,88,112,63,35,43,232,74,137,236,6,34,90,36,204,230,50,194,26,205,155,127,242,21,252,194,
-  255,252,25,188,82,206,134,16,9,24,77,224,113,3,247,43,120,226,48,28,30,132,185,17,208,62,139,12,233,173,37,112,
-  218,170,103,232,171,82,99,158,245,227,172,29,208,217,138,16,60,255,58,161,255,44,197,71,2,149,147,85,118,254,212,189,
-  116,92,220,199,69,219,166,248,6,144,51,214,8,37,231,45,118,98,201,177,195,51,31,253,173,183,182,255,247,15,189,55,
-  236,110,47,98,90,35,8,40,1,149,249,89,190,248,233,219,104,43,150,8,194,144,125,251,15,80,40,148,90,249,214,190,
-  23,16,37,49,82,10,124,207,167,45,95,96,227,134,141,220,244,162,155,40,21,139,28,58,112,144,167,119,238,98,104,104,
-  152,222,222,94,226,36,65,74,141,82,210,249,14,103,23,180,84,206,129,49,73,157,57,173,82,30,86,24,18,163,9,189,
-  0,99,52,74,41,58,58,58,73,211,148,106,165,202,204,244,36,199,144,94,233,77,175,84,191,247,215,127,156,217,136,153,
-  204,144,75,101,0,111,102,237,102,4,167,231,172,187,24,229,197,227,202,226,116,27,219,180,171,48,77,252,102,241,154,91,
-  60,99,196,113,205,136,229,76,97,168,205,112,35,97,89,180,165,202,222,99,133,54,124,47,164,49,63,79,189,58,75,195,
-  90,60,207,119,5,48,12,145,126,128,244,60,60,47,64,249,185,204,175,91,101,171,109,88,112,247,182,78,203,150,61,167,
-  166,62,94,176,32,1,81,205,239,21,22,132,6,145,96,77,226,182,97,90,211,168,167,36,141,8,157,38,160,83,116,154,
-  160,20,40,225,196,201,198,8,206,219,188,153,142,82,59,15,62,248,16,23,92,176,153,174,238,78,82,157,58,75,143,172,
-  35,115,55,22,78,103,61,91,139,77,234,92,243,202,91,213,238,37,75,75,91,255,226,29,35,107,118,236,223,220,1,7,
-  19,56,30,194,252,17,72,255,12,231,16,250,19,56,83,57,128,224,251,232,78,188,111,211,17,62,47,139,208,127,4,192,
-  249,91,22,157,156,135,40,249,168,188,231,200,138,117,205,204,35,19,236,124,100,66,108,132,224,103,160,24,91,150,234,212,
-  92,154,131,181,39,160,120,255,254,125,234,186,167,119,112,213,21,87,161,117,234,140,57,37,212,231,102,121,232,222,123,200,
-  133,1,231,157,119,9,191,244,171,191,157,141,88,13,18,157,32,4,164,169,118,126,49,64,71,169,141,43,175,188,138,203,
-  47,191,12,33,36,213,106,157,161,145,37,244,246,246,243,212,182,167,16,66,48,60,50,66,154,166,232,38,111,196,146,233,
-  205,92,247,97,140,193,247,125,247,166,213,6,229,123,153,133,105,198,164,149,130,92,46,207,192,96,63,213,234,44,155,54,
-  175,229,111,222,241,215,162,173,61,143,77,51,210,27,11,192,110,19,225,104,25,58,103,238,132,153,68,124,209,90,62,89,
-  4,183,152,86,113,49,42,115,17,68,182,210,39,220,159,23,214,241,130,133,173,90,211,155,199,102,27,42,87,188,52,146,
-  5,162,162,200,254,134,12,80,133,28,197,48,135,95,244,153,154,156,194,88,67,33,204,19,71,17,38,106,184,130,128,2,
-  2,60,79,162,60,137,242,37,158,167,50,16,223,98,140,64,72,31,229,185,104,63,33,165,219,236,53,253,160,141,37,19,
-  108,145,38,17,113,28,147,164,13,162,168,130,213,105,182,197,116,60,48,79,228,8,252,2,161,151,35,110,104,166,166,102,
-  152,157,155,102,174,54,65,84,143,178,85,190,102,199,142,237,92,121,213,21,173,232,105,41,188,214,117,189,32,204,93,136,
-  10,50,22,26,213,26,231,92,124,161,232,123,207,219,219,238,251,179,183,95,60,242,249,187,109,31,80,131,59,95,10,251,
-  246,67,244,1,92,142,88,9,231,2,177,49,137,184,81,72,106,194,146,96,159,219,88,158,197,69,200,26,184,240,69,63,
-  218,34,212,44,62,245,72,243,218,223,123,254,23,159,179,105,254,199,222,191,139,173,119,28,166,109,46,230,10,231,129,227,
-  165,144,31,128,238,105,24,206,195,106,15,206,155,131,139,239,246,212,57,201,27,95,218,253,143,127,244,251,106,245,178,149,
-  24,147,56,113,32,150,168,90,225,169,199,30,69,38,49,47,184,230,26,62,245,233,59,56,114,228,56,202,47,160,211,20,
-  165,36,105,154,98,173,37,77,52,109,197,60,151,92,114,49,87,94,121,5,214,66,154,26,108,38,84,244,130,128,11,183,
-  92,196,222,189,123,57,124,248,48,43,86,174,196,36,177,3,174,165,192,195,203,88,183,238,46,154,166,105,182,69,81,206,
-  151,90,138,140,105,11,105,10,81,20,227,123,138,246,206,18,191,246,214,95,163,163,179,3,155,214,79,67,102,69,203,112,
-  43,251,63,35,51,112,52,91,23,55,85,18,139,41,130,217,158,222,54,67,129,155,104,188,144,8,171,78,147,97,156,222,
-  1,217,204,59,121,193,176,127,161,11,50,89,244,241,98,31,48,231,182,108,179,11,19,25,16,148,250,233,180,121,30,248,
-  198,215,137,170,53,206,219,120,14,165,66,158,188,23,146,154,132,88,87,73,106,154,200,90,132,84,153,255,15,72,169,48,
-  169,118,91,69,213,202,115,59,29,196,182,26,157,197,30,88,107,49,198,253,59,87,44,148,243,254,17,1,141,40,98,174,
-  58,195,244,244,33,198,70,79,186,227,236,251,116,119,119,209,213,217,73,208,231,152,238,107,215,175,33,77,83,234,245,26,
-  129,159,253,62,165,178,209,107,1,220,151,82,34,68,182,17,203,10,118,163,94,161,191,163,141,151,255,213,219,114,183,231,
-  131,75,249,196,157,178,31,152,150,66,189,205,216,189,1,212,255,21,23,48,80,1,142,77,28,197,122,62,151,44,93,70,
-  49,151,107,117,231,205,71,156,234,239,189,0,45,46,66,79,126,197,109,199,126,84,43,122,55,118,73,162,72,243,154,255,
-  245,13,238,121,226,249,91,124,154,50,12,223,157,36,241,83,174,169,102,7,200,198,190,89,191,109,223,172,15,228,87,65,
-  167,134,190,192,37,63,172,50,176,110,10,86,31,47,230,151,151,175,187,112,96,203,127,251,153,224,69,47,127,25,97,32,
-  51,191,98,139,20,6,29,215,217,189,253,41,26,213,42,231,158,179,129,201,137,105,62,251,185,59,220,77,52,78,29,153,
-  76,90,180,78,17,66,225,41,197,198,13,27,185,225,134,27,72,146,20,157,9,13,201,124,117,76,166,13,218,176,97,3,
-  39,78,156,96,122,122,138,246,142,14,146,36,193,243,188,86,209,81,74,225,123,62,22,75,24,230,78,187,147,42,41,73,
-  83,157,17,24,107,76,207,76,240,147,111,121,35,107,214,175,194,196,53,7,160,47,6,123,79,91,90,57,194,159,148,106,
-  1,111,22,103,168,220,173,92,100,163,145,97,44,52,173,80,23,143,89,139,137,118,180,182,88,66,184,49,47,91,136,45,
-  116,69,214,100,79,193,169,188,155,58,168,38,171,24,229,64,92,99,60,194,98,39,87,95,247,34,62,127,219,109,188,227,
-  159,222,197,213,87,92,134,77,18,74,109,5,186,251,122,233,236,234,34,151,207,101,5,195,98,181,198,88,75,16,8,68,
-  170,177,54,117,4,71,99,92,184,99,166,162,183,139,214,248,70,184,174,211,104,65,212,72,152,153,157,97,98,124,130,180,
-  150,16,199,117,188,64,82,106,47,177,116,168,159,174,238,118,194,156,27,249,82,29,56,177,170,214,164,105,140,64,100,57,
-  242,142,211,149,218,20,223,247,179,162,227,116,97,14,71,116,12,121,79,10,82,99,80,158,79,172,99,66,47,228,250,223,
-  249,149,240,35,19,99,151,108,185,123,43,61,198,114,80,96,126,205,178,175,2,141,71,112,38,124,6,248,234,232,1,182,
-  206,141,211,155,47,160,179,194,96,172,37,244,20,47,60,127,133,99,219,159,165,96,124,199,134,100,63,106,158,80,171,248,
-  196,134,87,61,143,139,143,205,10,207,16,112,37,120,69,232,152,128,174,45,16,10,144,167,32,167,160,87,66,183,20,244,
-  24,203,176,133,37,51,176,108,174,32,135,14,175,236,239,111,187,225,230,210,85,175,127,179,220,124,233,133,4,94,1,155,
-  156,194,164,22,43,2,84,230,5,124,96,247,110,162,74,153,101,75,151,225,249,57,30,120,232,235,28,63,57,138,31,230,
-  136,35,131,49,154,36,137,81,217,93,111,201,146,165,188,228,37,47,67,8,137,214,6,41,21,82,42,226,56,113,155,21,
-  107,144,74,18,197,17,253,3,253,76,76,78,50,57,57,73,119,119,55,58,91,163,54,127,22,214,226,7,65,246,51,220,
-  155,27,4,105,170,1,131,54,49,213,250,60,55,220,124,61,151,92,117,25,38,174,184,81,71,102,106,245,236,130,63,29,
-  58,56,195,102,35,179,134,16,173,113,141,108,3,40,22,81,110,154,32,242,217,155,254,211,229,23,217,103,105,155,200,146,
-  163,52,160,145,210,100,18,23,223,121,130,139,69,72,141,176,173,142,200,8,141,176,150,32,231,243,186,183,188,145,77,155,
-  214,50,126,242,56,133,192,99,226,212,56,99,123,198,17,66,146,207,23,80,50,160,84,106,163,171,187,7,41,37,81,84,
-  35,12,36,133,98,17,233,41,167,50,207,70,48,79,9,7,196,167,138,153,185,89,198,199,199,49,22,38,38,198,25,27,
-  27,5,97,89,58,50,204,234,21,195,20,139,75,16,170,9,44,187,206,197,24,137,181,6,99,146,214,177,116,161,149,174,
-  139,107,18,16,83,215,250,180,58,85,215,1,137,204,192,222,44,12,79,214,245,237,81,220,160,51,95,224,53,127,250,7,
-  193,39,254,215,219,46,185,252,222,237,116,91,24,21,232,255,105,217,63,0,241,57,110,60,3,96,162,86,97,162,86,57,
-  237,28,228,124,197,21,254,48,42,239,60,134,190,231,2,244,163,44,66,22,200,7,146,70,244,252,46,62,38,43,60,123,
-  64,14,66,113,37,44,77,224,252,60,108,106,64,151,113,240,100,62,133,190,26,116,54,160,189,177,172,191,39,89,190,188,
-  45,190,96,93,225,220,87,190,70,94,125,238,58,70,250,7,92,102,130,174,145,234,4,41,2,16,26,33,52,73,189,194,
-  145,189,187,153,61,53,78,127,223,0,105,106,40,215,98,238,252,202,61,68,137,110,145,118,149,244,48,104,164,112,42,242,
-  107,174,185,150,158,238,62,170,213,42,74,121,45,224,209,243,92,17,106,94,228,72,137,54,134,158,158,30,198,198,198,208,
-  90,227,251,62,113,28,103,186,176,212,141,6,8,148,148,142,187,34,5,214,100,133,201,151,204,204,205,115,209,197,231,113,
-  243,173,55,162,147,154,219,250,100,119,221,103,51,142,63,19,57,88,144,88,44,30,85,78,231,250,180,92,126,196,194,144,
-  181,96,193,209,140,10,48,173,177,102,1,164,182,8,227,10,101,101,118,154,156,47,241,252,16,25,128,80,138,211,242,115,
-  178,223,34,113,199,95,120,25,39,74,39,156,179,229,2,84,8,147,227,99,108,188,96,51,36,142,115,19,71,41,213,106,
-  149,249,249,10,251,247,63,77,181,86,165,86,109,96,109,64,106,52,65,62,231,214,212,97,64,62,23,162,68,70,50,52,
-  144,36,17,66,64,177,173,64,161,144,99,227,57,107,232,235,235,166,88,204,163,141,161,158,38,78,63,5,72,233,35,164,
-  69,72,183,217,84,173,132,215,172,208,24,155,141,113,162,213,145,25,52,86,187,142,71,74,153,121,253,100,99,104,182,62,
-  151,6,12,26,172,32,214,13,250,187,186,120,205,159,254,126,112,199,111,191,109,203,197,15,238,138,11,150,114,44,168,236,
-  183,28,95,147,221,42,158,205,95,171,22,120,217,120,44,206,74,67,248,174,45,89,127,216,69,168,89,124,106,13,205,107,
-  126,239,254,231,101,241,105,190,244,113,16,47,133,48,129,193,58,156,155,131,203,27,112,201,231,61,54,219,13,203,58,54,
-  109,88,139,10,115,178,109,176,223,43,14,244,169,225,213,43,68,247,134,245,44,93,185,134,82,190,23,80,36,204,128,174,
-  96,117,136,81,138,84,54,240,76,17,37,60,180,46,51,57,54,74,84,157,163,167,173,136,137,99,194,98,23,59,118,238,
-  99,215,222,131,72,233,99,18,231,100,40,148,69,38,144,232,148,11,46,184,136,145,165,203,152,171,84,156,130,218,44,208,
-  242,141,113,178,11,132,112,64,179,204,86,199,214,50,56,56,232,248,66,66,180,64,103,165,36,113,156,16,248,97,166,145,
-  106,70,12,91,2,63,68,155,26,221,189,93,188,248,101,183,32,133,37,205,182,46,205,78,166,181,133,130,69,110,132,11,
-  43,240,197,199,244,116,165,148,93,64,212,178,191,54,234,13,140,73,241,2,133,239,231,51,69,183,201,138,141,62,61,4,
-  80,184,146,37,22,17,21,149,144,8,147,80,158,42,35,165,68,169,60,97,91,9,145,203,163,194,124,11,204,118,227,161,
-  68,216,180,213,137,89,107,209,169,97,253,230,75,240,194,93,140,31,59,196,146,158,54,71,233,203,41,242,249,78,250,250,
-  186,193,46,37,77,99,210,52,65,199,16,197,17,73,154,146,36,49,198,106,226,56,194,87,110,52,245,51,87,130,32,116,
-  254,74,190,231,57,145,177,49,164,73,138,167,4,158,231,103,128,52,40,105,157,32,213,64,170,117,70,207,200,172,52,76,
-  179,112,103,93,163,176,142,74,145,166,89,23,156,58,199,4,41,51,247,129,12,7,202,60,177,155,61,160,198,16,39,49,
-  3,125,3,220,248,231,127,152,123,248,87,254,191,139,46,216,126,120,202,90,38,58,160,254,183,48,121,5,216,194,179,92,
-  27,255,62,210,205,231,66,159,103,51,5,253,158,60,161,23,23,33,107,224,194,155,127,48,69,104,113,241,121,229,239,126,
-  131,175,111,61,245,188,235,120,74,56,141,215,227,16,52,160,91,194,234,28,92,106,224,170,195,176,121,199,133,231,46,217,
-  244,171,63,81,120,217,173,47,99,160,103,192,129,182,162,105,146,41,1,141,38,198,152,121,140,54,72,5,82,134,46,251,
-  93,128,103,4,216,8,140,97,110,252,56,181,242,20,109,197,54,172,129,212,104,144,130,47,221,249,32,243,243,49,97,62,
-  135,240,193,232,4,147,26,148,231,81,44,229,185,224,188,243,169,55,234,20,138,153,37,71,118,199,109,105,159,132,116,171,
-  253,51,174,119,41,37,97,16,158,230,141,99,12,4,65,136,177,169,75,35,213,22,79,42,180,244,48,66,18,155,136,87,
-  191,246,37,132,161,64,235,4,37,189,76,104,9,70,8,148,16,11,155,31,107,29,231,166,85,22,100,6,248,106,36,41,
-  88,207,221,91,69,132,65,96,200,35,172,65,89,3,196,68,213,41,170,115,83,180,229,115,232,32,68,230,219,80,65,9,
-  229,23,90,64,175,21,26,105,23,118,97,182,137,237,88,199,142,46,22,59,137,171,13,60,37,49,58,166,58,125,202,17,
-  23,189,128,32,215,70,144,43,160,2,31,25,184,115,34,132,116,204,243,102,183,104,45,171,214,109,34,138,99,78,156,58,
-  202,96,95,31,113,220,192,87,162,101,221,33,149,64,90,137,10,45,126,46,0,2,132,40,44,216,189,102,224,126,146,141,
-  178,198,58,207,30,112,96,180,209,238,247,89,109,80,74,162,164,66,42,55,198,217,236,123,108,230,138,208,202,32,147,14,
-  103,210,58,93,24,149,141,205,70,63,145,173,227,197,34,19,126,131,176,25,193,85,100,185,101,86,58,59,14,43,208,81,
-  194,210,229,67,164,127,246,191,58,182,254,244,111,93,113,254,228,220,108,21,230,45,60,60,47,68,185,240,44,23,255,233,
-  29,233,115,84,128,22,23,161,31,20,48,221,196,124,234,177,225,53,191,119,255,243,174,248,88,96,30,104,3,245,50,104,
-  159,129,85,121,184,72,192,165,71,225,162,3,195,35,171,54,254,218,207,181,253,198,207,191,89,245,118,14,0,25,167,195,
-  58,203,4,157,1,192,50,107,79,221,159,5,18,149,113,80,82,48,6,105,5,66,106,102,39,198,168,204,76,161,178,107,
-  39,49,134,48,95,224,233,93,251,120,224,193,71,200,229,242,88,44,73,154,96,113,188,144,56,142,185,250,234,107,241,125,
-  143,70,189,142,167,20,86,186,213,185,53,6,43,37,202,115,227,88,148,38,40,207,195,164,89,14,185,82,173,55,107,189,
-  94,207,214,199,153,12,32,3,173,211,36,109,201,50,130,192,99,124,106,156,235,111,186,156,190,254,126,146,184,134,151,141,
-  122,205,206,71,102,12,240,150,162,92,186,55,190,182,180,156,139,17,6,105,125,192,50,63,55,138,141,35,2,191,157,160,
-  208,129,10,18,231,101,44,36,214,8,138,109,29,232,218,60,42,53,216,184,70,101,118,30,153,243,9,219,58,200,21,123,
-  16,94,9,33,60,76,22,33,217,12,2,180,100,90,55,173,81,133,18,181,40,225,11,183,223,197,45,55,191,136,182,66,
-  142,52,170,161,107,117,170,229,6,101,41,240,194,0,21,4,120,190,115,144,244,131,0,233,41,132,82,110,181,109,13,27,
-  54,110,100,91,181,204,212,116,153,238,142,34,164,177,99,107,219,5,203,179,56,141,241,60,229,36,31,89,81,55,214,102,
-  76,118,213,234,38,69,86,224,148,146,11,86,105,86,32,133,211,229,185,163,230,176,42,87,180,28,165,192,99,193,122,196,
-  66,75,231,101,140,235,142,132,114,91,75,99,22,114,140,28,30,232,192,112,163,227,172,19,202,18,102,149,200,126,183,163,
-  78,164,115,49,235,55,108,98,230,143,127,123,100,255,255,252,131,235,214,197,102,84,195,216,209,192,219,51,16,167,137,176,
-  150,249,124,192,124,16,32,173,37,159,106,202,129,231,98,204,4,207,109,1,250,65,142,99,205,226,211,72,12,175,254,221,
-  231,23,230,211,180,214,120,41,136,9,40,120,48,226,193,230,18,92,53,1,151,62,220,217,190,33,247,250,91,187,223,240,
-  255,253,154,88,179,226,28,208,13,108,60,237,66,3,133,204,138,131,68,102,109,252,2,101,159,214,197,189,248,0,74,9,
-  213,185,57,42,51,83,232,196,96,53,248,94,182,209,193,227,158,123,31,100,106,118,146,124,169,3,107,154,124,29,73,18,
-  199,44,95,177,156,222,222,30,26,141,6,237,165,118,124,169,136,162,24,47,155,255,91,9,161,82,18,134,33,66,72,82,
-  145,144,36,77,162,161,211,139,197,218,16,71,13,167,128,111,42,240,133,68,102,172,92,97,161,92,153,165,171,43,207,5,
-  231,159,139,77,19,60,233,157,238,134,218,234,58,50,37,185,200,36,180,214,145,41,143,31,62,142,50,150,145,165,125,104,
-  13,74,5,120,70,83,173,205,145,154,6,186,86,165,216,149,71,133,237,164,4,24,169,240,130,54,74,93,35,124,234,67,
-  31,228,130,13,231,176,97,205,106,106,233,12,245,233,57,226,234,28,42,232,33,87,234,70,5,42,187,168,220,69,107,16,
-  104,44,66,73,148,181,116,13,143,48,93,174,240,206,119,253,43,151,95,186,133,75,46,56,159,48,40,96,51,191,101,157,
-  52,72,226,42,66,204,99,90,84,33,129,31,230,200,23,10,228,11,69,84,24,112,222,69,91,216,250,205,7,169,213,26,
-  116,20,242,206,61,50,203,100,107,130,189,89,127,114,122,240,97,38,18,150,25,174,70,147,40,152,241,157,68,43,46,58,
-  219,46,105,183,73,211,214,249,73,35,101,86,220,105,173,192,23,19,12,23,127,246,60,119,99,106,110,24,157,175,145,114,
-  231,196,72,76,10,73,146,18,132,57,114,249,60,190,239,147,234,20,164,64,153,2,74,5,92,255,250,215,243,213,195,71,
-  87,86,254,250,221,87,23,96,255,140,16,147,123,124,239,212,80,156,152,59,215,47,231,238,117,75,200,197,9,22,75,212,
-  168,146,79,52,4,242,185,47,64,63,168,34,20,20,125,230,107,9,111,120,30,21,159,230,203,57,23,72,193,239,131,158,
-  58,108,204,193,149,21,184,242,126,212,102,251,202,155,6,95,243,135,111,245,207,187,224,82,55,92,153,26,146,4,45,93,
-  210,128,27,50,196,34,81,35,139,20,214,42,187,91,101,122,40,233,220,232,116,163,78,101,106,18,27,215,51,149,182,227,
-  199,4,97,64,173,150,240,248,227,187,16,82,16,69,13,103,189,137,91,151,231,243,121,150,142,44,101,118,102,22,63,8,
-  24,236,31,68,167,14,227,105,110,179,172,181,153,118,201,117,94,105,156,128,117,56,131,49,6,99,45,137,54,148,218,219,
-  153,58,213,112,220,34,207,207,108,64,4,198,186,87,161,4,204,205,159,226,101,215,223,154,113,12,229,66,113,21,153,118,
-  201,90,164,205,46,46,148,147,38,52,191,142,199,212,196,36,127,253,39,127,206,235,127,236,101,220,250,146,27,48,8,114,
-  126,129,163,99,71,232,234,76,41,132,41,181,169,57,188,182,24,194,118,68,152,39,5,130,246,94,242,157,3,252,201,95,
-  189,157,151,191,244,22,174,190,246,114,122,250,186,104,68,85,210,250,113,210,234,4,190,151,35,95,106,71,22,243,8,207,
-  203,52,87,142,123,172,177,228,10,69,174,190,230,106,226,242,44,91,183,110,101,223,211,59,89,187,118,29,171,87,174,166,
-  189,171,45,243,172,209,200,236,60,10,161,48,198,146,212,42,36,181,26,85,53,67,174,80,32,44,182,177,97,253,6,118,
-  110,125,146,80,121,174,131,177,110,205,110,108,243,220,36,173,237,94,83,24,108,22,5,38,234,212,180,228,18,45,81,109,
-  230,223,212,204,184,215,153,38,183,169,225,18,194,97,84,74,41,7,174,167,174,232,45,246,187,118,92,48,147,177,225,131,
-  214,249,215,70,131,117,227,153,39,20,82,250,156,56,118,146,71,30,125,146,217,217,10,197,82,7,113,234,92,17,139,82,
-  153,216,243,101,174,163,157,74,190,83,153,82,238,220,155,42,141,11,114,198,110,219,106,236,212,61,74,154,163,3,221,20,
-  147,52,27,83,93,167,103,197,247,65,68,252,110,139,144,49,112,209,247,138,9,41,1,165,0,121,231,33,222,254,190,29,
-  220,115,100,254,121,131,243,120,192,86,8,87,65,135,129,145,8,46,12,224,5,79,192,101,135,46,58,103,213,213,111,251,
-  205,220,77,183,190,132,130,10,177,73,12,194,67,202,16,43,3,55,70,145,102,155,6,90,2,70,219,132,69,133,61,205,
-  200,202,98,192,164,96,83,230,167,166,209,81,132,176,6,147,106,164,200,129,48,4,65,158,199,30,223,197,209,99,163,89,
-  158,151,165,16,134,164,58,37,142,35,86,173,88,65,224,5,196,141,4,129,108,17,7,155,250,159,230,22,68,91,227,120,
-  27,25,15,136,76,221,78,147,109,108,28,95,168,84,44,146,38,49,158,39,50,31,99,31,163,13,185,32,96,98,242,36,
-  55,223,122,61,155,206,63,7,19,71,144,173,248,23,2,134,229,34,43,13,15,211,50,252,113,24,152,37,101,195,166,245,
-  188,229,167,222,204,206,173,59,248,192,191,126,136,55,188,233,21,148,43,227,124,237,158,187,217,180,97,21,43,87,44,161,
-  183,111,144,242,244,12,86,149,233,232,233,197,43,228,177,218,240,226,87,190,10,21,132,116,182,133,220,113,215,55,24,30,
-  232,101,203,69,231,208,81,10,49,105,132,174,55,168,68,117,100,181,64,190,173,132,44,228,241,51,233,8,128,82,30,58,
-  142,232,40,42,94,250,226,23,50,55,51,207,209,35,199,217,190,107,27,198,26,114,69,159,66,49,79,160,60,172,5,223,
-  15,41,149,218,41,22,10,228,194,34,86,8,226,122,131,36,113,30,203,75,70,150,114,96,255,62,150,44,29,65,74,157,
-  101,185,11,172,116,36,64,99,178,77,85,54,146,138,150,232,55,163,14,88,209,74,181,16,66,100,227,58,45,110,77,166,
-  241,200,192,125,215,197,164,38,201,156,10,36,210,247,22,156,32,141,113,124,41,211,84,214,139,133,200,33,75,107,75,38,
-  1,99,171,88,227,179,100,89,63,93,189,215,179,103,247,33,238,190,231,126,238,184,235,94,150,159,154,173,188,30,100,5,
-  194,57,72,61,33,170,195,74,140,165,16,201,56,49,43,192,218,165,253,28,236,235,66,87,170,174,248,26,251,109,141,129,
-  212,203,87,108,226,185,42,66,82,193,137,189,238,239,3,155,3,103,215,9,103,221,255,63,131,148,228,75,84,106,137,62,
-  127,128,169,191,122,132,187,102,35,30,255,1,22,150,115,28,79,135,47,0,215,1,131,208,138,13,241,128,163,184,236,247,
-  141,64,63,168,97,232,172,194,6,9,215,9,184,101,14,110,250,90,33,127,149,255,214,95,89,241,75,255,242,14,111,203,
-  249,151,184,187,154,6,33,83,172,172,162,69,138,176,2,165,113,130,165,22,249,77,52,47,203,150,36,193,178,192,203,16,
-  214,32,173,161,60,53,73,101,118,26,29,71,88,171,29,160,168,20,169,177,120,65,145,143,124,228,223,217,181,239,32,66,
-  185,55,162,146,146,70,163,193,242,101,203,24,26,26,162,81,143,72,226,24,223,243,25,25,30,161,163,163,163,181,209,106,
-  90,115,10,225,124,166,155,212,27,99,109,230,247,99,81,205,55,167,148,8,4,73,28,81,40,228,220,29,93,187,72,159,
-  74,121,150,21,43,135,185,238,69,87,67,90,205,236,49,112,41,7,210,100,177,55,205,77,140,187,176,164,16,206,12,29,
-  119,193,10,64,73,159,153,201,41,94,112,229,69,212,171,101,142,30,59,65,169,163,141,70,82,101,205,138,213,236,221,123,
-  132,147,227,19,44,93,178,20,133,161,94,153,67,160,221,230,40,215,70,163,81,65,165,13,174,190,250,18,166,167,78,241,
-  228,227,219,104,52,160,189,171,159,160,24,184,245,115,98,168,215,42,164,141,134,203,140,80,14,232,23,82,49,123,106,20,
-  91,155,65,40,139,239,91,6,135,135,24,26,30,166,212,222,70,88,200,17,132,1,18,231,201,164,181,102,126,126,158,217,
-  217,121,198,199,79,49,57,57,73,189,94,35,142,234,40,41,41,22,157,217,255,216,216,40,93,93,93,24,92,87,226,232,
-  0,166,101,231,138,16,142,46,161,53,82,73,247,125,198,32,61,183,20,176,89,247,104,23,235,227,50,230,183,109,158,39,
-  200,86,236,11,139,3,22,165,125,216,211,178,202,68,171,104,181,206,107,147,243,36,5,210,115,14,11,82,66,152,83,44,
-  95,57,204,13,55,92,201,245,55,94,137,215,91,244,106,115,243,114,249,212,60,23,91,42,235,96,95,175,177,15,105,120,
-  200,8,246,140,64,121,87,185,202,147,161,207,234,66,64,109,182,66,96,53,117,105,241,148,68,158,225,198,8,240,226,55,
-  252,236,115,87,128,90,69,200,19,156,218,111,216,244,181,71,73,139,48,94,8,232,233,201,35,197,233,133,200,97,15,224,
-  121,64,168,136,235,154,131,255,223,55,136,63,187,143,156,117,145,189,143,253,8,10,80,115,217,171,128,30,240,11,78,30,
-  177,202,131,75,5,188,164,10,183,30,201,5,87,140,190,244,166,245,55,252,243,223,119,188,254,167,222,76,33,144,152,36,
-  1,27,33,189,196,109,147,132,231,114,233,49,8,210,86,139,221,60,80,34,147,83,52,137,59,198,106,87,72,140,70,232,
-  148,70,121,142,249,217,105,116,26,97,172,70,91,199,225,72,77,138,16,1,115,229,132,127,123,223,71,168,214,27,24,43,
-  104,107,111,7,99,220,232,181,108,153,3,142,149,135,239,135,128,96,96,104,136,190,254,62,7,122,74,143,32,112,1,131,
-  152,172,0,9,209,42,100,162,73,39,206,86,232,82,56,223,231,35,71,14,35,149,32,137,99,138,133,18,38,49,204,205,
-  157,226,197,47,190,14,105,106,84,102,167,168,205,206,81,47,207,81,153,159,162,94,157,195,152,212,109,210,50,81,169,176,
-  26,27,87,136,203,179,196,181,121,132,73,128,20,233,183,51,63,51,71,18,31,103,213,138,33,226,196,99,247,222,131,116,
-  118,118,210,217,81,164,127,160,135,83,167,198,121,240,129,135,24,30,90,66,87,103,23,229,242,60,198,166,40,31,6,6,
-  6,216,187,243,105,186,58,66,134,6,186,233,235,237,101,108,108,150,221,251,14,82,141,27,228,242,57,242,94,232,88,193,
-  113,76,163,82,6,163,81,158,68,6,62,243,83,147,212,231,103,200,229,115,8,105,209,58,198,104,75,144,11,201,5,1,
-  133,92,142,66,230,137,93,42,21,233,104,111,163,152,47,208,214,94,194,243,157,128,180,86,175,49,61,53,205,228,169,9,
-  124,223,167,82,169,80,107,212,41,149,10,24,173,23,121,240,100,55,58,165,88,236,53,100,164,235,58,117,147,37,173,84,
-  171,224,104,171,91,27,43,247,217,197,229,144,69,243,8,68,102,60,118,250,67,46,26,197,154,221,206,153,141,192,2,77,
-  34,4,235,186,87,173,13,105,26,145,36,13,58,219,10,92,124,249,37,98,228,165,47,149,135,86,13,201,3,135,14,89,
-  127,186,220,200,195,148,128,83,169,16,83,5,168,30,135,180,227,212,12,175,59,118,138,251,246,159,224,156,64,114,168,191,
-  131,208,242,172,5,232,185,143,102,22,160,60,75,254,155,39,217,122,223,1,62,210,87,224,231,94,187,158,210,149,195,12,
-  175,232,32,150,2,153,24,172,208,164,13,152,154,134,162,136,40,253,221,67,76,111,61,197,200,143,24,231,105,184,15,5,
-  20,151,195,146,8,46,16,112,241,60,156,51,26,248,107,219,111,189,161,239,210,183,254,98,113,253,149,87,73,95,42,116,
-  50,143,80,2,37,52,86,65,34,20,2,137,167,21,34,177,88,37,137,125,80,26,148,21,167,3,74,66,96,141,118,46,
-  131,25,200,40,17,36,245,58,243,83,51,217,70,202,160,141,118,88,2,96,137,41,20,123,121,228,177,237,76,78,78,227,
-  135,62,104,133,231,249,164,113,196,234,213,171,29,142,22,248,20,10,5,199,245,16,34,219,130,168,12,60,150,72,37,145,
-  218,44,128,224,144,37,97,58,44,161,121,193,232,196,141,15,66,74,230,231,231,105,107,203,83,106,43,33,165,228,228,169,
-  19,68,141,121,172,142,168,204,78,162,27,13,140,105,218,196,106,188,208,163,26,85,9,148,71,80,232,68,8,69,125,110,
-  154,250,236,56,202,104,12,150,56,200,97,165,194,47,212,8,114,134,250,20,68,94,68,255,96,129,199,182,143,17,230,125,
-  36,18,29,87,56,119,211,42,60,47,228,67,31,191,141,203,46,187,140,203,175,188,136,180,94,167,82,63,76,231,224,74,
-  150,175,63,159,253,71,118,178,102,249,32,158,74,57,111,211,48,147,83,115,28,62,60,198,212,232,36,3,3,61,244,247,
-  116,211,221,213,78,206,247,169,207,205,209,72,26,116,122,18,235,73,234,177,162,77,248,142,252,137,113,196,195,84,18,40,
-  15,79,41,180,72,29,142,150,73,83,124,169,72,109,138,231,229,51,128,55,68,73,143,52,49,204,205,205,83,175,71,204,
-  150,231,24,24,236,115,199,189,25,211,179,136,155,36,90,148,240,108,21,46,29,46,36,69,115,92,166,101,38,223,148,84,
-  156,206,161,114,93,171,181,214,1,255,176,40,117,195,182,64,108,33,84,166,219,91,48,34,91,176,4,177,153,7,146,251,
-  253,22,227,10,28,18,171,33,78,12,105,84,163,83,5,188,236,149,183,50,122,197,150,194,214,79,126,97,237,161,15,255,
-  123,199,154,153,202,64,151,181,253,99,240,192,74,216,117,94,170,167,147,84,39,41,208,54,58,67,223,138,126,234,249,80,
-  72,125,118,38,208,115,218,1,53,47,42,105,45,231,31,223,199,184,78,121,186,150,80,121,108,140,187,62,187,143,158,221,
-  211,4,93,33,141,130,79,41,12,184,243,253,17,223,252,178,34,248,210,33,214,238,220,207,97,183,214,38,0,238,255,33,
-  117,64,159,7,174,117,99,22,243,160,166,161,93,194,50,31,46,77,224,230,10,188,100,2,174,180,47,185,97,253,133,239,
-  254,219,254,23,252,214,175,230,6,151,175,20,202,196,96,211,214,93,199,10,133,144,62,194,42,164,49,72,147,186,187,91,
-  203,250,32,91,129,90,155,201,1,12,214,68,164,81,13,145,52,16,94,136,231,121,152,70,141,217,137,113,210,168,6,164,
-  104,147,130,132,52,50,40,44,97,33,135,80,37,62,240,193,127,103,223,129,195,248,97,128,49,78,159,21,4,30,203,150,
-  45,167,90,169,129,21,52,26,17,181,90,13,63,240,233,233,235,101,96,112,8,132,179,147,80,202,61,23,231,51,166,50,
-  8,78,46,152,97,129,19,152,10,65,144,11,73,117,194,216,232,40,131,195,67,248,126,224,240,159,48,196,90,216,191,255,
-  8,86,75,134,135,187,48,196,120,120,40,27,184,215,232,105,194,246,62,60,47,71,90,47,51,59,121,138,70,45,97,199,
-  246,221,28,63,54,202,190,61,71,169,206,59,182,116,154,166,212,203,243,148,242,10,27,25,6,122,122,40,21,139,200,64,
-  129,74,209,113,66,79,95,39,171,86,175,224,115,159,189,131,123,239,254,38,231,172,63,151,174,174,18,213,202,44,131,67,
-  75,216,187,247,0,158,167,241,130,132,52,54,20,115,221,244,247,119,225,231,19,198,167,78,113,228,240,40,163,99,115,212,
-  106,154,82,123,39,57,63,196,214,34,230,39,103,152,175,84,233,236,40,33,209,120,153,191,182,219,248,25,60,101,240,173,
-  1,5,49,17,49,113,43,93,85,10,141,18,174,75,77,211,24,41,161,173,84,164,183,183,155,174,206,118,210,36,201,116,
-  95,114,193,161,17,50,108,48,91,193,227,54,163,10,133,146,178,21,61,237,198,68,167,217,50,34,3,168,165,200,70,173,
-  108,89,209,106,172,79,143,153,118,133,200,129,230,82,40,100,214,237,168,236,239,74,122,40,169,50,204,73,128,72,51,135,
-  2,145,25,215,137,22,93,66,102,22,33,113,163,74,177,144,99,221,11,46,147,225,181,151,183,237,139,107,75,226,61,135,
-  134,59,45,189,30,132,17,164,101,79,69,126,46,72,87,87,26,102,122,178,204,236,112,15,86,10,34,99,8,172,165,106,
-  29,65,245,229,63,241,243,63,216,2,52,166,83,118,102,23,247,36,208,126,188,76,238,206,67,188,225,222,195,236,220,41,
-  40,141,119,65,94,49,88,158,102,197,244,24,135,126,136,5,104,67,86,120,78,2,151,128,151,64,123,2,43,115,112,137,
-  132,155,107,112,235,168,20,215,153,27,174,89,119,193,223,253,69,207,101,191,251,214,252,208,234,213,194,166,113,203,50,162,
-  57,199,187,109,146,104,193,170,34,235,40,76,198,217,240,154,35,142,196,89,74,152,6,113,163,76,220,168,33,133,192,15,
-  243,72,229,99,146,152,185,233,9,162,90,5,97,82,140,118,65,115,113,154,34,45,46,19,94,73,70,199,230,249,208,71,
-  254,157,36,117,93,68,154,26,210,56,102,227,198,13,36,73,194,220,220,92,166,160,150,45,196,105,96,104,136,225,37,195,
-  173,77,138,88,36,136,212,214,224,121,30,94,118,135,116,44,95,71,114,84,153,131,98,189,81,103,122,106,138,129,129,193,
-  108,125,235,188,127,218,218,59,177,214,227,240,161,19,84,171,101,86,172,90,138,78,12,74,248,72,101,192,131,124,251,48,
-  158,42,18,85,167,137,27,117,254,230,255,188,147,201,169,83,12,14,247,80,43,215,153,155,158,228,196,241,221,236,218,177,
-  139,137,137,73,150,45,31,33,213,41,97,40,8,60,223,17,0,173,67,231,140,142,200,229,60,182,92,180,133,35,135,143,
-  240,165,47,222,65,190,208,198,224,96,15,194,54,200,229,122,216,182,237,73,86,172,26,68,39,9,214,72,18,211,160,88,
-  240,24,234,233,163,187,179,11,107,53,71,78,28,100,239,129,157,104,19,145,247,3,146,56,101,122,126,150,206,142,130,27,
-  19,173,117,219,46,43,16,190,202,64,122,72,172,192,88,31,172,135,20,22,107,98,140,9,176,54,196,52,77,245,173,117,
-  198,111,153,20,36,73,19,140,209,100,203,242,86,209,144,139,198,34,145,37,158,52,61,168,149,116,93,167,20,77,157,150,
-  69,121,94,203,199,89,8,199,106,106,222,48,220,8,230,131,53,25,21,99,209,184,101,221,54,78,54,59,46,139,35,58,
-  54,237,177,141,197,247,252,12,163,84,25,83,218,117,106,142,24,155,153,193,42,247,164,141,53,164,141,152,161,129,62,214,
-  222,112,173,55,187,102,121,223,190,125,251,151,250,211,229,145,18,116,91,99,101,155,239,55,172,49,245,168,30,235,246,241,
-  89,202,147,243,92,121,98,154,59,78,206,240,115,39,103,88,118,114,134,77,191,249,59,63,128,17,236,91,60,252,172,184,
-  236,29,173,131,158,228,226,245,43,137,236,217,55,79,5,96,89,86,144,158,227,9,145,41,160,221,253,124,239,167,160,211,
-  194,18,13,235,125,56,127,10,46,60,226,123,235,195,235,175,237,191,240,87,126,62,127,238,141,215,202,48,12,177,105,140,
-  142,235,25,203,212,158,150,180,41,165,2,19,103,231,218,57,20,54,205,38,69,179,213,198,96,211,24,147,212,72,163,138,
-  107,171,189,16,47,95,34,149,57,60,19,83,157,155,165,58,95,70,24,157,141,102,218,109,169,210,166,5,171,32,244,11,
-  60,245,212,195,76,77,207,160,188,144,212,52,28,5,223,88,114,185,28,214,66,169,216,70,24,134,173,118,61,8,67,226,
-  40,33,110,36,142,222,47,100,118,103,204,146,47,140,88,72,121,200,116,67,198,51,196,73,130,242,90,217,20,244,246,244,
-  44,240,75,178,21,176,214,41,74,9,122,123,135,120,234,169,253,168,32,224,133,87,93,76,117,126,194,129,233,132,120,66,
-  96,26,53,66,36,183,221,241,21,250,123,114,188,254,199,94,73,154,70,136,53,110,75,103,116,157,19,199,39,57,116,236,
-  20,245,40,69,121,30,154,58,164,26,79,231,28,48,171,4,136,0,173,45,82,214,120,227,155,95,194,19,79,236,228,206,
-  47,221,201,241,99,199,120,193,213,151,177,254,220,45,60,112,191,230,248,145,41,134,6,122,137,117,140,23,186,142,207,75,
-  99,58,218,36,29,237,221,12,175,232,160,161,83,230,230,170,108,219,181,155,217,233,121,82,93,103,229,202,129,204,195,56,
-  243,104,150,62,130,16,85,112,145,53,182,150,162,210,4,207,52,0,67,148,122,238,70,35,82,20,34,19,238,186,68,82,
-  99,156,83,163,144,153,53,171,36,27,129,132,235,60,180,3,124,129,214,185,114,203,129,102,188,82,214,233,224,196,170,22,
-  78,147,74,180,70,173,166,45,73,22,163,172,252,108,3,37,23,162,119,50,115,163,172,163,163,21,71,100,172,93,228,104,
-  32,90,9,25,206,184,126,65,38,35,51,246,126,115,175,105,5,212,107,117,132,240,184,244,214,23,137,213,87,94,214,247,
-  240,251,62,118,229,248,251,63,189,124,117,181,177,170,163,214,184,39,129,251,47,132,3,59,231,107,181,234,124,205,46,113,
-  114,37,214,3,179,207,229,26,254,185,126,212,129,45,192,255,206,10,197,115,253,56,4,28,6,127,47,140,0,151,249,112,
-  249,20,156,183,211,87,107,210,27,175,27,188,234,127,252,66,112,249,139,110,32,231,123,24,93,67,235,90,70,18,92,164,
-  79,60,51,241,160,229,18,191,144,224,233,128,66,199,227,48,81,133,56,170,101,197,37,193,15,11,168,176,128,80,62,218,
-  122,212,202,83,212,102,167,145,214,162,211,20,147,17,204,146,40,118,73,150,190,203,137,210,90,241,232,227,219,72,98,77,
-  16,122,104,60,164,108,160,132,79,28,167,132,97,136,181,150,40,138,92,8,97,38,32,77,146,36,139,219,1,109,221,22,
-  38,77,93,145,243,148,202,182,52,11,236,89,4,156,56,126,2,207,247,233,31,26,0,11,61,189,189,45,127,161,133,196,
-  5,129,84,146,36,53,12,141,172,225,209,71,118,179,100,112,136,53,171,123,73,162,42,194,72,68,90,3,155,176,111,247,
-  110,234,243,243,188,225,13,47,162,94,157,65,144,67,83,201,216,185,57,134,87,140,48,180,124,136,36,169,64,154,3,36,
-  158,116,121,33,90,104,231,25,40,66,23,29,100,83,26,81,149,139,46,218,196,154,213,171,184,227,75,95,229,95,255,245,
-  189,92,180,101,23,107,86,159,203,158,29,219,25,236,27,6,17,115,226,228,60,163,51,101,218,58,67,218,243,237,116,149,
-  138,180,151,138,4,34,33,239,215,25,238,210,76,79,205,80,141,34,210,52,6,225,186,8,41,221,152,114,114,116,156,79,
-  127,230,118,110,121,237,107,57,127,195,16,186,92,37,170,186,124,52,165,12,130,26,70,72,132,8,50,198,53,248,74,144,
-  26,237,28,2,172,113,221,135,176,173,209,215,100,95,211,90,59,135,69,156,48,24,32,77,12,94,224,57,6,181,21,40,
-  229,116,120,58,43,140,142,62,101,23,34,118,140,105,221,240,36,194,17,21,155,140,104,235,70,54,151,31,183,128,31,25,
-  187,40,245,3,131,54,73,214,253,56,125,153,151,101,192,11,156,31,146,64,57,83,53,33,50,61,95,147,34,144,82,155,
-  159,165,61,23,240,162,223,249,101,127,255,77,55,173,120,234,79,255,166,123,217,99,79,13,116,67,127,29,238,91,3,79,
-  143,192,84,29,210,31,8,15,232,7,241,240,128,227,192,19,56,215,181,231,74,102,150,41,119,69,8,221,22,174,138,225,
-  199,238,246,197,101,83,87,93,212,251,226,223,248,53,117,253,139,111,33,244,60,76,218,192,234,104,65,108,105,113,41,18,
-  139,131,229,22,100,218,88,161,178,194,211,76,182,52,78,65,173,35,162,242,28,166,81,113,216,143,159,71,228,219,32,44,
-  97,132,135,50,160,27,101,202,83,167,28,215,71,39,217,29,70,56,47,31,41,145,194,195,24,167,171,154,158,169,179,227,
-  233,189,248,185,208,17,5,147,20,129,197,247,20,133,124,17,173,83,188,76,94,209,244,239,177,205,162,150,69,242,72,41,
-  241,60,143,70,189,209,98,222,106,227,86,193,173,13,77,150,47,222,136,234,244,233,94,4,206,200,94,107,221,42,64,158,
-  231,161,124,223,69,189,56,110,49,131,35,203,185,253,43,95,231,205,111,120,9,61,29,69,44,9,73,109,26,99,12,79,
-  62,177,141,205,155,55,146,36,77,95,194,8,95,228,92,241,147,130,122,162,241,101,72,177,80,36,170,215,81,162,136,69,
-  160,213,52,158,103,65,75,140,141,81,184,149,184,53,146,168,214,160,152,151,188,238,199,110,229,208,161,81,190,252,149,175,
-  115,247,93,15,18,122,138,11,183,172,162,171,167,200,71,63,248,97,187,245,51,95,73,214,244,150,76,111,88,50,229,190,
-  222,176,184,126,141,58,247,210,243,185,240,130,117,12,245,20,24,26,30,160,222,112,91,31,33,36,202,83,153,154,220,210,
-  222,86,98,207,206,221,236,255,242,111,38,151,222,112,149,186,241,77,175,147,203,70,122,168,85,230,193,72,148,113,26,183,
-  200,102,242,153,76,49,239,216,201,105,54,138,201,214,104,212,196,120,76,162,209,166,105,117,226,198,42,173,93,103,105,180,
-  113,239,169,44,245,212,117,56,246,180,104,103,215,58,45,176,233,155,93,145,75,69,37,115,175,116,203,4,169,156,59,163,
-  181,26,147,69,48,139,204,123,201,104,235,54,182,210,69,55,233,108,116,84,202,91,40,106,198,56,220,72,169,140,63,229,
-  192,109,149,17,34,211,52,193,206,204,112,206,133,107,89,254,137,119,182,63,242,79,31,184,236,224,59,63,208,179,162,17,
-  47,1,238,11,225,225,126,56,122,171,11,167,108,93,207,63,20,12,168,55,27,123,150,3,221,192,191,2,61,197,14,46,
-  237,29,33,18,226,25,24,16,192,41,160,19,23,228,183,18,231,177,211,200,100,16,223,207,71,195,49,153,101,27,44,53,
-  112,235,126,120,113,254,247,126,123,240,173,239,123,167,60,103,195,58,72,106,24,19,211,116,201,19,50,19,141,46,10,150,
-  91,28,24,215,76,62,48,25,2,36,5,72,171,177,105,132,169,149,137,42,179,68,213,50,146,132,32,8,81,249,118,200,
-  119,160,113,6,240,202,104,170,83,99,36,213,10,214,56,51,47,164,92,224,142,180,230,118,67,24,150,216,246,212,65,62,
-  119,251,151,209,8,226,212,117,50,82,24,148,244,24,28,28,162,90,173,146,36,73,166,94,135,52,141,105,111,47,81,106,
-  239,96,112,112,8,47,195,17,140,214,204,77,207,16,6,65,203,106,85,52,59,59,107,241,149,211,143,149,43,101,186,187,
-  187,168,84,202,140,143,143,83,42,149,220,27,46,73,48,64,172,83,154,214,23,82,57,142,75,163,30,115,96,223,97,46,
-  187,228,18,226,164,138,16,154,3,123,199,248,240,135,190,192,181,215,189,16,223,115,223,175,132,139,148,110,254,222,32,200,
-  243,192,3,223,228,228,196,52,203,55,174,198,16,97,76,13,79,88,20,37,192,199,11,156,53,171,181,26,37,125,172,17,
-  238,98,53,150,246,206,18,151,92,118,46,43,87,13,211,168,205,179,122,213,114,188,98,158,169,39,118,233,155,31,223,62,
-  126,109,165,177,115,253,220,252,211,203,79,142,77,136,237,79,199,251,238,190,87,124,253,129,111,122,135,15,159,82,34,223,
-  70,190,187,141,192,47,96,82,71,53,182,56,147,181,158,222,62,214,12,173,48,171,62,250,133,164,99,219,46,238,186,239,
-  126,83,238,108,147,203,54,110,20,18,141,212,25,195,93,57,156,78,96,178,205,149,204,54,84,11,162,204,214,184,147,145,
-  244,154,197,164,89,128,90,235,115,165,144,202,59,205,92,77,74,47,219,106,57,224,218,102,226,81,129,43,46,82,218,108,
-  11,237,181,164,46,94,107,228,118,62,220,82,101,110,147,25,77,180,233,229,228,249,62,65,224,103,108,124,215,121,57,44,
-  203,100,210,25,215,77,53,77,236,90,64,186,84,25,171,27,164,53,52,226,10,202,19,172,186,241,133,42,58,127,67,223,
-  222,71,30,29,41,205,213,122,67,80,101,152,223,12,101,1,73,4,44,125,219,219,158,191,5,104,60,251,119,219,128,23,
-  3,251,129,92,86,68,244,247,241,145,56,182,131,232,114,78,132,235,36,172,46,231,219,59,123,175,186,74,245,118,117,183,
-  14,238,66,116,174,204,76,157,108,139,7,198,162,216,149,197,198,222,2,11,38,193,52,230,73,107,243,196,245,42,58,137,
-  241,124,69,144,43,161,114,29,200,92,59,86,6,174,179,209,154,168,60,77,52,63,135,53,41,137,78,91,119,134,36,137,
-  51,31,95,137,18,78,45,237,7,37,62,253,153,47,179,99,247,94,132,39,73,83,227,46,62,157,98,181,165,183,183,159,
-  32,240,93,17,3,226,36,194,247,61,148,39,201,23,139,12,12,12,18,134,1,74,10,226,70,131,153,217,25,74,109,165,
-  204,145,207,1,210,2,119,65,75,41,169,215,234,76,79,79,211,209,222,65,20,69,84,170,21,242,249,60,73,146,32,148,
-  114,14,50,217,103,173,181,83,170,251,146,131,7,142,114,244,240,9,70,134,251,24,25,233,167,222,136,249,236,109,247,242,
-  133,219,239,181,227,147,147,246,170,107,46,22,70,39,89,132,112,132,16,62,18,69,41,95,96,106,42,226,111,255,224,239,
-  141,61,62,97,251,87,111,20,29,221,157,40,13,54,13,156,207,178,202,116,75,77,2,103,6,176,198,113,138,242,5,73,
-  18,209,222,86,224,188,205,235,81,50,65,134,30,39,31,221,101,58,30,223,177,43,116,180,175,47,21,224,169,101,112,112,
-  147,182,199,214,77,206,141,182,109,219,53,123,242,142,187,204,211,79,110,15,170,94,222,107,95,177,78,180,183,119,96,83,
-  221,186,80,251,134,134,197,209,71,182,177,98,116,44,218,56,95,157,62,118,239,55,27,219,43,149,220,240,69,151,200,246,
-  146,71,156,206,131,240,90,248,152,204,58,3,107,51,183,1,227,120,60,77,140,167,233,119,36,165,204,162,178,29,251,76,
-  41,185,16,13,155,45,10,164,204,198,160,236,66,111,74,6,29,107,221,165,152,98,44,86,184,116,18,229,57,221,97,115,
-  141,191,248,103,54,109,81,140,209,25,55,75,162,148,199,212,212,12,199,142,31,39,73,52,93,93,189,20,10,237,88,235,
-  225,41,191,165,49,107,254,91,23,86,96,22,109,218,50,66,43,26,169,242,206,21,179,49,199,242,13,171,233,186,233,166,
-  210,131,79,63,61,28,30,31,239,233,0,149,56,190,208,124,47,196,67,111,123,219,243,119,4,107,221,9,50,240,250,1,
-  96,45,240,170,53,43,145,97,232,248,41,223,75,125,148,146,122,185,98,211,19,39,79,73,184,175,23,66,190,124,199,245,
-  255,120,205,182,115,47,253,139,63,41,189,241,205,63,142,34,70,39,213,150,8,112,97,173,105,221,217,207,236,11,108,54,
-  79,59,115,246,24,29,85,208,245,26,186,81,93,24,159,188,128,48,95,64,132,29,224,231,29,144,104,65,144,146,68,21,
-  170,243,115,45,221,148,144,2,171,29,17,77,103,134,237,205,45,135,20,130,70,148,178,119,239,33,151,78,33,83,144,10,
-  147,56,64,51,142,99,170,149,50,29,157,157,24,107,168,204,149,209,58,37,232,238,106,49,97,141,209,132,97,128,213,134,
-  122,189,230,180,77,74,182,212,211,58,251,221,178,201,164,21,144,36,9,149,74,133,122,189,78,177,88,92,192,30,22,105,
-  102,141,78,177,169,135,111,37,229,104,134,35,39,142,114,238,218,77,220,123,223,253,172,95,183,132,253,123,70,57,116,100,
-  154,141,107,86,26,241,137,207,148,111,31,110,235,120,249,79,189,74,216,250,52,146,46,172,6,45,171,36,54,101,100,100,
-  41,175,8,84,188,241,35,95,168,238,253,242,131,118,246,103,222,216,177,226,199,110,240,11,237,17,34,138,177,186,13,76,
-  1,107,99,16,117,132,72,200,231,139,236,217,125,152,157,187,246,242,19,111,122,5,181,250,28,113,84,7,163,240,108,129,
-  40,74,45,48,7,236,177,240,144,15,186,14,29,30,116,23,160,167,4,35,75,27,233,186,232,225,167,55,158,124,108,247,
-  198,219,207,253,226,138,252,141,47,40,92,116,227,229,172,94,210,139,213,41,65,201,67,191,248,74,255,240,227,91,103,214,
-  192,225,203,82,91,63,252,193,207,45,253,244,99,59,151,109,250,213,159,204,95,126,213,5,248,141,50,73,156,100,9,166,
-  54,99,60,123,25,197,126,193,186,214,247,61,148,148,36,81,228,248,62,86,144,232,20,79,153,150,32,217,225,52,167,167,
-  184,182,44,245,237,98,91,219,76,47,47,133,147,50,89,73,20,167,25,111,200,203,206,143,110,42,104,157,5,136,92,196,
-  1,178,78,30,210,209,217,133,69,242,205,111,62,198,39,15,221,206,154,213,235,184,224,130,243,105,235,110,39,95,244,40,
-  20,10,248,210,221,112,146,216,121,133,59,174,216,130,127,139,38,64,89,75,206,226,88,231,147,83,12,15,247,240,186,143,
-  189,179,248,201,63,252,203,203,226,15,124,161,125,9,20,186,193,36,176,29,168,63,247,5,200,146,9,13,5,207,85,148,
-  152,201,112,160,49,224,43,253,125,204,108,62,151,174,158,30,12,180,192,211,239,92,233,26,144,236,217,107,87,125,234,179,
-  243,231,192,19,85,152,235,133,137,87,157,56,57,245,165,159,253,239,151,60,246,181,123,251,127,230,183,127,157,243,206,57,
-  7,72,177,182,138,209,73,214,146,11,240,146,108,26,203,185,243,29,213,137,230,231,169,215,103,93,38,148,21,110,87,32,
-  12,129,231,225,23,125,100,62,135,48,29,238,46,164,18,231,110,152,132,232,234,44,38,213,104,75,118,23,116,109,182,214,
-  137,203,231,82,77,63,22,141,31,230,217,179,239,48,7,15,31,192,147,57,103,195,160,117,6,110,250,104,171,105,36,41,
-  189,65,72,17,231,37,236,251,33,190,23,18,4,5,164,80,212,171,53,68,175,3,64,157,209,216,194,69,161,117,38,139,
-  200,18,19,92,70,188,3,45,171,149,10,213,122,149,238,30,103,47,234,186,51,55,22,26,1,158,239,17,39,154,36,181,
-  28,63,58,74,95,119,23,202,179,76,207,215,185,239,193,157,204,205,84,233,94,210,77,116,106,55,47,129,220,248,59,62,
-  24,223,221,217,21,220,242,186,91,69,50,119,10,148,135,79,9,105,37,29,29,17,43,194,180,214,3,79,119,79,78,29,
-  159,249,171,127,24,217,122,207,215,206,89,250,43,111,236,93,118,233,133,120,34,135,174,213,92,232,30,10,67,74,148,148,
-  89,189,98,132,191,248,155,247,50,173,125,126,230,45,47,67,86,29,238,148,164,30,179,115,53,177,44,75,203,243,33,250,
-  123,168,253,24,204,215,225,100,59,120,62,20,2,231,203,189,114,185,54,23,13,111,219,121,201,161,109,59,55,125,249,179,
-  159,95,90,184,229,134,252,245,175,125,57,27,58,218,88,127,227,53,60,254,206,15,229,245,116,57,74,225,209,165,240,68,
-  207,211,251,207,219,241,43,127,188,233,99,111,120,197,240,205,191,240,122,213,91,8,73,234,21,148,13,157,75,161,15,202,
-  198,32,36,146,16,223,151,236,216,190,23,223,111,99,243,133,231,17,37,51,152,40,65,105,133,39,116,230,9,21,128,20,
-  232,36,109,185,73,186,162,147,96,154,182,45,74,181,240,31,33,157,231,135,77,45,86,57,5,190,78,242,116,246,14,80,
-  77,43,40,147,34,18,75,98,157,24,217,36,206,176,77,91,183,1,13,124,144,194,167,191,119,128,87,189,252,165,236,220,
-  181,155,183,191,227,159,249,219,183,255,189,61,167,123,64,247,118,180,201,225,85,171,200,47,25,22,157,157,29,98,253,198,
-  245,116,46,233,161,20,122,4,197,144,124,144,119,72,145,1,235,187,27,87,65,135,40,235,209,16,208,217,83,228,13,239,
-  248,43,245,129,246,190,115,27,239,254,104,178,58,110,28,10,225,72,179,0,137,231,16,227,117,107,232,36,114,219,158,231,
-  120,125,46,129,39,30,124,132,244,193,71,184,165,179,147,183,175,90,197,202,222,94,82,192,51,250,59,250,57,218,83,20,
-  199,79,177,6,236,131,80,94,15,187,125,40,123,48,254,234,56,62,245,228,7,62,116,229,223,125,249,171,203,250,94,245,
-  178,252,117,175,125,37,215,93,117,57,249,176,132,181,115,78,179,147,6,88,34,226,122,149,202,220,20,245,202,73,108,195,
-  93,176,94,16,226,169,28,72,139,23,128,231,229,240,189,65,87,144,189,6,72,141,16,121,182,62,190,27,79,39,172,94,
-  94,114,153,88,102,33,165,50,77,210,204,103,199,3,163,157,225,148,181,72,229,243,212,246,167,169,215,27,72,175,13,171,
-  117,102,239,96,49,110,209,66,189,81,71,72,153,97,64,30,185,92,142,32,8,73,226,196,197,23,27,67,26,39,196,73,
-  66,20,69,132,97,216,178,90,245,148,215,242,126,110,102,72,181,214,237,88,226,56,162,80,200,19,199,206,211,198,233,151,
-  60,71,48,139,99,132,176,196,137,165,90,174,51,50,52,76,20,53,40,181,117,113,223,55,30,165,222,104,112,222,249,231,
-  50,174,149,72,160,182,57,181,243,143,253,245,187,218,30,235,42,117,95,114,203,229,34,173,52,32,81,24,25,227,21,75,
-  200,124,71,156,50,179,195,74,238,106,183,116,151,30,217,121,249,232,127,251,189,45,135,174,190,116,101,215,155,94,221,177,
-  250,178,245,180,11,73,28,91,82,91,192,143,35,6,7,218,184,238,234,139,237,29,127,255,47,186,61,158,151,175,253,201,
-  215,72,223,143,105,152,148,106,181,134,128,192,56,83,8,249,112,198,1,219,3,122,35,232,21,16,245,194,220,78,56,209,
-  7,251,125,216,190,14,182,172,62,56,122,209,158,119,126,120,237,55,110,255,218,240,137,87,190,164,125,203,207,254,184,232,
-  127,221,43,75,179,239,254,80,169,13,142,196,240,100,14,30,187,34,73,175,222,247,161,219,94,240,222,39,182,158,115,245,
-  111,252,82,120,229,69,235,208,209,156,179,242,208,30,66,120,248,138,140,119,229,156,12,254,252,143,255,214,252,196,229,87,
-  214,174,121,229,11,67,127,197,144,111,83,13,73,197,157,79,98,132,245,91,145,211,218,58,111,33,79,132,46,11,77,8,
-  140,145,11,221,144,112,238,142,214,6,36,86,211,222,222,197,195,95,127,154,233,250,94,70,54,44,165,171,179,68,87,80,
-  164,80,242,240,177,232,68,99,116,66,170,147,44,214,59,193,10,227,168,4,82,113,225,249,235,249,231,119,253,61,31,248,
-  192,199,236,204,59,62,208,120,113,66,88,127,124,167,152,130,186,133,153,3,158,31,197,189,237,121,57,212,87,202,15,244,
-  250,221,93,189,150,124,65,106,15,225,231,124,26,113,34,210,68,136,40,182,162,166,181,80,57,161,148,178,178,160,125,142,
-  118,150,250,151,159,106,116,10,199,200,193,211,139,52,42,223,119,167,34,21,65,146,176,121,231,3,168,36,250,129,140,100,
-  194,97,56,44,153,157,101,199,19,79,224,175,92,201,165,235,215,49,154,47,226,39,201,183,237,186,140,231,225,123,62,5,
-  224,239,129,235,161,241,135,112,100,43,84,27,48,113,17,28,222,52,54,118,241,142,127,254,215,141,31,253,200,39,134,63,
-  245,194,23,22,110,250,137,31,231,250,155,46,101,160,171,155,164,82,229,212,177,253,68,245,58,58,173,97,68,10,66,226,
-  217,60,8,65,24,8,148,50,120,42,36,8,123,17,170,4,54,198,120,21,148,232,228,201,39,182,242,207,127,247,65,254,
-  247,31,253,12,86,85,80,34,135,162,66,108,155,137,74,11,138,116,99,28,111,68,10,73,146,194,182,167,158,206,146,33,
-  88,72,86,0,140,73,65,26,146,164,129,53,9,81,92,199,247,21,160,49,54,205,70,170,148,122,189,158,229,77,185,181,
-  124,144,249,251,52,199,5,167,144,23,45,207,104,128,192,119,49,47,190,231,103,29,83,102,33,225,172,11,221,198,204,243,
-  65,107,142,30,58,72,169,173,216,2,84,133,16,116,116,180,211,209,217,65,26,43,84,216,41,82,24,53,240,196,249,213,
-  70,238,208,31,252,237,69,79,21,255,96,213,249,215,93,33,146,169,57,18,157,71,22,3,212,208,128,210,219,15,207,43,
-  196,30,35,152,53,214,238,29,210,118,71,116,239,195,23,159,120,232,209,243,199,206,95,183,180,235,229,47,111,95,253,162,
-  43,68,79,41,79,82,241,41,91,201,171,111,125,161,221,248,190,79,234,218,63,124,216,124,106,223,17,239,210,95,253,111,
-  114,229,186,62,130,66,160,44,120,10,100,10,66,122,138,255,91,12,185,38,78,145,245,152,62,87,140,204,19,80,253,125,
-  56,180,31,78,85,97,175,128,39,206,133,245,201,209,209,115,103,254,225,223,206,125,236,190,251,87,207,14,247,23,151,64,
-  93,193,116,10,251,82,56,96,224,248,42,24,239,222,121,160,250,185,95,255,223,23,236,251,169,215,151,222,240,166,91,241,
-  61,75,28,37,120,228,144,120,136,32,161,30,85,217,176,121,35,231,93,112,142,173,124,240,227,250,196,231,190,24,249,55,
-  191,48,237,252,137,87,230,58,214,12,8,17,105,116,212,128,36,1,153,102,17,105,46,225,213,26,144,66,97,132,108,197,
-  221,139,204,122,54,53,26,105,37,82,248,8,242,12,140,172,225,61,63,253,139,201,13,105,61,153,238,235,150,141,174,110,
-  149,95,187,214,27,220,184,94,244,175,24,97,201,146,126,58,219,114,132,129,165,16,148,48,217,150,20,43,136,26,41,190,
-  87,224,23,126,225,151,229,189,203,55,22,239,251,179,191,49,151,159,154,176,23,65,57,130,167,72,147,29,140,77,233,116,
-  108,170,207,64,91,234,80,133,86,146,116,152,33,88,6,2,235,188,208,187,50,71,160,74,30,182,106,41,247,42,225,188,
-  236,213,139,151,174,19,205,20,200,239,235,67,41,66,157,226,237,124,128,115,230,38,233,200,212,228,223,207,22,172,23,120,
-  20,184,52,91,199,55,181,90,2,56,31,248,48,176,105,118,150,63,59,118,156,253,198,80,238,237,117,140,228,111,225,3,
-  98,165,196,171,84,216,116,242,36,95,7,206,3,86,128,249,18,212,150,194,120,10,71,20,28,29,129,83,23,71,81,165,
-  125,207,30,253,224,231,110,247,63,249,149,123,195,121,191,32,214,174,94,74,163,122,16,17,73,156,223,118,128,17,78,135,
-  19,248,142,206,46,101,72,174,212,133,42,118,160,101,10,94,29,37,251,184,247,158,199,245,47,191,241,231,163,11,182,108,
-  244,94,250,170,43,132,49,57,68,90,192,36,51,36,38,33,73,13,198,56,98,159,109,234,115,16,72,79,50,51,23,241,
-  177,143,127,142,249,114,29,169,194,150,126,168,169,138,22,210,97,11,195,35,195,84,202,101,146,36,33,78,92,183,146,203,
-  231,40,20,74,228,139,37,250,250,250,91,152,78,46,151,35,151,203,157,230,78,208,36,162,57,16,186,70,163,222,192,90,
-  71,74,108,235,232,68,8,209,50,44,203,96,119,60,47,192,232,148,167,182,61,201,146,165,75,90,155,65,169,20,129,23,
-  226,41,143,48,204,49,55,53,46,250,247,238,58,220,109,184,67,195,99,157,81,194,145,7,30,239,143,54,109,236,24,94,
-  61,76,18,205,33,61,143,234,227,59,35,182,239,123,82,89,30,46,88,142,90,24,175,101,231,165,215,216,147,157,39,38,
-  231,167,238,126,64,60,177,125,87,219,76,247,176,223,183,118,25,129,151,208,222,214,43,78,126,253,97,214,142,77,84,139,
-  251,143,206,222,245,245,251,189,249,225,165,126,189,28,137,182,199,183,141,118,194,55,107,176,111,255,210,222,248,134,151,92,
-  140,89,210,203,138,147,211,244,68,9,51,64,21,208,171,151,216,125,133,48,233,105,196,179,210,152,227,53,56,32,224,64,
-  9,78,20,199,39,199,242,251,143,28,42,193,99,10,30,213,112,34,7,101,13,211,26,38,11,80,63,55,74,228,177,71,
-  159,108,187,251,192,201,226,208,5,231,139,190,238,18,58,78,193,170,44,119,76,18,248,121,180,240,101,253,174,251,88,87,
-  139,166,234,59,247,28,63,126,215,125,241,201,169,185,92,95,111,191,144,253,93,210,10,139,48,54,211,214,57,50,163,208,
-  110,60,246,164,135,20,94,198,168,118,157,144,242,220,6,214,74,103,96,56,48,178,18,253,196,86,125,209,238,253,51,107,
-  103,202,187,87,158,60,245,116,251,142,93,19,115,247,220,95,59,246,149,187,211,167,190,118,31,15,60,252,184,183,239,216,
-  184,56,49,17,83,77,61,252,124,158,66,123,59,126,222,177,216,173,110,176,225,220,13,162,235,186,203,229,103,246,238,146,
-  246,196,68,60,12,71,19,120,60,133,135,44,60,98,224,9,31,30,43,192,163,1,60,18,102,31,121,120,196,135,199,61,
-  216,25,194,174,188,235,22,191,33,224,94,223,218,237,135,172,157,217,244,182,183,25,245,146,21,231,56,127,145,239,241,67,
-  72,1,65,192,188,73,97,251,253,12,206,79,209,151,97,54,63,140,2,180,18,120,181,214,148,38,39,41,38,9,7,187,
-  58,91,6,234,223,174,0,125,3,216,12,228,129,29,96,127,22,226,131,48,171,225,164,128,131,22,14,247,192,177,243,77,
-  58,179,124,116,212,124,253,11,119,228,239,124,234,80,126,217,69,235,24,236,107,39,137,28,211,85,18,226,7,41,82,20,
-  193,20,201,183,43,194,206,14,180,42,146,98,240,85,192,189,119,61,200,87,222,244,179,213,181,227,39,167,110,253,223,191,
-  218,182,124,245,106,169,100,55,245,250,52,81,189,74,28,107,180,112,185,230,82,169,69,32,166,196,247,124,246,29,28,227,
-  246,59,238,67,27,151,81,165,141,201,120,59,182,101,222,174,60,197,64,127,63,73,146,16,134,97,139,88,88,200,231,105,
-  107,239,192,243,3,6,7,7,169,84,42,204,205,205,209,222,222,222,250,190,166,61,135,148,110,11,150,38,9,245,122,157,
-  90,173,70,28,199,88,225,128,74,99,140,219,130,9,129,54,218,89,148,134,1,7,15,236,227,228,137,227,12,15,15,83,
-  40,20,1,137,209,6,147,26,124,207,35,159,11,153,26,61,70,207,238,221,227,253,134,251,45,60,2,204,245,214,27,109,
-  187,190,241,80,95,99,243,154,210,200,234,97,172,81,212,183,29,142,147,199,182,61,225,195,19,59,96,186,0,105,23,148,
-  171,48,30,9,14,43,193,145,94,152,30,56,62,158,238,190,243,238,226,131,251,15,149,114,131,35,98,120,195,57,76,157,
-  154,162,246,224,35,199,122,96,219,218,249,250,232,182,187,31,236,248,242,216,104,225,220,70,116,178,95,235,7,124,216,183,
-  175,171,20,7,107,6,201,21,67,70,67,159,205,135,79,49,3,204,0,135,54,44,231,67,29,121,57,210,209,97,143,143,
-  79,197,1,148,115,112,202,194,97,96,79,0,79,120,240,100,93,136,163,58,80,245,131,66,216,47,26,155,92,15,179,21,
-  24,19,48,177,222,82,111,63,116,212,191,247,193,199,59,146,145,149,254,202,117,43,193,68,24,237,52,88,88,75,123,87,
-  7,163,247,127,179,209,55,53,191,61,128,123,219,106,141,253,114,235,174,74,245,174,175,219,164,26,133,114,100,73,40,59,
-  219,178,248,69,7,40,123,74,183,54,175,205,121,64,168,166,215,143,51,169,55,194,45,37,242,249,2,39,103,102,100,124,
-  223,67,39,74,136,175,74,248,98,9,158,88,1,79,175,77,210,189,235,102,203,199,87,30,25,157,146,143,110,175,142,223,
-  253,141,248,208,61,247,153,167,190,122,143,61,240,228,14,230,199,103,68,106,60,225,229,138,104,1,75,150,143,112,201,43,
-  95,204,214,208,43,28,222,177,163,115,56,210,70,194,9,3,79,3,251,103,225,216,1,56,49,14,39,230,225,196,180,251,
-  56,94,132,99,2,14,249,176,199,135,157,13,120,58,129,35,67,48,247,215,160,223,248,182,183,225,9,249,125,64,197,82,
-  210,240,2,134,70,15,145,28,218,201,88,117,158,149,63,34,211,176,26,112,245,161,67,220,118,242,36,226,242,203,88,210,
-  209,65,242,221,2,212,192,0,104,237,214,132,149,125,48,154,194,118,3,143,117,193,133,111,49,233,165,7,238,252,220,249,
-  31,219,241,224,234,235,255,248,55,11,215,94,117,1,113,89,59,169,187,9,176,214,195,47,6,228,218,219,209,170,13,132,
-  36,39,75,124,225,75,119,114,255,79,255,82,114,235,248,132,255,200,138,165,253,43,55,111,86,66,150,72,210,136,90,99,
-  14,99,5,214,74,200,172,54,155,160,176,187,187,73,60,21,114,114,116,130,212,56,150,181,209,169,115,198,75,77,166,60,
-  119,164,182,52,77,169,214,106,52,50,15,231,92,46,68,8,168,55,26,89,102,151,243,140,158,154,158,98,110,110,142,129,
-  129,129,5,155,212,108,164,211,70,163,50,207,155,52,117,164,198,106,181,130,231,251,36,113,130,206,120,63,90,107,242,249,
-  60,169,209,68,81,196,83,219,183,227,73,65,165,90,161,84,106,39,8,66,26,245,26,105,154,96,140,231,112,138,92,136,
-  17,228,51,130,123,213,131,71,19,40,108,158,156,11,182,255,250,159,95,227,255,253,255,238,63,247,134,23,210,144,70,130,
-  75,201,121,8,196,91,193,222,13,118,9,212,102,45,117,96,178,33,196,49,15,14,92,147,166,7,199,239,184,247,138,39,
-  239,121,96,237,209,87,221,218,21,172,88,45,165,146,149,78,109,190,14,204,223,92,111,28,89,115,108,236,188,46,216,149,
-  194,148,5,253,226,137,57,246,221,189,29,5,36,229,6,201,162,243,159,79,13,129,48,70,9,56,1,244,129,93,14,141,
-  163,48,30,193,196,90,144,135,193,204,251,210,188,125,197,32,7,106,49,155,142,79,48,15,209,52,28,233,132,57,5,199,
-  151,193,161,225,67,199,175,121,236,55,254,232,162,175,253,226,79,12,190,224,167,94,169,2,79,19,55,98,132,214,116,117,
-  21,232,190,228,60,191,190,247,120,84,128,29,177,146,7,75,218,12,218,153,249,205,245,127,251,216,150,185,207,127,229,156,
-  220,143,191,108,73,223,107,94,150,207,181,21,33,174,97,50,224,88,217,108,3,108,29,7,72,169,192,57,88,26,231,221,
-  108,132,32,169,85,24,222,114,190,216,147,15,252,161,70,50,223,64,108,75,5,147,214,216,64,64,187,133,158,60,12,110,
-  132,193,77,38,237,53,19,211,131,241,196,116,87,125,247,225,82,237,243,95,27,56,224,121,67,71,6,7,11,233,178,193,
-  112,186,183,35,23,172,90,233,47,61,247,60,30,127,237,107,6,30,250,208,39,94,116,101,172,27,10,198,139,48,243,105,
-  136,62,212,215,205,96,123,27,7,14,30,109,221,252,127,215,65,28,81,46,107,36,174,200,232,53,197,214,240,254,253,48,
-  161,133,36,108,212,8,14,63,206,227,227,71,88,251,28,225,72,223,239,163,22,69,60,241,205,135,185,229,242,203,232,250,
-  30,139,144,7,12,131,57,8,213,21,80,59,8,147,9,28,154,23,114,247,10,107,47,93,118,124,226,202,135,126,227,109,
-  91,202,191,242,203,3,183,254,216,11,105,164,117,148,113,166,233,165,206,78,164,215,71,66,153,128,28,159,249,216,167,120,
-  232,215,126,179,114,203,196,116,53,133,98,254,130,11,242,189,67,75,49,186,76,101,126,28,27,57,5,181,54,41,70,91,
-  252,64,185,13,136,118,43,82,109,44,105,170,217,127,240,176,75,193,96,193,71,88,32,72,181,65,41,71,92,75,226,20,
-  107,156,245,66,18,167,248,126,64,177,88,36,31,58,249,130,231,121,142,207,83,169,180,186,35,173,77,198,204,21,89,2,
-  170,201,220,236,154,210,15,141,214,154,98,91,137,40,170,103,250,38,183,190,215,58,37,151,207,241,248,147,79,114,248,208,
-  97,150,45,91,74,165,82,71,14,73,192,9,92,141,78,73,211,8,131,34,150,109,52,82,81,144,216,206,216,193,242,39,
-  21,124,221,3,177,121,114,142,253,191,246,167,215,242,119,165,190,153,184,33,187,93,60,149,120,11,240,242,204,175,169,238,
-  220,41,237,16,212,235,214,30,30,133,153,72,136,163,157,214,238,185,161,145,108,153,253,216,231,54,77,75,217,222,5,219,
-  53,108,87,176,47,129,61,171,140,89,38,92,103,187,207,135,248,162,106,196,37,7,198,91,55,175,33,224,224,34,166,188,
-  56,203,242,35,155,108,116,7,232,70,246,30,241,172,68,90,129,201,58,247,127,129,244,245,48,85,133,39,187,221,181,118,
-  228,170,36,57,122,236,29,31,184,242,246,3,251,55,92,249,91,191,144,31,233,233,163,81,157,69,10,205,210,27,94,16,
-  158,250,212,87,250,243,113,154,151,150,195,8,177,211,90,187,35,132,39,194,83,83,23,215,254,254,253,151,237,251,210,125,
-  155,251,222,242,250,129,190,235,174,240,115,93,3,136,70,13,97,83,36,22,147,166,238,153,25,157,73,187,36,224,12,225,
-  172,110,48,178,106,5,187,54,172,239,74,158,220,62,4,20,123,44,7,31,2,221,11,19,75,224,112,23,132,51,144,143,
-  17,5,129,237,244,160,216,14,133,18,44,85,105,122,142,62,126,124,40,61,126,220,31,0,111,30,218,27,208,115,89,46,
-  223,145,211,4,13,33,86,230,172,93,86,129,29,63,37,68,237,215,63,245,46,214,94,123,13,47,125,201,143,243,197,47,
-  221,115,26,141,166,150,25,252,93,189,232,152,126,207,82,140,102,247,55,170,35,186,142,236,100,213,232,17,118,192,115,182,
-  114,127,46,120,67,68,17,247,126,243,97,174,187,252,50,58,59,58,90,196,188,239,5,240,14,192,182,67,180,27,78,46,
-  179,102,182,140,56,26,192,209,107,103,235,149,187,255,234,237,47,188,179,144,239,187,233,213,215,17,71,51,20,114,125,248,
-  133,54,12,49,129,24,224,99,239,127,55,123,126,229,183,199,95,90,105,60,29,184,147,176,118,217,11,175,42,42,124,234,
-  177,38,169,205,227,153,186,179,68,85,202,217,36,32,72,18,237,232,241,64,16,228,72,146,148,39,183,62,69,35,74,240,
-  61,129,177,41,24,213,98,164,57,129,161,19,192,234,212,129,195,133,66,129,48,8,72,19,195,124,92,163,119,64,58,14,
-  84,163,65,173,90,35,23,132,217,136,119,198,5,39,197,105,120,16,64,18,199,36,73,12,130,76,107,102,90,65,127,179,
-  179,179,220,127,255,55,72,210,148,249,185,121,226,40,198,247,67,146,36,38,142,99,226,36,194,226,145,198,101,148,138,137,
-  124,169,68,172,125,235,8,9,169,239,64,233,123,37,232,181,147,51,122,247,175,252,246,213,39,7,10,225,53,80,21,160,
-  119,0,247,1,31,3,126,43,3,56,243,110,148,215,255,2,211,55,88,91,85,112,60,22,98,91,27,118,77,187,49,69,
-  11,199,37,236,170,194,120,23,140,213,97,123,10,166,46,101,237,27,195,189,122,133,239,157,150,95,158,19,130,227,99,83,
-  20,234,17,241,183,120,47,136,51,254,44,206,120,223,231,28,152,109,39,160,254,98,56,44,97,38,129,227,35,48,154,255,
-  210,3,47,186,227,196,196,69,235,127,253,87,74,87,93,184,14,157,206,227,15,245,49,223,221,217,63,56,54,57,130,49,
-  185,57,24,87,80,109,131,113,33,196,254,146,181,187,114,251,14,93,89,253,253,191,188,120,124,211,186,181,226,101,47,238,
-  94,243,226,27,188,222,193,110,68,92,65,103,90,178,38,107,217,136,24,35,20,34,219,156,182,21,187,105,223,114,65,169,
-  241,228,246,21,121,88,90,135,253,151,64,101,22,204,16,152,149,144,252,37,84,126,222,90,49,5,199,43,32,102,92,154,
-  111,41,129,135,45,116,20,192,203,131,44,66,73,66,31,141,122,95,10,131,218,109,196,70,243,136,184,108,45,203,239,126,
-  16,174,125,5,27,54,110,56,173,0,125,39,55,251,239,234,130,180,22,166,106,85,198,100,76,199,98,178,220,243,236,209,
-  136,34,190,254,205,135,121,225,229,151,225,117,118,226,125,7,27,178,179,233,198,196,130,67,162,189,20,170,59,176,251,231,
-  161,170,133,240,175,141,211,206,47,252,237,187,175,28,216,60,84,218,114,241,58,114,94,63,34,168,33,100,59,31,124,199,
-  59,25,251,173,223,61,126,83,212,248,134,134,29,22,86,207,230,139,107,87,95,122,190,80,212,209,181,50,164,150,84,7,
-  88,45,209,52,220,109,86,40,84,38,85,208,169,65,169,54,118,239,61,194,201,147,167,92,168,96,22,146,103,50,55,69,
-  175,233,189,108,93,56,93,189,94,199,247,61,230,203,179,228,146,156,219,194,120,30,214,172,68,24,67,212,168,97,172,118,
-  235,220,38,246,211,202,102,207,248,24,89,122,130,1,231,49,29,69,206,134,213,104,116,154,102,221,86,66,24,6,60,249,
-  196,99,148,231,102,41,21,243,36,105,76,35,106,100,99,90,246,243,165,159,177,101,53,185,80,226,11,105,45,90,148,64,
-  252,155,235,28,244,159,194,216,46,248,134,133,198,218,153,185,67,29,149,249,110,43,217,17,26,202,87,184,84,52,74,25,
-  126,88,207,126,178,6,118,3,111,133,72,193,216,46,107,167,27,176,39,20,194,171,75,17,89,109,170,239,242,189,244,173,
-  198,38,161,214,141,195,64,163,187,131,111,44,25,224,65,125,58,101,67,41,201,73,33,248,229,131,39,158,181,0,125,55,
-  239,153,28,240,9,208,183,194,76,55,108,77,161,218,13,241,181,219,246,218,79,253,202,111,111,25,253,221,95,45,189,230,
-  213,55,83,236,21,212,134,58,58,205,216,228,50,31,250,234,112,98,24,146,97,40,111,149,226,64,155,182,147,33,28,44,
-  193,246,252,206,189,151,204,236,220,123,254,163,159,248,236,170,244,69,215,247,174,127,205,139,228,218,149,61,4,113,13,83,
-  49,52,82,1,94,128,210,9,74,164,104,37,17,166,74,215,85,23,4,99,239,249,208,170,53,150,115,12,60,5,84,125,
-  176,126,118,179,62,226,206,129,109,128,126,218,9,181,211,155,161,113,24,102,134,64,238,2,209,6,98,0,60,1,97,234,
-  146,96,58,178,17,234,212,56,182,118,29,80,124,247,135,225,143,222,10,66,127,247,13,195,119,116,96,133,196,0,115,149,
-  154,243,167,201,188,75,44,207,223,71,45,138,56,255,155,15,115,217,196,4,137,82,24,241,253,149,74,15,184,0,116,55,
-  140,167,214,62,0,124,249,134,137,241,39,119,254,233,59,235,185,96,53,109,61,189,88,217,205,71,255,242,159,152,252,141,
-  223,60,246,130,168,113,183,134,219,45,236,139,161,48,191,102,99,199,138,181,27,193,84,80,113,29,163,45,41,22,45,42,
-  24,17,103,33,115,26,99,82,164,213,40,233,168,246,123,246,29,165,92,174,226,43,233,66,246,204,226,188,115,219,18,204,
-  98,33,77,83,2,223,35,151,11,240,125,137,177,41,65,224,19,6,62,2,75,163,94,39,201,212,210,14,247,113,96,119,
-  83,41,45,113,98,198,32,8,9,124,31,107,13,141,70,131,184,17,145,100,192,116,212,104,224,7,62,211,211,211,60,245,
-  212,54,2,223,233,146,242,249,128,56,142,136,227,40,187,176,61,60,149,209,249,133,64,6,62,210,247,91,117,206,7,113,
-  151,235,50,181,128,49,11,95,71,240,193,37,154,119,74,195,253,101,152,237,7,126,26,120,109,214,253,248,139,72,107,217,
-  104,68,135,139,48,140,34,152,94,109,237,169,138,54,115,13,41,210,137,149,35,252,159,229,67,220,213,81,34,1,62,219,
-  221,142,106,196,200,40,125,198,71,162,159,219,119,114,0,236,3,251,110,168,247,193,222,58,220,81,144,226,246,31,47,215,
-  31,158,250,139,127,152,189,235,182,187,40,118,15,209,253,194,171,75,115,176,82,192,112,55,132,99,192,161,165,195,220,38,
-  68,218,6,211,35,240,84,10,183,11,120,111,63,188,103,195,161,163,159,233,123,247,251,31,249,230,27,127,105,226,115,255,
-  252,49,115,232,100,3,89,232,193,107,11,65,214,145,194,195,218,34,58,205,147,214,35,86,92,184,145,227,171,151,142,36,
-  112,142,129,37,101,8,206,119,16,3,53,22,34,149,197,233,98,109,0,29,67,242,147,16,255,6,68,18,170,19,48,189,
-  27,78,204,57,238,220,110,11,147,135,65,231,0,101,191,143,137,229,219,147,11,21,158,209,84,230,43,46,127,92,8,254,
-  163,60,186,162,136,223,125,240,33,126,126,199,14,26,158,71,34,229,247,84,54,69,182,153,59,8,108,133,164,29,142,104,
-  248,106,27,124,101,237,55,183,239,251,210,255,125,39,141,134,229,206,95,255,221,200,254,238,31,61,125,121,170,191,20,195,
-  103,44,60,233,9,81,152,130,165,237,55,95,87,234,236,25,164,17,107,146,52,197,88,13,162,225,140,226,61,7,24,75,
-  233,10,137,182,77,27,77,143,67,135,142,208,204,55,183,100,248,79,214,13,37,73,66,146,164,45,169,72,163,81,207,100,
-  68,34,35,34,6,180,181,181,99,45,52,26,13,106,149,106,150,7,238,182,93,77,75,6,33,197,130,129,89,22,84,87,
-  44,21,169,215,235,206,80,107,33,34,47,243,149,150,60,244,208,131,212,170,85,4,130,192,15,72,181,161,209,168,19,69,
-  81,246,124,220,129,51,198,32,172,34,159,47,34,60,217,58,250,33,112,0,248,128,251,179,46,193,244,180,101,207,148,177,
-  59,13,140,190,11,226,165,89,231,211,147,73,113,78,102,255,238,91,61,20,48,213,222,70,90,40,112,220,83,226,51,253,
-  221,76,251,222,247,124,145,124,175,227,123,30,120,63,240,215,208,232,130,253,210,218,47,74,33,62,241,242,114,253,75,242,
-  247,255,252,208,151,254,244,29,182,227,156,243,68,226,201,162,128,124,0,42,41,20,184,125,203,38,246,158,187,156,82,33,
-  180,195,16,23,224,148,132,173,53,248,130,17,226,125,67,240,111,215,78,204,222,214,255,183,239,127,228,209,31,255,31,227,
-  95,123,251,187,147,137,163,167,80,165,30,76,24,144,40,67,138,65,164,138,174,66,55,118,195,186,194,60,172,148,176,172,
-  13,242,191,8,172,206,54,201,143,103,29,219,183,42,12,205,73,96,30,56,1,118,214,221,48,180,1,27,60,7,174,23,
-  223,242,225,123,1,113,220,224,166,237,15,242,249,193,181,212,242,37,22,242,35,254,227,60,94,114,232,48,162,82,225,223,
-  55,111,102,214,247,191,43,29,153,205,222,76,63,9,124,57,251,218,79,64,227,23,225,64,10,247,244,65,223,145,119,254,
-  115,225,75,95,249,106,91,241,177,39,183,173,132,111,212,225,126,31,118,10,65,155,176,118,233,177,32,24,217,252,162,23,
-  10,73,149,212,206,97,109,140,180,206,30,65,10,133,209,100,118,12,174,250,184,20,76,69,20,165,28,61,54,234,128,229,
-  36,93,176,118,205,56,64,77,187,141,166,104,49,73,19,210,196,145,202,42,213,170,91,183,183,181,163,148,200,54,37,134,
-  66,46,143,144,146,56,137,1,129,31,248,24,227,152,177,2,233,214,238,153,71,241,244,244,36,113,220,64,27,77,163,209,
-  192,247,125,60,223,103,247,238,61,236,221,179,151,92,232,236,52,172,181,78,31,100,52,229,242,28,185,92,216,26,197,154,
-  26,51,63,231,163,23,4,222,173,187,238,59,112,38,85,43,129,74,22,206,209,140,188,94,36,133,250,158,186,109,63,155,
-  79,127,20,157,122,243,245,189,23,184,1,26,67,150,253,158,176,243,137,224,248,185,150,35,39,223,247,241,43,230,191,122,
-  95,223,160,231,111,51,105,116,18,72,162,98,158,40,244,68,161,183,221,70,199,39,17,181,136,28,216,54,136,231,96,188,
-  108,237,108,4,71,66,33,182,143,88,54,143,76,78,159,55,255,222,143,174,63,249,233,47,172,60,122,243,245,35,93,175,
-  127,121,113,233,166,85,120,73,21,146,136,156,77,233,187,226,82,111,223,237,95,27,190,18,150,166,80,186,9,102,207,201,
-  158,91,31,80,254,46,143,173,92,4,206,243,131,44,64,57,47,96,235,216,17,46,57,180,131,142,90,5,59,178,241,91,
-  197,60,63,175,31,22,88,61,49,201,173,15,63,204,23,151,46,37,254,14,95,134,5,84,232,81,236,43,210,103,45,61,
-  217,129,175,10,193,234,40,173,111,159,168,238,202,195,23,150,206,150,39,244,99,79,134,30,236,52,176,53,132,99,57,104,
-  84,45,189,18,122,231,151,246,119,174,88,51,140,52,9,126,45,160,62,171,72,26,9,90,217,204,46,193,137,65,37,158,
-  243,151,22,128,244,40,87,235,204,204,149,93,146,106,83,95,151,233,181,50,143,52,164,116,185,95,22,50,37,123,22,131,
-  99,45,149,114,153,84,59,100,67,107,87,68,38,167,166,24,28,26,66,27,227,236,53,117,150,25,134,6,147,105,172,180,
-  38,78,98,166,166,167,41,228,29,97,177,214,136,90,69,236,145,71,31,161,88,44,58,83,45,207,195,88,67,181,90,165,
-  167,187,135,70,189,65,146,56,47,37,151,129,5,58,241,72,101,129,138,81,207,32,126,236,3,30,2,54,101,221,139,121,
-  142,222,220,103,43,6,63,170,71,4,204,66,114,200,114,242,133,80,158,23,156,28,18,98,155,57,62,90,170,11,113,164,
-  164,228,158,57,109,26,43,227,152,251,26,117,139,49,46,162,121,209,207,104,119,31,81,25,198,231,173,157,17,66,28,70,
-  200,199,74,66,172,108,159,47,159,87,253,212,231,46,57,244,229,187,207,219,127,203,13,131,171,95,249,226,96,205,121,107,
-  177,74,177,246,178,45,124,165,191,171,199,156,154,89,146,66,231,18,24,237,118,93,12,41,240,89,92,224,230,143,66,153,
-  254,172,191,179,232,7,220,115,226,32,159,223,245,8,55,103,99,216,127,228,135,197,121,10,13,87,107,60,182,123,15,243,
-  89,31,247,237,94,149,1,122,54,13,176,230,61,175,227,252,70,210,122,67,4,161,199,145,253,19,54,120,203,39,103,195,
-  122,250,88,13,14,27,183,217,153,42,192,108,9,210,237,32,87,128,104,128,39,134,151,132,125,67,75,64,38,164,182,76,
-  156,206,145,16,147,106,237,178,211,180,51,244,210,194,173,197,141,209,4,126,200,190,253,123,153,154,158,35,8,115,89,170,
-  230,162,43,202,100,209,127,214,233,194,76,150,233,222,244,152,9,130,160,149,243,21,199,49,24,75,173,90,229,216,209,163,
-  244,245,15,144,102,244,123,41,165,43,70,153,69,131,84,46,158,55,141,83,78,157,154,96,233,210,17,170,181,90,203,14,
-  116,219,142,29,140,79,76,48,60,56,224,114,231,27,53,230,230,202,88,107,153,157,157,161,90,173,96,76,63,158,231,161,
-  73,51,103,65,73,42,13,54,244,229,217,238,182,31,199,197,35,249,217,197,250,159,237,225,3,211,238,195,62,12,243,203,
-  44,187,114,214,30,77,65,21,125,191,241,84,28,215,255,16,204,137,185,50,183,140,79,225,183,121,207,90,68,219,220,71,
-  60,111,237,68,206,218,233,26,28,138,29,227,120,247,134,249,242,11,231,63,249,239,91,14,124,241,43,75,182,95,123,69,
-  207,218,55,252,88,176,246,242,43,232,219,120,78,208,56,245,64,78,65,104,23,185,202,248,217,53,48,138,243,109,87,207,
-  135,2,84,244,3,238,61,126,128,247,239,126,148,206,31,192,221,232,71,253,40,0,159,92,24,165,190,109,225,66,128,240,
-  37,42,149,173,55,129,39,5,81,123,142,193,246,208,60,89,79,103,111,128,249,0,120,59,216,139,221,156,204,52,216,21,
-  78,241,59,90,124,122,207,169,143,188,237,15,150,47,191,98,11,253,131,253,12,181,15,225,217,105,114,170,141,184,58,141,
-  64,99,108,232,50,161,132,15,66,16,4,121,142,30,61,73,28,155,86,212,110,147,249,108,157,149,139,43,26,210,81,252,
-  109,43,194,215,109,183,234,181,58,245,122,157,48,12,241,148,34,214,9,167,38,38,168,84,42,207,72,220,212,38,235,156,
-  204,194,10,190,94,143,40,151,43,204,205,151,201,231,10,132,97,200,212,204,12,199,30,127,188,209,38,165,138,226,216,239,
-  234,233,116,73,17,137,38,137,28,151,105,102,118,150,254,106,149,66,62,159,153,162,25,148,210,120,42,38,202,73,121,182,
-  134,100,183,227,209,240,42,254,115,63,60,96,7,176,20,226,99,16,231,129,210,77,87,146,91,49,200,137,127,250,184,123,
-  207,125,155,72,225,69,155,103,91,130,180,8,243,7,160,218,14,51,41,140,182,193,246,77,213,218,218,202,151,190,182,246,
-  216,157,247,174,20,155,206,41,92,52,57,121,12,216,173,96,38,6,83,57,227,186,142,29,190,195,16,252,80,199,85,239,
-  217,138,207,251,118,63,250,221,173,201,254,131,62,222,149,97,59,133,111,83,133,172,113,24,71,11,143,48,22,180,107,147,
-  143,2,189,142,153,195,49,224,66,224,114,96,5,216,105,167,66,121,248,146,169,153,158,177,191,122,215,229,251,61,57,242,
-  228,210,161,206,210,133,23,22,251,183,44,83,165,117,151,176,118,237,106,74,254,12,245,90,68,16,228,29,94,163,53,66,
-  10,142,30,61,74,162,173,75,218,104,122,49,11,167,126,55,194,100,91,48,183,138,39,179,224,108,90,105,68,113,140,78,
-  83,252,192,39,8,3,202,243,21,102,166,167,221,184,102,108,43,230,87,9,73,98,210,140,5,237,108,65,165,116,88,144,
-  167,60,242,249,2,113,148,224,43,159,189,251,246,243,170,169,169,169,109,61,221,249,131,97,208,45,148,114,91,81,79,97,
-  234,49,198,58,32,60,142,35,114,97,136,20,206,49,47,77,53,167,166,230,145,58,80,153,171,18,157,96,123,178,21,187,
-  197,241,125,150,0,23,253,144,47,130,31,5,54,180,248,186,210,75,6,24,122,221,141,144,21,160,239,118,172,44,0,79,
-  128,190,2,166,82,167,191,218,39,161,175,0,203,55,26,189,212,108,223,30,26,152,180,82,238,212,198,140,111,2,179,129,
-  103,26,52,75,135,193,177,251,71,85,128,206,44,62,207,199,49,202,126,171,78,229,123,120,236,199,153,223,255,222,115,244,
-  28,115,192,71,129,123,178,150,251,106,168,156,11,79,104,152,29,134,39,71,82,179,70,31,58,177,180,118,232,68,127,249,
-  51,12,29,232,40,141,60,116,238,218,174,53,175,185,53,184,238,166,11,32,73,49,54,64,72,69,189,150,242,244,206,253,
-  68,105,130,239,41,23,5,148,113,126,180,113,186,32,165,188,214,129,145,66,97,172,35,11,166,105,74,20,69,72,169,240,
-  85,72,146,164,212,234,53,202,213,50,169,78,152,153,157,164,189,171,141,36,73,145,210,169,223,61,33,208,169,198,42,231,
-  114,23,248,30,74,73,106,229,50,126,143,199,241,153,9,186,79,30,169,94,232,137,233,199,69,184,52,78,34,82,29,19,
-  120,121,106,166,1,194,144,36,49,141,70,204,220,220,60,133,124,17,37,4,66,26,142,29,29,101,223,137,99,108,136,202,
-  210,66,177,12,185,95,3,249,151,89,94,195,226,115,248,24,206,61,204,240,255,200,35,77,73,171,245,239,235,71,152,108,
-  34,159,131,218,90,168,165,48,49,9,135,114,144,87,66,72,35,101,162,181,174,134,16,165,223,98,252,249,97,99,101,222,
-  243,181,248,216,179,108,63,100,118,119,236,18,66,230,133,224,45,198,216,243,129,186,227,129,144,126,31,7,240,95,221,29,
-  153,151,63,7,179,254,195,89,39,4,112,23,152,79,193,140,15,79,213,224,160,231,172,96,123,124,232,25,128,21,75,231,
-  42,91,230,31,120,242,226,175,76,79,175,191,232,218,75,242,29,57,139,173,67,222,11,24,155,172,48,54,117,18,223,115,
-  62,65,206,22,214,37,17,52,237,51,28,33,217,189,106,99,156,249,124,24,6,132,97,64,20,53,16,66,81,42,181,161,
-  148,199,204,236,44,81,163,129,84,146,233,233,41,150,44,91,146,117,47,26,161,157,19,162,148,210,129,210,198,1,219,243,
-  115,51,4,74,146,6,146,147,199,14,218,159,47,151,119,14,167,246,120,151,79,71,156,212,58,203,179,51,148,74,61,228,
-  194,128,164,94,195,90,147,217,126,164,40,207,167,90,174,112,252,248,113,70,79,142,146,120,130,137,134,9,82,23,21,215,
-  61,233,204,192,158,193,253,91,130,147,94,180,159,229,61,225,101,56,133,230,191,30,103,219,78,153,236,184,41,208,71,161,
-  50,0,21,191,144,231,112,103,59,246,196,216,183,223,60,253,40,10,80,201,15,184,231,135,92,124,100,230,222,159,1,14,
-  167,223,16,178,55,153,196,41,225,113,40,189,88,46,68,174,96,109,151,18,162,77,11,33,127,25,140,6,61,6,113,27,
-  204,231,96,190,243,123,188,113,158,194,81,253,159,2,126,253,57,124,157,198,117,69,86,64,60,235,136,101,51,195,112,244,
-  8,248,41,108,5,118,151,96,118,243,190,35,165,135,62,243,232,154,87,252,220,139,136,235,179,228,148,199,145,147,135,152,
-  169,214,9,50,202,157,102,33,218,89,156,102,21,75,43,194,215,121,240,116,48,54,54,70,181,90,109,121,243,52,85,237,
-  113,146,64,98,153,158,158,166,86,173,17,71,17,190,239,172,52,13,206,228,160,201,51,82,74,209,219,211,231,120,64,149,
-  10,29,149,114,125,189,101,42,134,184,88,176,169,177,41,81,37,38,31,54,144,38,193,24,75,45,170,227,123,1,149,74,
-  5,157,166,204,204,204,112,252,248,9,82,29,225,135,57,142,7,65,190,2,61,237,208,86,1,249,123,110,59,116,26,248,
-  41,178,177,108,219,25,199,50,1,254,12,120,159,27,121,255,235,241,109,138,145,93,116,60,237,162,209,239,249,52,222,122,
-  29,65,142,175,30,219,199,251,127,136,197,71,0,181,52,161,166,19,33,114,69,100,154,218,102,225,153,119,237,119,216,13,
-  109,83,80,58,7,228,28,36,155,17,73,44,212,176,181,233,197,21,99,214,104,240,27,144,134,142,137,57,147,192,142,10,
-  60,249,155,48,62,233,138,18,179,223,67,27,255,33,215,181,112,103,118,17,60,87,175,183,201,48,77,192,46,117,192,97,
-  122,24,234,26,234,17,20,86,165,44,253,234,63,189,179,247,241,115,251,58,95,112,233,38,76,108,216,189,247,8,105,234,
-  17,72,141,16,6,107,85,43,21,193,117,60,38,51,46,115,0,178,51,47,183,248,190,79,189,94,119,172,232,32,32,159,
-  119,78,134,113,28,19,71,49,22,195,252,220,28,113,20,97,180,195,125,48,6,154,60,164,204,116,44,8,67,250,251,7,
-  24,30,25,228,224,241,147,204,167,135,180,208,182,77,66,187,110,196,62,97,145,52,85,232,84,210,214,214,78,146,66,165,
-  92,161,90,173,50,49,49,65,28,39,116,117,117,209,221,221,197,201,209,19,144,164,32,172,48,66,120,202,90,85,5,241,
-  101,156,213,174,127,150,110,39,231,192,218,211,58,225,175,3,247,2,111,248,175,26,243,159,3,148,255,231,29,15,242,240,
-  248,177,31,218,88,5,153,255,206,212,73,222,246,228,221,246,47,58,251,81,167,142,52,145,120,175,12,61,33,172,246,96,
-  163,116,221,120,232,65,69,99,103,165,213,195,147,112,237,216,11,175,61,239,188,23,92,42,102,39,39,244,215,239,123,208,
-  118,60,189,119,242,74,120,56,114,222,190,247,127,22,198,151,131,249,48,176,231,123,120,158,163,192,205,25,56,253,214,231,
-  104,46,22,103,220,153,194,236,2,27,135,121,207,25,116,223,127,237,236,204,242,175,188,245,207,207,143,254,244,119,243,183,
-  190,248,6,14,29,61,134,109,8,116,62,71,98,26,88,76,11,136,110,146,16,91,157,86,182,189,114,49,44,130,70,163,
-  225,142,117,62,79,24,134,204,206,206,82,46,87,156,197,134,175,72,146,132,90,181,138,213,198,113,90,105,70,234,209,202,
-  187,87,202,3,41,41,150,74,188,232,133,55,178,173,187,163,248,205,219,62,222,251,210,24,211,83,179,70,229,114,4,185,
-  60,169,182,120,40,124,223,39,159,207,145,38,186,37,219,104,111,107,99,249,178,229,204,151,231,169,233,24,109,61,18,161,
-  66,109,211,188,15,222,83,124,119,225,147,54,187,81,213,255,235,218,253,207,81,128,238,31,61,252,67,41,60,249,69,179,
-  251,91,64,204,3,249,218,60,203,106,243,210,130,122,28,194,14,24,66,136,203,140,181,87,31,18,226,130,253,109,249,145,
-  178,231,7,75,202,149,202,154,68,79,117,89,27,142,14,14,44,187,244,207,254,160,125,96,73,55,131,186,206,242,95,253,
-  73,238,248,200,167,186,238,251,235,127,41,93,19,107,171,161,190,23,238,15,97,214,251,62,218,205,49,224,175,113,174,89,
-  127,225,73,46,15,60,102,107,9,223,143,10,165,153,240,33,113,196,187,221,174,48,217,115,96,70,193,145,18,76,174,28,
-  157,136,126,251,247,254,62,191,231,104,196,145,99,147,248,158,33,53,202,229,145,147,182,58,158,230,248,181,240,57,91,163,
-  91,75,28,199,148,203,142,151,211,214,214,70,163,209,200,64,233,6,82,58,161,105,28,199,212,106,245,108,156,115,193,132,
-  126,150,57,238,140,254,21,65,16,16,197,17,59,158,126,26,99,44,231,93,118,177,124,104,244,196,242,13,247,126,99,180,
-  61,49,137,78,35,27,9,45,146,58,200,176,68,154,56,158,82,169,88,68,72,183,137,51,198,80,106,43,177,102,205,90,
-  158,220,177,29,163,13,13,107,187,36,44,139,160,227,56,84,58,157,24,242,59,46,244,2,199,222,205,204,40,254,235,241,
-  31,185,0,61,151,128,113,83,165,108,179,121,61,7,108,2,177,20,188,13,142,89,95,24,135,194,171,193,111,146,160,170,
-  80,208,208,51,15,189,1,172,173,90,123,213,232,186,85,151,180,255,204,27,251,46,95,191,138,64,133,236,123,236,137,222,
-  59,191,124,207,210,104,215,238,116,175,151,250,235,38,142,146,51,147,40,105,200,123,138,159,124,243,171,248,55,196,208,67,
-  127,250,206,23,92,13,83,198,137,230,182,11,168,174,201,166,176,239,181,110,220,11,188,124,207,4,159,127,242,4,151,159,
-  55,196,108,57,250,174,139,80,179,8,158,4,190,8,252,21,142,108,55,147,225,77,109,144,171,67,239,140,16,61,95,90,
-  186,169,237,156,45,215,51,58,238,115,228,248,52,50,144,36,137,70,170,44,86,165,25,137,44,23,46,189,166,129,188,51,
-  17,211,84,170,85,26,141,70,22,1,227,183,204,198,92,18,134,32,8,2,146,56,166,50,63,15,38,75,212,108,30,33,
-  65,43,39,94,74,69,177,80,32,73,19,142,28,57,194,255,207,222,123,135,89,118,149,87,222,191,189,247,57,231,158,155,
-  43,231,234,234,156,179,164,86,4,101,129,4,152,12,6,27,99,123,28,39,224,48,51,30,143,103,252,217,51,223,140,199,
-  51,159,51,99,112,24,27,27,147,49,89,36,33,161,156,67,119,171,115,206,149,211,205,39,237,189,191,63,206,173,150,192,
-  2,36,1,99,140,123,63,79,61,221,93,93,85,125,251,222,123,214,121,223,245,174,119,173,92,87,137,242,186,205,217,227,
-  247,63,86,144,214,46,34,173,49,132,202,104,75,28,57,248,217,44,149,197,69,234,141,6,158,151,161,217,108,210,221,213,
-  69,24,134,148,203,101,214,172,88,193,179,179,115,76,98,187,87,192,166,12,140,30,131,233,127,15,145,126,137,96,146,144,
-  50,217,241,165,107,248,159,33,0,9,65,44,101,123,177,51,253,33,101,96,29,136,60,56,11,224,204,129,19,67,126,53,
-  20,5,116,91,232,7,134,60,232,79,192,79,131,121,83,190,81,66,191,130,238,105,232,173,92,187,107,100,231,127,255,181,
-  114,247,64,39,34,104,130,54,108,126,211,141,220,116,251,13,234,196,185,115,234,200,193,189,228,85,128,20,14,73,98,112,
-  157,2,245,133,38,175,121,253,107,249,235,135,158,90,118,252,190,39,110,90,13,81,11,58,250,224,184,7,51,7,161,54,
-  10,201,203,29,235,206,84,2,94,243,11,159,230,139,127,246,166,139,32,244,82,72,104,128,101,109,0,218,0,44,127,222,
-  196,194,1,21,66,191,7,27,191,226,23,87,230,111,255,247,106,108,104,45,141,153,67,84,195,6,70,105,156,184,157,217,
-  69,202,249,44,129,207,82,37,180,196,9,201,118,96,95,179,209,32,12,67,114,217,108,170,70,214,154,102,179,73,171,213,
-  66,42,117,209,86,181,213,10,8,162,224,185,239,109,47,168,218,118,210,156,239,251,228,242,121,42,149,69,130,8,206,158,
-  159,167,114,104,143,185,206,198,250,110,225,150,26,137,80,69,55,139,17,128,150,72,55,213,251,88,99,218,85,86,10,214,
-  75,153,230,35,3,3,84,54,175,227,161,39,22,203,151,53,163,181,192,198,60,156,252,63,48,189,30,108,242,50,110,124,
-  230,210,53,252,207,3,128,44,16,73,7,227,120,104,27,176,97,102,130,53,243,51,116,130,188,17,252,50,228,37,116,132,
-  208,211,132,78,153,182,246,125,10,6,99,24,170,194,80,5,122,106,80,182,2,133,227,96,188,140,66,73,101,141,206,156,
-  23,248,135,6,250,51,183,189,241,181,114,239,209,147,100,78,9,58,203,37,138,133,28,88,75,214,81,172,28,234,99,253,
-  138,59,72,116,148,142,138,157,212,3,39,209,134,188,178,188,253,63,190,71,125,248,220,111,174,115,79,156,114,135,211,229,
-  233,131,26,246,77,193,161,41,184,224,164,147,178,248,229,84,67,243,149,128,87,255,226,167,249,244,255,126,3,215,108,30,
-  120,158,37,198,55,30,221,110,55,151,196,117,189,164,226,174,9,96,61,233,206,205,210,81,192,60,120,53,24,108,193,234,
-  249,141,151,117,6,54,161,85,15,9,235,53,226,200,160,156,28,56,96,147,22,178,221,34,89,1,86,183,163,163,121,46,
-  129,2,1,81,146,80,111,181,240,252,44,70,107,114,185,60,214,10,170,213,26,113,162,201,250,25,172,77,201,230,74,165,
-  70,171,217,34,49,49,74,40,208,34,13,202,75,119,60,48,218,162,163,4,29,197,100,253,12,243,19,83,120,135,142,55,
-  71,33,156,87,73,167,107,98,154,129,69,43,131,175,138,40,55,3,38,117,89,108,182,34,206,157,187,64,177,80,166,187,
-  171,139,32,9,81,142,195,134,21,107,216,31,132,254,215,159,222,187,250,85,113,184,83,192,222,22,44,220,7,209,203,105,
-  167,18,224,230,239,182,148,183,160,228,243,242,183,196,37,96,248,129,2,32,11,120,214,112,213,133,195,220,239,120,220,214,
-  90,20,73,109,65,90,40,36,48,80,134,49,9,43,37,140,58,48,92,135,190,42,20,103,161,107,54,235,117,68,35,195,
-  133,100,100,89,174,184,98,76,245,173,24,19,163,171,150,211,209,211,73,177,163,156,110,118,43,69,44,211,55,127,80,171,
-  80,175,213,240,179,89,28,165,40,148,10,196,81,8,113,72,61,142,9,230,170,104,29,35,73,205,206,93,79,146,245,179,
-  40,2,86,12,117,241,150,255,249,91,153,191,253,221,63,94,151,28,56,56,182,51,8,119,108,183,28,205,167,142,120,187,
-  45,236,86,82,158,190,222,218,230,110,107,41,191,68,18,121,113,49,224,15,126,247,62,94,241,177,119,18,235,23,190,247,
-  246,1,159,108,19,171,246,121,109,133,126,129,159,23,164,0,228,100,160,179,9,93,131,151,191,194,25,218,182,138,147,143,
-  29,34,168,158,163,101,23,241,113,113,18,137,176,41,73,108,141,109,175,107,136,212,74,213,202,52,5,68,164,235,20,73,
-  162,169,213,27,216,118,75,230,186,94,234,19,100,210,118,76,107,147,110,180,43,39,213,22,181,35,132,93,215,35,245,210,
-  75,227,168,181,182,88,163,201,250,89,226,176,133,178,10,226,128,150,76,172,128,74,54,54,121,153,56,196,194,224,185,14,
-  65,16,210,108,77,161,209,41,178,10,67,179,213,96,247,238,221,172,92,177,130,209,177,101,88,107,112,81,108,216,118,25,
-  7,102,23,123,174,57,126,116,121,6,122,59,64,173,120,153,213,140,37,157,156,46,126,19,217,255,82,142,54,154,225,142,
-  110,166,87,172,164,162,155,200,122,149,75,40,244,3,6,64,202,90,118,206,95,96,38,181,103,204,207,195,72,6,54,40,
-  216,94,133,45,23,96,197,184,82,189,141,145,193,162,90,191,42,59,118,197,78,53,184,102,149,216,177,106,37,203,87,173,
-  160,220,217,133,239,101,83,155,137,139,44,81,59,18,24,131,115,113,15,250,249,102,151,75,69,118,59,128,205,88,116,28,
-  161,117,66,216,74,91,138,11,231,207,113,106,124,130,78,87,210,211,231,179,126,243,10,126,249,189,255,69,158,56,125,54,
-  119,244,177,167,151,127,233,222,71,7,157,131,199,183,94,153,36,59,6,225,158,121,99,191,250,6,56,124,3,52,110,125,
-  57,207,216,254,73,248,127,239,193,253,47,183,242,66,100,208,111,189,132,31,213,72,43,35,87,164,22,152,57,153,93,38,
-  122,251,55,99,215,133,124,241,83,159,194,209,22,87,5,88,225,160,141,66,182,125,127,165,109,87,60,8,172,208,88,76,
-  154,220,106,185,216,110,93,140,207,53,134,40,142,72,146,24,33,5,197,124,30,37,37,58,73,40,149,74,72,36,58,49,
-  224,166,62,63,214,154,139,217,235,162,45,76,76,55,229,45,157,61,157,136,87,222,80,248,203,187,238,46,28,17,210,179,
-  73,154,202,42,132,161,165,107,132,24,60,199,75,109,67,144,88,145,134,234,157,60,125,28,99,34,86,44,91,145,166,104,
-  184,14,174,163,158,167,106,74,95,237,151,187,8,41,72,69,164,241,203,2,49,139,35,125,20,14,227,157,101,60,213,65,
-  62,10,33,142,46,161,195,15,10,0,233,118,27,49,3,110,63,244,87,97,179,15,87,87,96,215,177,172,191,97,230,242,
-  237,131,125,215,93,235,93,253,202,235,88,179,125,19,165,222,18,121,229,165,218,14,27,164,235,5,82,131,105,160,69,234,
-  25,147,218,73,60,103,7,170,137,46,10,234,150,198,192,105,252,111,123,103,201,182,121,138,182,185,186,231,103,41,118,66,
-  223,208,8,213,90,149,35,135,14,179,231,248,41,134,122,106,140,246,117,50,188,105,140,91,119,109,161,246,75,191,152,121,
-  246,196,185,129,3,127,254,119,221,225,223,127,182,60,134,21,85,8,71,225,200,239,66,252,44,223,217,224,234,155,193,56,
-  248,248,94,54,150,125,222,252,238,157,200,246,243,243,219,164,102,101,153,23,57,121,11,128,159,3,114,105,21,217,63,239,
-  137,78,205,180,208,45,67,247,186,173,92,119,227,235,169,125,240,12,149,112,150,192,81,184,194,34,76,106,84,33,105,71,
-  177,96,211,88,222,246,126,185,92,210,9,33,176,58,173,94,178,217,44,66,8,162,40,192,152,152,108,214,79,213,208,82,
-  48,60,60,140,239,103,73,146,246,247,88,211,190,37,200,118,224,161,105,71,28,65,43,174,99,61,75,95,247,144,152,188,
-  234,186,85,231,158,120,128,188,83,67,40,7,99,4,113,4,202,203,33,133,139,53,1,88,75,179,25,144,241,28,60,207,
-  97,106,106,18,41,29,150,141,44,163,49,62,201,166,179,231,230,242,112,38,129,217,41,72,142,72,69,67,170,151,93,119,
-  44,73,27,50,128,76,162,23,105,58,103,113,165,143,148,169,243,163,74,82,7,130,53,203,86,177,80,173,50,117,242,2,
-  59,234,141,75,40,241,143,9,64,121,160,7,196,30,200,59,66,172,114,225,106,107,237,43,15,185,206,229,181,31,185,125,
-  108,215,123,126,193,219,186,107,7,5,191,128,192,96,147,8,155,36,216,168,73,34,65,40,16,142,66,90,209,78,249,20,
-  40,218,126,53,230,121,250,76,153,82,182,22,241,92,27,110,211,59,190,18,2,164,109,103,102,181,69,120,23,91,32,65,
-  169,84,230,138,171,175,98,205,154,21,60,249,232,67,156,58,91,101,168,167,27,217,10,105,105,135,141,203,70,184,233,35,
-  31,112,255,71,125,126,107,246,43,15,84,122,225,228,28,92,16,176,176,234,101,220,121,35,96,213,103,246,19,172,237,166,
-  33,37,203,129,199,218,119,225,23,123,1,185,41,160,203,101,208,33,96,232,116,71,87,231,53,87,46,48,53,115,23,83,
-  93,87,176,241,21,111,166,59,83,230,207,63,240,159,136,131,121,144,18,35,44,74,184,23,239,250,75,155,239,75,207,162,
-  227,40,116,98,46,198,246,64,170,11,106,54,91,212,42,21,178,126,6,199,145,200,172,79,146,36,244,245,245,165,213,81,
-  28,167,49,64,24,172,144,88,253,156,235,165,235,58,105,61,106,28,148,113,168,53,102,216,177,109,61,6,205,179,135,246,
-  34,164,33,137,13,198,10,84,152,96,12,56,34,133,101,171,53,137,134,90,35,198,74,69,237,236,41,180,84,12,156,31,
-  215,87,52,235,39,13,236,137,224,194,70,72,30,24,93,205,254,114,23,222,119,145,174,98,73,165,13,253,115,51,116,36,
-  213,54,24,127,103,240,249,134,91,134,181,184,214,50,222,217,73,112,227,181,76,30,60,130,57,113,230,82,67,246,253,2,
-  160,63,250,54,101,109,51,189,67,11,23,58,178,176,85,89,123,243,20,220,120,124,211,218,45,215,253,222,255,40,239,188,
-  245,6,124,105,208,113,128,14,43,41,123,96,69,138,37,66,98,101,59,66,214,166,246,17,72,137,177,23,67,61,219,9,
-  195,233,189,74,182,163,126,133,32,229,52,172,120,222,202,65,59,232,181,237,153,188,68,188,166,111,24,147,2,89,28,209,
-  81,46,243,202,91,110,227,240,190,189,84,90,13,114,210,165,228,251,140,31,59,66,169,220,201,141,255,254,87,220,103,238,
-  126,112,77,127,98,183,52,97,207,29,80,217,242,50,7,41,122,166,193,51,255,225,43,84,141,97,61,105,192,225,75,109,
-  27,22,193,13,211,232,218,254,227,221,203,243,242,217,73,222,117,29,60,112,230,28,11,250,77,12,94,254,42,222,229,6,
-  252,221,95,252,54,173,160,138,235,186,36,137,198,145,41,249,156,130,143,196,74,133,213,105,76,139,213,150,48,8,113,93,
-  23,207,243,176,214,82,171,86,218,57,94,57,28,199,161,218,168,224,185,46,25,207,107,155,216,123,116,150,59,218,160,37,
-  16,74,181,127,103,211,169,24,128,208,68,205,6,25,167,196,236,220,60,55,221,112,3,29,221,163,196,145,226,216,241,227,
-  204,205,156,64,234,10,88,7,209,158,214,117,117,119,131,116,168,44,214,169,53,3,2,3,207,30,216,207,234,74,189,89,
-  134,227,45,56,148,129,133,86,177,195,30,236,234,19,57,99,172,80,223,157,178,199,2,83,3,35,156,211,33,5,179,72,
-  189,29,120,253,162,192,231,121,63,67,36,9,189,133,28,135,175,186,12,175,191,151,209,125,135,113,106,245,75,64,244,189,
-  6,160,95,250,54,127,89,1,246,65,190,2,91,115,240,198,211,112,243,145,219,111,89,247,142,63,255,3,119,221,232,114,
-  108,179,130,177,22,225,8,148,77,121,3,35,5,70,202,246,82,81,42,241,183,198,192,146,110,101,201,64,253,249,4,0,
-  96,133,69,44,177,1,82,128,89,186,203,155,246,59,34,229,36,210,175,177,207,17,7,109,13,140,149,233,198,85,198,245,
-  89,189,122,19,179,227,103,56,119,250,4,103,107,167,88,187,118,45,147,227,211,92,117,195,205,60,118,245,174,158,214,131,
-  143,111,20,66,172,18,214,158,107,119,151,47,249,124,1,56,163,13,111,79,43,153,139,250,167,151,2,64,46,136,24,84,
-  11,156,161,145,117,170,115,205,47,242,208,177,143,114,251,214,25,30,57,244,121,206,235,55,177,122,215,143,242,46,41,249,
-  196,223,254,1,243,115,51,120,174,135,54,17,74,164,201,168,8,137,182,18,108,130,104,123,53,47,217,180,46,69,47,87,
-  43,85,146,68,35,16,23,199,239,67,3,67,164,118,169,9,181,90,21,221,219,151,6,13,168,231,216,55,108,74,108,59,
-  74,165,9,28,78,234,237,99,90,130,133,201,5,118,237,220,198,19,79,28,228,250,107,223,194,248,244,9,14,60,251,21,
-  38,39,199,201,100,4,165,130,123,113,234,134,6,71,56,184,174,67,84,111,209,105,76,64,26,199,51,61,12,225,233,124,
-  153,80,185,228,237,247,136,119,177,134,172,202,144,213,14,53,251,124,255,156,244,125,243,237,192,231,249,199,104,131,210,134,
-  103,86,45,103,95,87,39,87,29,56,74,120,246,60,74,107,226,54,117,112,233,124,31,91,176,67,169,195,223,144,15,175,
-  220,15,175,58,245,186,215,174,251,213,191,121,159,232,233,42,162,163,10,66,88,144,10,109,21,70,180,147,60,165,32,165,
-  118,108,187,164,79,53,32,75,117,134,64,164,241,51,237,23,223,216,165,132,13,217,230,122,82,228,145,242,27,151,45,49,
-  75,127,175,210,76,236,165,22,36,245,46,37,17,26,43,45,210,66,190,144,197,118,117,35,19,203,93,119,223,207,211,207,
-  222,197,149,215,94,203,198,29,87,208,243,250,59,178,71,30,124,124,237,86,107,215,71,130,61,79,88,234,127,202,115,254,
-  195,47,134,187,185,146,212,198,242,245,164,177,38,19,164,158,198,75,174,139,47,22,128,18,72,52,204,150,224,216,192,189,
-  159,155,152,188,252,142,209,210,213,239,225,209,83,159,226,154,141,115,60,113,230,243,156,27,191,150,177,157,63,194,47,13,
-  12,242,249,143,252,37,123,246,62,141,20,22,75,26,51,100,219,185,244,32,48,38,193,90,8,163,8,41,4,158,151,193,
-  207,248,56,82,165,211,179,196,16,6,17,58,209,20,11,121,180,78,173,56,150,252,160,51,110,6,107,45,137,214,237,205,
-  248,212,156,94,41,133,116,124,34,109,209,113,136,43,18,106,243,9,165,206,44,215,94,181,149,135,31,57,206,216,234,43,
-  25,25,219,197,225,67,15,115,248,192,3,212,234,227,72,17,162,128,140,155,198,205,234,86,132,159,203,209,104,70,194,166,
-  11,196,241,95,10,73,179,107,152,46,109,172,254,30,250,241,41,43,24,16,121,38,169,34,173,37,80,105,142,154,43,51,
-  72,94,154,70,62,19,70,152,98,158,71,174,189,156,179,27,86,211,113,232,56,131,211,83,232,182,141,238,165,243,93,0,
-  208,35,223,226,226,136,83,102,198,247,96,109,3,118,29,223,117,217,138,159,252,195,223,17,61,93,101,140,105,130,171,210,
-  182,202,74,84,27,8,46,142,157,77,123,74,99,211,88,153,148,93,110,79,184,172,109,195,144,2,43,144,164,230,235,90,
-  132,169,203,95,123,26,102,144,72,68,202,175,90,211,30,99,139,246,224,169,189,81,213,206,155,50,194,32,210,102,36,205,
-  170,151,146,66,111,15,65,164,185,253,142,219,249,195,223,251,35,238,249,210,221,220,252,218,87,177,237,182,155,249,108,199,
-  31,246,111,91,92,92,81,177,162,119,28,59,225,130,254,78,133,255,210,50,105,54,229,196,46,122,74,91,82,129,225,67,
-  192,239,242,210,244,40,115,144,236,133,115,49,60,120,93,210,24,249,226,123,255,237,171,78,118,255,81,207,198,109,239,228,
-  174,147,159,225,138,145,179,140,85,239,229,153,201,141,120,189,87,243,174,95,93,199,138,187,254,134,175,126,234,111,105,180,
-  2,132,148,56,9,196,42,193,8,75,70,120,72,3,58,140,145,89,73,198,177,132,141,58,65,210,66,40,65,198,207,128,
-  53,40,71,209,217,213,141,177,150,36,14,201,122,57,180,73,176,50,211,30,4,216,244,102,34,210,40,232,40,73,109,85,
-  131,32,192,69,129,136,105,73,203,212,228,52,229,142,65,110,188,97,29,247,62,178,155,254,161,27,217,124,249,205,44,223,
-  176,133,67,251,30,231,236,241,251,16,97,72,33,91,64,121,150,196,26,140,114,105,136,212,144,204,2,21,107,56,219,168,
-  176,58,55,74,168,147,239,225,157,85,82,23,169,78,90,58,46,15,175,95,143,149,18,247,185,250,238,37,29,169,13,82,
-  27,206,151,75,156,191,122,39,199,163,152,177,74,133,76,43,160,171,25,162,171,181,75,104,242,114,94,167,109,223,130,32,
-  61,12,76,67,41,7,171,38,4,171,58,111,184,38,179,97,213,10,48,205,20,80,218,166,232,152,212,10,73,146,182,93,
-  98,169,52,21,150,56,14,9,91,13,130,160,133,20,2,141,197,241,92,116,98,81,72,208,130,68,107,162,88,227,58,30,
-  82,72,180,17,120,249,84,60,39,17,8,37,41,20,10,40,183,61,186,183,96,147,118,176,158,72,91,51,99,45,210,202,
-  182,103,177,73,135,249,74,82,232,233,160,50,49,193,47,252,236,59,120,255,251,63,205,196,228,56,155,55,111,224,254,171,
-  174,206,215,191,242,229,101,74,138,161,78,99,15,255,123,104,13,240,237,61,102,50,164,155,216,51,164,155,216,75,220,153,
-  36,93,57,249,125,224,115,192,79,1,231,95,68,69,37,73,87,49,118,66,101,23,236,14,97,232,142,250,212,216,95,253,
-  254,111,92,206,47,255,169,191,105,251,79,115,207,225,15,177,189,251,36,55,47,123,146,7,46,92,160,150,187,158,87,220,
-  241,111,89,177,250,50,62,251,177,223,227,248,161,221,100,29,7,37,178,41,99,227,8,140,4,65,76,54,83,38,91,44,
-  34,125,65,206,115,200,56,170,205,163,9,242,249,34,202,245,104,5,1,190,239,163,148,67,98,82,32,87,74,33,244,146,
-  252,65,145,201,248,152,212,166,158,140,167,144,74,225,185,89,154,145,69,182,34,102,231,38,24,27,27,229,134,107,87,241,
-  204,254,61,148,114,59,65,141,176,121,251,32,99,195,27,56,114,240,75,204,77,95,192,247,32,239,187,52,98,80,237,16,
-  178,165,214,232,193,233,51,52,130,26,201,247,48,222,91,34,168,217,8,81,116,232,238,234,72,23,109,173,229,187,53,163,
-  112,218,161,134,177,82,244,118,119,211,227,56,228,187,58,185,112,239,35,168,90,253,18,162,188,212,231,211,125,161,242,21,
-  40,130,156,135,14,11,203,38,10,217,238,93,183,220,146,150,249,246,185,221,33,33,192,10,145,190,184,34,45,215,177,154,
-  168,213,96,126,118,146,70,179,70,28,181,104,5,13,116,3,226,88,179,88,173,208,106,182,208,218,48,51,53,211,246,37,
-  22,136,88,210,213,213,197,220,194,2,70,74,138,165,14,234,173,58,177,78,245,42,43,87,143,210,223,223,71,169,80,162,
-  183,183,143,108,169,4,82,161,218,83,155,165,164,80,129,104,175,40,216,180,220,247,50,116,118,247,112,235,171,174,101,122,
-  98,138,193,129,53,172,127,253,237,153,217,175,124,121,100,153,49,163,46,20,159,133,214,239,125,135,234,37,3,172,252,14,
-  32,37,218,237,217,223,167,220,25,47,230,126,126,57,152,24,22,12,226,172,143,157,219,185,48,31,253,207,63,248,13,255,
-  199,223,243,159,217,113,245,59,121,236,208,215,152,169,60,197,173,91,15,113,120,98,146,131,19,55,144,95,249,42,254,197,
-  191,219,206,51,247,125,132,7,238,251,4,179,231,79,145,83,2,235,103,168,89,77,214,201,128,86,72,235,16,197,150,86,
-  37,38,151,41,224,186,46,205,122,147,222,222,62,242,249,220,197,22,55,73,98,130,32,32,159,47,96,180,126,78,6,97,
-  12,142,163,200,102,253,148,196,206,229,48,198,144,36,9,174,43,137,163,144,106,181,198,252,124,149,129,161,94,214,173,201,
-  112,234,196,5,58,75,155,152,171,79,147,47,174,231,138,171,7,57,121,226,49,142,236,189,27,130,38,42,159,67,40,41,
-  45,8,167,253,156,53,90,117,30,108,125,127,46,222,45,133,49,148,84,36,250,123,107,97,38,172,69,91,139,136,34,230,
-  10,5,158,184,241,26,42,247,63,134,170,84,47,161,202,75,1,160,167,190,197,221,217,3,37,160,171,5,67,206,182,157,
-  133,13,91,54,129,14,46,146,194,66,166,160,35,68,58,38,215,180,35,94,130,128,51,71,78,18,183,90,156,60,123,138,
-  133,197,5,130,32,68,89,133,53,6,63,147,105,231,139,91,134,7,135,211,133,73,199,161,163,51,79,28,133,12,143,118,
-  164,137,161,113,68,198,43,210,217,81,38,137,99,22,91,117,22,39,35,38,130,128,3,218,82,238,232,164,171,167,143,190,
-  129,65,186,122,123,17,106,41,35,93,94,220,137,2,75,161,92,162,114,161,198,202,85,3,212,226,212,200,97,211,77,215,
-  240,245,193,254,126,59,49,181,78,8,49,16,88,59,223,13,137,255,45,238,143,75,169,1,115,164,33,121,223,174,170,89,
-  11,252,87,224,20,112,125,58,69,252,182,99,54,63,173,52,169,97,29,11,190,144,137,76,146,9,62,246,222,255,132,174,
-  84,217,126,199,91,25,191,48,198,223,223,251,113,110,222,58,75,199,232,151,120,236,236,49,90,185,155,185,234,246,247,176,
-  249,218,215,242,204,131,159,102,247,61,95,100,122,230,48,142,178,8,153,39,50,9,36,1,50,48,68,6,130,36,70,38,
-  10,165,28,6,7,7,47,26,142,165,94,215,105,162,234,18,32,165,55,25,139,227,166,162,66,221,30,203,63,55,154,119,
-  145,82,18,199,49,11,243,243,20,242,121,124,63,203,200,64,63,213,217,211,212,235,46,165,66,39,243,205,10,34,238,100,
-  213,170,219,232,239,26,228,192,179,119,179,24,206,209,240,60,215,166,97,154,50,223,150,122,124,47,189,151,104,131,127,0,
-  148,11,133,111,200,129,255,94,31,11,120,73,194,92,46,71,207,29,55,113,100,255,17,70,246,29,226,18,61,253,34,1,
-  232,53,223,60,94,78,171,31,254,2,100,25,178,13,200,55,202,157,94,185,88,120,238,242,124,190,27,95,59,31,70,27,
-  141,43,4,11,211,179,92,56,115,158,7,191,126,31,197,114,39,195,163,203,72,104,209,55,214,137,142,90,116,119,116,81,
-  89,92,36,54,17,142,43,40,230,75,36,113,76,152,196,20,75,229,52,143,42,138,40,148,10,228,11,57,16,22,87,102,
-  41,116,20,210,246,75,150,49,214,210,108,133,204,207,92,96,118,234,2,67,195,35,44,223,184,1,148,215,78,134,16,44,
-  169,26,189,76,6,43,37,86,196,44,46,84,33,137,25,91,53,74,112,221,174,238,250,39,191,176,62,3,107,198,224,204,
-  155,160,146,255,54,79,212,19,164,86,171,223,137,100,94,178,136,248,98,187,10,250,207,124,251,152,153,32,125,206,133,0,
-  37,16,242,140,180,42,145,33,182,85,229,99,127,241,223,57,55,127,154,31,121,235,47,16,103,255,35,159,223,255,247,108,
-  236,219,205,109,203,14,112,114,102,154,51,19,219,241,58,47,231,234,215,255,18,219,175,123,3,207,62,248,9,30,255,234,
-  199,169,213,171,180,156,136,134,209,120,65,11,191,208,194,46,54,81,178,128,155,117,241,51,62,158,227,34,132,162,209,106,
-  162,164,75,24,68,196,113,140,231,101,80,109,65,160,235,58,237,73,153,38,155,205,34,5,248,190,79,24,134,105,204,15,
-  16,199,17,173,102,147,74,173,2,18,54,111,238,99,239,254,35,52,163,245,248,110,158,102,18,208,170,75,74,29,107,217,
-  113,117,7,15,62,249,89,106,179,23,164,133,108,29,188,127,3,226,119,193,126,183,245,201,146,37,111,196,115,137,171,159,
-  4,62,109,204,247,12,220,190,45,233,221,54,116,107,109,219,72,183,181,92,216,127,248,18,186,188,152,215,109,241,5,62,
-  169,83,182,69,168,52,106,94,230,11,37,225,56,18,99,211,81,174,160,173,223,177,96,49,24,171,81,82,208,170,55,88,
-  152,159,97,239,222,39,201,21,92,54,110,94,77,179,213,162,148,201,224,23,50,72,147,33,142,34,202,229,18,133,114,137,
-  68,39,68,65,64,33,155,37,155,203,167,252,141,209,228,140,143,114,36,137,73,72,76,130,18,138,140,78,91,2,233,164,
-  161,125,133,130,79,169,109,200,30,55,42,204,140,95,160,111,120,20,107,4,86,56,136,139,194,69,7,199,247,113,154,25,
-  38,46,156,33,142,12,110,206,101,228,77,175,247,206,127,242,11,171,54,88,187,206,192,83,143,65,229,67,192,193,111,226,
-  111,2,224,215,72,179,180,95,236,69,178,68,115,126,1,184,163,253,125,47,52,244,93,34,251,115,105,5,160,141,148,122,
-  10,165,164,73,136,51,18,220,22,247,125,238,125,212,79,158,227,245,239,254,77,70,175,255,89,78,158,126,128,227,143,127,
-  134,203,86,76,112,229,80,200,145,241,195,204,85,174,194,27,216,192,43,223,248,107,92,126,205,235,248,244,223,253,79,78,
-  28,60,128,54,14,213,100,150,48,108,161,100,6,33,4,189,189,189,228,115,89,194,32,196,216,212,27,104,41,1,99,201,
-  196,222,201,56,23,125,130,148,227,160,148,66,57,10,207,115,46,102,143,45,249,18,197,113,68,181,182,136,227,123,40,199,
-  5,19,177,110,93,31,143,63,117,150,56,92,75,206,87,72,63,33,137,93,92,217,143,162,72,35,140,29,4,93,198,210,
-  177,8,78,25,162,151,91,49,136,54,232,76,146,186,215,205,145,186,40,22,121,241,170,244,239,89,53,100,45,197,56,230,
-  224,214,13,212,138,5,244,254,195,112,137,23,122,249,99,248,54,24,137,66,62,143,116,36,214,36,41,255,44,100,26,73,
-  35,20,182,173,235,17,198,18,54,27,180,90,117,250,6,123,232,40,21,105,197,85,188,188,79,169,179,139,170,205,49,61,
-  49,137,104,70,148,10,69,206,94,152,160,214,104,176,56,51,67,78,185,244,12,150,24,24,30,192,43,228,241,51,62,120,
-  30,158,35,113,76,130,176,6,169,45,113,24,166,21,142,209,72,145,198,254,10,99,81,2,206,158,62,73,161,212,65,190,
-  208,209,142,45,94,26,249,75,220,140,79,33,219,73,88,63,68,179,17,82,206,185,172,223,181,139,61,163,195,189,27,207,
-  93,88,174,161,191,23,198,143,67,252,4,223,24,209,211,108,223,89,95,234,182,181,109,183,110,187,72,247,195,14,240,15,
-  87,62,18,82,143,146,159,77,43,160,76,40,101,102,210,203,40,199,177,88,147,42,140,115,46,236,221,243,5,78,76,236,
-  225,214,183,252,75,174,190,225,237,68,3,235,121,224,200,215,233,170,62,195,43,86,213,136,230,191,204,179,103,158,161,82,
-  190,153,222,225,43,233,91,185,139,243,71,143,80,82,62,137,81,44,198,18,43,211,74,181,163,163,3,173,45,173,86,72,
-  62,159,199,109,139,21,151,44,91,93,215,35,81,26,167,13,50,182,237,23,148,198,252,152,246,126,24,228,218,124,80,163,
-  209,32,10,35,146,208,208,106,132,184,82,162,154,130,245,235,11,60,253,196,17,124,103,53,184,10,229,184,84,171,115,220,
-  126,199,107,56,59,189,215,137,235,139,93,2,74,205,182,47,244,2,47,111,23,76,3,171,219,207,239,111,241,141,246,173,
-  255,40,109,144,5,147,104,122,87,47,103,119,111,55,157,247,62,204,194,37,16,122,249,0,100,128,169,70,5,109,52,158,
-  16,8,41,150,214,183,144,207,11,249,22,54,193,36,33,97,28,18,26,136,172,67,182,208,65,172,5,159,253,224,231,147,
-  252,145,51,81,249,194,68,131,102,43,156,179,194,132,81,8,88,65,162,77,67,39,40,87,234,70,214,167,81,40,120,153,
-  238,110,183,212,215,231,169,254,190,172,237,239,247,220,145,97,89,88,214,75,71,111,7,165,124,14,207,53,160,35,146,176,
-  133,176,144,81,10,27,133,156,57,126,130,141,219,46,79,31,156,18,24,155,234,31,51,121,31,213,82,216,216,112,104,239,
-  1,54,94,185,133,209,177,85,176,126,99,62,60,119,97,181,130,205,85,184,240,255,192,228,8,232,85,237,202,36,79,170,
-  247,153,253,46,248,129,165,29,165,107,218,96,246,205,23,79,79,90,113,185,2,10,129,177,185,89,37,164,193,161,16,180,
-  208,174,36,214,14,89,215,82,153,57,206,199,255,228,215,56,254,208,125,220,244,182,159,101,195,214,215,177,184,112,45,247,
-  159,122,136,13,229,211,108,31,29,103,166,254,73,162,243,67,228,106,147,88,199,208,148,139,120,162,128,105,37,120,74,210,
-  217,217,73,198,203,224,56,46,133,66,234,19,20,133,81,202,233,56,146,32,104,145,205,230,47,102,205,107,157,242,63,218,
-  24,146,86,3,109,76,234,17,148,205,166,70,102,237,141,251,88,74,106,139,115,120,202,210,242,44,218,10,178,89,201,101,
-  59,243,60,189,247,16,61,93,215,80,79,90,116,44,203,112,249,117,155,25,191,115,64,6,231,143,229,114,80,168,131,219,
-  1,65,129,151,182,127,190,180,186,28,144,250,119,247,112,209,115,251,31,253,8,32,14,35,114,133,60,111,186,241,90,62,
-  122,239,195,232,75,32,244,242,0,40,11,198,206,204,196,81,148,168,76,214,77,53,55,214,182,137,222,182,74,217,10,48,
-  49,113,171,133,175,92,58,253,60,25,229,227,10,159,7,247,61,203,194,71,63,95,191,221,154,195,89,216,35,211,106,249,
-  31,12,136,108,172,177,113,3,170,13,244,248,148,171,57,216,21,195,64,4,61,11,66,246,93,200,251,69,53,220,159,111,
-  140,141,22,114,107,87,120,107,118,108,21,195,171,150,211,219,85,34,136,91,116,151,60,166,43,11,84,234,243,148,139,93,
-  232,68,163,149,64,197,22,33,92,158,125,234,9,146,19,231,216,125,224,36,251,190,248,69,198,182,111,167,153,207,58,79,
-  192,218,87,10,110,139,45,139,30,220,223,7,115,222,243,218,176,230,119,121,39,13,129,219,72,45,68,119,242,141,230,235,
-  30,105,124,143,134,188,128,129,5,207,235,108,42,71,120,90,32,149,34,81,2,97,5,73,96,112,85,1,215,141,217,247,
-  244,231,57,120,224,49,118,94,117,19,55,191,254,103,24,89,255,54,206,206,157,224,220,133,167,232,244,142,210,223,125,138,
-  145,174,57,114,202,39,104,25,172,87,33,10,22,200,229,11,100,243,57,130,176,133,163,36,158,151,65,8,73,156,196,56,
-  110,6,99,37,141,70,64,177,20,130,205,33,109,26,241,19,39,1,137,78,240,253,18,54,106,96,13,132,97,152,106,130,
-  28,143,140,231,17,4,33,100,28,162,160,6,139,22,175,100,104,36,62,197,142,60,163,67,49,227,51,135,160,80,96,197,
-  134,213,204,84,3,236,64,159,154,131,222,2,140,72,40,253,66,106,24,247,146,193,125,201,210,228,205,252,96,198,244,232,
-  36,161,92,200,243,230,27,175,37,119,223,195,44,84,47,129,208,63,0,160,225,111,65,66,251,41,49,104,179,96,250,143,
-  159,106,86,166,23,51,197,229,253,34,229,129,12,234,226,186,68,58,148,55,70,147,232,4,37,5,133,124,142,64,39,8,
-  44,173,243,231,185,202,154,233,28,220,19,192,23,61,24,247,47,6,0,191,240,157,77,131,106,64,209,131,46,15,122,58,
-  172,233,95,85,111,246,36,71,78,245,53,142,156,26,108,222,245,64,255,184,159,29,126,106,221,218,1,239,53,183,248,59,
-  111,184,154,213,195,221,148,18,216,247,204,30,174,187,249,150,116,132,172,193,120,30,135,190,254,40,147,79,62,203,168,117,
-  136,109,76,56,95,227,236,221,247,49,236,23,196,99,219,119,12,179,111,223,43,110,208,201,100,3,78,159,132,106,247,243,
-  110,164,115,237,73,213,119,115,98,210,77,249,183,124,83,27,118,30,232,6,53,9,61,2,70,79,249,217,174,138,78,112,
-  19,75,36,93,26,58,65,201,4,132,197,146,128,149,56,190,143,164,202,67,247,126,138,221,123,30,231,234,27,223,200,43,
-  110,125,11,254,208,143,48,94,27,231,236,133,103,248,250,222,251,241,243,46,30,5,116,24,145,152,0,215,47,2,6,161,
-  68,187,186,209,237,236,120,77,162,227,116,162,169,53,73,152,16,169,22,202,247,144,210,193,90,73,28,181,200,203,12,126,
-  156,2,163,177,10,84,134,70,28,227,88,40,228,114,32,114,44,52,4,93,210,210,172,47,162,242,69,106,21,65,71,38,
-  203,193,201,167,25,222,122,13,221,197,65,230,206,61,66,105,216,99,143,155,25,92,21,135,235,68,106,198,54,245,157,84,
-  11,182,13,54,89,82,239,159,191,36,141,81,250,65,63,113,146,208,93,200,179,251,198,235,232,190,247,33,184,4,66,223,
-  8,64,31,248,22,37,164,74,51,183,26,2,170,250,194,249,248,212,83,187,245,200,138,55,56,152,56,229,128,158,95,48,
-  11,129,17,130,216,26,60,47,131,206,103,72,26,49,213,48,34,123,252,92,50,10,167,99,120,170,12,251,143,64,237,99,
-  60,103,216,245,66,21,195,43,65,172,5,57,148,218,187,122,58,245,141,206,73,33,202,69,107,187,243,48,212,31,180,54,
-  13,237,221,123,217,227,135,14,109,121,255,167,191,216,187,235,221,111,146,239,124,237,143,112,126,226,32,179,179,85,122,74,
-  101,172,178,60,113,207,3,204,60,248,12,189,137,203,162,14,72,124,23,139,164,208,140,233,118,51,116,92,117,19,95,157,
-  154,237,223,49,113,110,99,14,150,73,56,114,20,226,28,255,48,66,247,187,45,203,91,164,30,64,180,43,161,227,41,247,
-  211,45,96,163,145,106,237,25,100,222,152,180,183,21,130,52,173,194,74,164,18,196,38,85,121,199,38,65,90,75,62,231,
-  19,53,230,248,218,231,254,150,189,143,125,157,181,87,95,207,214,203,110,98,164,176,145,185,74,158,124,182,129,113,154,232,
-  208,65,24,151,222,174,62,60,55,131,209,22,235,62,71,154,10,169,48,86,224,42,139,2,148,240,49,38,70,27,23,43,
-  20,249,194,16,35,99,221,212,227,41,26,141,42,74,7,100,140,164,228,122,52,99,152,175,213,241,114,57,98,109,137,162,
-  38,217,2,56,56,100,45,8,109,153,156,156,166,111,104,25,107,174,184,153,153,185,113,156,224,8,59,95,113,35,95,191,
-  255,80,169,126,124,207,72,6,186,207,130,243,63,32,105,188,192,243,109,218,36,115,12,252,27,224,198,246,215,252,83,242,
-  130,214,73,66,35,159,227,236,13,215,176,101,223,97,226,83,103,47,45,181,46,1,144,255,173,91,7,13,76,41,56,210,
-  169,147,43,206,61,252,104,39,111,125,61,98,105,157,93,152,180,245,18,233,116,74,27,155,218,48,196,26,27,197,248,174,
-  143,240,74,20,206,94,168,23,82,223,151,115,171,160,245,44,240,249,239,240,160,124,176,99,160,71,65,111,134,240,46,168,
-  29,2,177,222,90,153,128,147,133,156,65,236,206,97,247,223,26,69,215,109,59,124,120,215,23,255,243,255,90,243,191,142,
-  158,45,188,225,221,63,198,153,163,103,233,185,246,74,238,253,244,167,169,60,180,155,46,55,71,147,16,47,159,101,106,106,
-  156,201,133,89,150,245,13,144,201,56,12,58,25,250,55,110,80,227,19,231,122,214,65,103,5,188,46,104,244,145,106,127,
-  158,127,7,254,174,198,180,164,41,132,197,118,235,85,77,63,50,157,176,198,194,245,161,114,214,158,21,194,83,82,32,77,
-  106,76,2,169,162,215,216,37,11,1,129,209,237,142,55,106,33,133,164,232,100,104,206,30,229,145,79,61,203,222,47,127,
-  148,101,3,43,112,154,51,8,25,99,28,7,43,12,153,124,7,224,209,106,134,228,114,170,29,1,38,241,60,31,19,4,
-  109,11,15,133,112,125,66,4,58,49,100,173,139,137,67,74,189,131,172,94,117,3,129,219,160,90,25,167,114,254,12,141,
-  241,83,152,218,60,133,172,194,218,132,230,194,28,126,206,197,68,9,113,224,35,203,30,177,142,152,159,169,51,189,80,97,
-  227,13,175,39,50,157,248,225,35,244,173,88,77,82,188,6,151,191,17,9,168,12,168,4,196,23,218,213,230,11,157,1,
-  224,39,219,191,38,255,68,47,52,155,36,116,231,114,76,189,98,23,149,66,30,111,223,161,75,32,4,56,175,248,22,127,
-  81,1,115,4,102,90,112,160,27,206,78,236,217,55,92,175,7,94,54,171,48,70,183,149,207,207,191,83,165,147,176,44,
-  138,140,149,148,186,122,120,252,169,103,233,156,156,152,151,112,214,194,236,57,208,111,0,254,11,112,250,59,60,176,165,26,
-  107,105,204,58,1,182,3,244,97,208,191,13,225,126,108,173,46,228,116,36,196,153,110,163,79,188,91,39,175,248,218,7,
-  62,116,197,99,103,207,117,154,205,91,188,147,251,15,138,234,19,251,88,153,47,19,232,128,40,171,104,85,107,184,40,114,
-  217,28,243,205,6,211,181,58,203,178,9,3,67,131,84,148,83,210,58,233,117,33,191,31,22,178,237,187,239,104,187,90,
-  89,154,132,137,23,104,13,94,10,225,246,252,11,168,0,69,3,27,4,98,71,75,80,170,72,97,173,54,36,198,166,123,
-  113,109,107,18,251,60,204,23,168,212,39,210,154,182,108,161,133,194,82,118,75,216,56,224,216,137,71,232,236,232,99,96,
-  96,3,70,70,132,166,198,192,80,47,74,57,24,29,227,58,94,106,28,143,192,104,157,146,200,113,140,149,14,210,86,72,
-  140,143,16,121,226,56,34,227,123,24,221,162,94,159,33,215,177,154,129,238,229,244,119,93,65,188,174,194,252,212,41,42,
-  211,39,80,139,231,112,231,199,105,53,102,201,72,151,164,161,193,248,160,20,245,230,44,214,19,248,189,35,196,149,3,140,
-  22,230,161,112,25,39,78,156,134,179,135,227,66,26,85,168,151,146,80,95,8,128,254,93,58,37,164,183,205,163,253,83,
-  142,100,182,198,16,7,17,3,91,54,176,63,159,195,63,116,148,124,165,246,207,58,90,200,121,226,219,0,128,77,187,134,
-  51,57,56,161,247,29,216,122,238,240,177,194,134,203,55,162,105,181,151,77,219,190,199,82,34,218,134,89,38,163,136,48,
-  36,174,67,50,49,75,95,20,77,27,56,231,67,245,44,216,113,210,176,191,255,240,2,83,161,239,212,194,40,158,139,251,
-  217,2,201,39,173,153,217,134,172,205,41,103,194,96,39,110,215,58,56,118,207,253,215,254,233,179,7,6,244,53,55,136,
-  45,195,163,84,234,53,50,217,12,213,133,58,253,221,61,248,29,61,244,234,144,90,189,70,208,10,113,98,139,109,181,104,
-  10,81,86,176,49,20,98,75,217,90,115,12,90,18,188,30,200,180,65,48,216,14,205,24,162,10,36,175,2,51,198,115,
-  49,210,170,61,53,179,223,226,34,81,109,238,98,22,56,75,26,53,61,4,185,0,6,21,182,107,193,245,212,28,86,40,
-  209,246,225,65,182,141,226,45,218,166,254,58,142,148,104,169,72,172,193,74,75,98,12,194,26,12,6,35,19,172,107,8,
-  165,32,223,223,67,215,96,63,245,202,28,174,3,25,233,18,71,33,174,163,136,194,144,140,159,26,146,213,226,56,117,20,
-  48,22,35,211,113,187,140,12,210,19,132,162,69,70,120,120,246,44,3,250,20,241,252,8,85,49,66,226,244,163,253,101,
-  184,67,187,24,28,189,28,25,207,19,213,207,241,232,221,159,39,172,30,167,25,79,192,180,71,103,103,9,108,72,84,247,
-  200,196,45,138,242,12,34,51,64,192,48,167,15,252,111,26,166,34,4,162,96,176,221,125,224,191,29,90,255,235,155,158,
-  179,159,110,243,60,147,252,240,100,193,139,118,75,118,104,213,24,238,216,8,93,135,142,81,46,22,200,122,223,56,15,210,
-  198,180,29,37,126,200,1,168,247,219,220,173,207,64,18,195,84,6,142,118,206,207,205,28,190,231,190,222,181,151,111,148,
-  26,131,176,18,99,44,82,138,246,226,168,196,85,14,145,23,161,28,69,53,137,48,71,143,7,101,152,146,48,81,131,214,
-  191,39,93,59,200,127,15,30,120,210,174,138,214,89,211,186,160,205,169,33,169,108,2,203,234,136,29,215,108,189,92,174,
-  29,26,33,10,154,36,36,204,78,204,179,122,221,90,138,217,60,213,122,149,176,217,66,70,134,178,85,224,88,198,122,250,
-  216,219,219,159,143,38,206,95,230,166,180,250,122,139,93,48,136,18,216,142,246,150,255,188,129,113,9,51,207,192,220,143,
-  64,179,149,238,113,133,203,161,121,103,26,251,99,103,193,22,33,185,21,204,70,176,109,87,1,100,27,132,154,237,22,204,
-  73,253,1,18,43,68,224,88,27,238,21,34,179,104,141,202,180,13,121,76,123,186,40,108,90,13,25,44,113,18,131,146,
-  23,183,213,109,123,41,216,160,144,196,72,45,17,81,150,206,82,23,157,69,143,218,92,72,62,91,66,99,112,165,67,33,
-  151,197,113,93,220,140,135,148,18,173,19,60,215,193,106,75,66,136,84,46,54,17,224,36,68,73,66,98,19,108,220,162,
-  39,43,88,53,50,78,61,58,205,212,130,195,228,162,207,108,216,71,160,198,176,249,101,228,186,183,113,213,237,195,60,240,
-  241,223,68,196,51,76,78,78,18,199,13,146,160,202,217,83,139,76,29,254,26,195,253,62,13,255,90,166,39,231,184,98,
-  213,58,30,184,230,21,153,167,31,184,111,249,21,176,173,14,7,222,10,117,7,226,223,121,222,107,124,45,124,15,13,58,
-  126,176,142,27,39,88,4,147,27,215,242,112,108,233,126,228,32,151,111,88,131,20,130,158,142,46,98,223,225,212,112,192,
-  234,249,35,63,212,32,228,204,124,7,222,194,135,69,11,135,58,224,232,153,47,126,105,108,226,39,223,92,28,232,43,163,
-  19,221,118,62,20,24,99,17,6,114,194,197,58,14,67,165,46,142,213,90,168,233,153,192,75,141,167,102,124,136,127,227,
-  121,63,59,7,252,53,112,231,119,113,39,17,192,161,116,34,162,255,212,232,133,6,204,42,41,131,174,108,14,207,90,180,
-  209,92,152,155,98,205,242,149,4,58,161,53,59,75,173,81,163,209,168,147,203,231,201,10,197,162,74,200,27,67,180,110,
-  149,247,224,236,196,104,167,160,39,31,155,43,124,75,3,172,159,3,207,73,219,176,166,130,57,7,166,61,152,12,82,35,
-  179,200,73,53,116,83,37,104,154,212,43,32,114,97,254,38,168,89,72,198,65,151,161,181,3,90,25,72,202,160,175,4,
-  179,10,120,28,90,89,107,231,91,82,182,158,208,137,103,164,159,238,102,37,150,68,164,249,92,210,128,86,233,230,191,35,
-  36,198,104,210,42,41,5,39,129,76,197,78,137,64,138,182,119,180,142,241,4,20,179,69,60,223,167,22,215,136,147,144,
-  56,118,208,198,224,122,30,25,63,131,49,138,36,136,136,194,16,75,130,144,14,117,147,80,84,26,47,80,216,102,72,100,
-  251,121,252,252,50,78,214,106,140,20,38,25,235,213,108,236,155,195,154,113,206,205,238,231,196,124,15,83,199,135,201,116,
-  108,102,211,150,171,56,183,239,126,84,62,67,103,79,23,171,151,239,224,150,215,102,153,159,158,103,114,188,14,225,97,154,
-  11,243,140,150,11,220,246,186,183,115,239,153,211,35,171,206,156,190,170,8,199,5,204,172,129,137,78,176,141,118,165,171,
-  190,7,220,91,234,109,109,47,70,87,127,203,246,200,90,164,146,66,57,202,10,243,127,79,194,232,0,115,51,21,166,39,
-  22,184,229,202,157,88,11,113,162,193,194,189,59,251,104,57,30,187,118,239,35,225,135,51,167,195,217,245,29,190,224,49,
-  104,85,225,120,14,246,116,60,245,244,166,211,247,63,189,110,224,45,183,99,68,170,17,86,58,139,164,133,69,145,144,32,
-  133,193,149,30,78,208,192,204,205,68,18,170,57,104,124,1,204,31,180,73,216,165,22,175,249,93,60,240,12,112,21,169,
-  10,246,6,96,30,226,24,230,134,140,158,58,120,116,223,134,96,120,84,29,189,48,73,103,95,47,245,86,64,92,173,147,
-  247,179,20,10,57,18,155,208,208,33,129,182,104,161,72,170,13,122,123,202,202,125,235,155,74,214,186,229,217,106,125,32,
-  142,66,194,74,213,122,81,152,196,54,164,30,104,225,90,73,41,137,66,181,184,216,234,136,194,86,103,28,71,202,232,166,
-  50,118,209,215,186,233,98,181,132,64,195,84,11,230,69,10,70,129,133,105,9,51,2,230,151,193,76,4,243,71,161,238,
-  131,235,66,102,194,24,47,191,118,141,44,47,44,18,214,235,72,87,33,141,193,152,212,168,40,117,55,209,32,76,123,83,
-  61,141,82,150,8,16,169,58,221,74,151,68,26,172,136,17,194,16,39,233,96,160,209,104,97,149,68,138,84,152,105,219,
-  62,220,73,18,227,72,213,22,150,90,180,246,208,45,133,173,215,241,125,23,137,33,50,33,94,71,15,197,254,107,145,249,
-  14,78,212,207,115,118,122,150,130,188,64,89,158,98,176,212,228,21,131,14,54,57,203,68,235,28,135,91,57,206,61,61,
-  207,79,255,244,187,24,29,217,198,211,251,31,97,106,246,12,202,184,108,94,191,138,254,101,99,220,249,217,221,204,197,69,
-  70,135,70,217,250,227,255,218,123,248,189,191,179,233,117,213,249,27,170,48,177,6,30,126,4,22,159,1,251,197,231,77,
-  12,95,238,49,192,244,194,12,29,29,57,162,56,249,14,0,132,152,24,215,120,158,123,49,125,246,255,246,153,254,166,106,
-  192,72,201,167,124,88,238,72,250,18,243,67,211,134,126,51,0,127,219,179,46,37,163,167,66,216,223,209,106,157,62,254,
-  55,31,28,91,127,253,43,253,114,159,151,78,188,108,2,2,148,204,160,114,25,162,106,19,167,88,162,88,109,208,129,142,
-  73,199,249,118,241,185,201,207,119,38,235,218,15,76,61,239,207,75,21,207,211,192,47,183,63,159,125,30,169,123,11,4,
-  121,56,219,1,199,87,46,204,108,123,242,196,190,158,140,246,40,248,89,202,142,34,235,251,104,87,80,181,17,249,21,3,
-  12,244,118,210,104,53,177,113,194,220,212,52,51,187,199,185,108,213,70,209,33,60,146,82,55,90,88,132,53,34,177,198,
-  109,186,16,4,1,42,189,248,157,32,12,243,73,98,168,36,26,27,37,152,32,180,45,221,50,68,129,201,86,234,177,91,
-  173,5,162,82,105,9,173,181,103,146,200,105,52,22,125,99,230,114,48,147,129,211,26,142,135,112,18,33,93,97,205,170,
-  9,199,235,222,118,245,53,156,250,210,157,132,214,96,100,90,221,72,1,86,166,147,48,97,212,146,137,118,26,84,36,68,
-  154,72,138,76,189,149,72,117,89,54,129,66,182,132,182,130,88,235,116,130,102,64,40,65,156,36,237,148,12,80,66,97,
-  116,186,43,39,29,151,196,24,164,8,145,97,140,13,242,180,132,64,197,30,243,139,211,60,244,192,123,217,114,217,13,116,
-  140,141,162,115,131,196,238,8,13,189,129,11,211,103,136,142,29,166,171,40,24,27,113,48,245,115,252,220,187,175,103,93,
-  95,157,39,30,254,51,194,240,24,219,7,71,169,212,138,28,222,115,132,83,135,87,242,166,31,123,7,95,254,200,71,72,
-  14,31,100,96,229,70,46,220,254,134,238,71,63,254,215,87,95,13,115,13,88,240,97,119,3,154,57,190,55,171,20,19,
-  139,21,94,123,197,90,74,133,252,119,4,33,99,140,181,63,96,54,171,145,235,112,202,110,162,247,177,125,63,156,45,216,
-  99,47,98,124,12,180,20,156,148,112,40,127,255,189,27,14,127,253,254,209,235,222,113,135,208,214,96,149,65,227,165,102,
-  244,74,33,132,32,227,123,72,215,33,178,246,37,197,168,23,219,147,142,114,202,63,177,145,84,41,188,240,60,78,224,120,
-  155,200,245,248,198,45,243,126,72,234,112,65,195,190,85,179,139,151,77,214,102,186,54,93,245,10,89,12,67,50,170,192,
-  140,12,81,197,28,43,55,109,100,104,205,10,156,98,150,32,9,137,130,16,105,45,217,66,47,245,147,19,248,89,72,116,
-  11,141,70,234,20,61,77,75,80,148,2,161,99,172,16,20,164,2,207,65,102,221,37,79,52,81,149,90,25,101,149,103,
-  133,155,196,113,46,49,150,80,107,148,1,211,10,160,214,52,157,211,211,13,255,236,177,185,176,82,155,30,17,226,168,197,
-  84,34,216,220,88,191,161,99,217,134,245,84,63,246,145,84,151,211,54,230,90,138,72,22,214,98,132,2,37,73,255,233,
-  212,9,50,137,13,70,75,144,62,74,106,76,18,98,146,152,108,54,67,24,54,137,147,0,65,202,33,105,13,185,108,39,
-  90,39,169,17,125,123,9,53,181,47,81,88,19,145,36,9,17,14,81,208,174,146,154,9,73,125,150,82,118,142,112,230,
-  62,138,93,43,144,129,68,235,136,196,106,178,217,18,178,184,134,11,245,44,181,51,17,181,70,133,91,54,94,205,35,247,
-  222,201,198,177,38,221,197,49,162,102,158,137,164,130,191,90,48,25,87,201,184,176,101,203,229,60,248,137,207,18,52,37,
-  35,171,54,240,208,246,237,203,123,247,236,185,126,21,76,159,133,218,74,56,42,32,204,182,227,154,95,46,36,40,128,86,
-  204,93,79,29,231,95,253,232,237,20,115,89,162,56,254,118,247,189,31,188,201,153,235,48,151,47,147,60,182,239,135,114,
-  90,230,108,126,17,61,116,8,201,65,184,96,16,79,246,52,195,13,39,255,224,247,203,43,175,92,95,30,90,54,132,49,
-  25,140,10,145,194,162,28,23,161,28,28,199,165,148,117,201,27,243,146,28,115,239,109,143,98,111,32,221,193,122,8,120,
-  42,173,194,184,21,24,35,221,249,249,237,209,110,26,175,216,136,8,82,75,8,41,5,225,125,7,236,225,217,218,124,3,
-  246,116,88,246,244,30,62,190,108,124,221,198,190,53,157,157,92,168,205,211,179,125,61,87,223,124,61,110,46,131,113,4,
-  185,98,30,71,41,38,206,158,37,14,90,108,186,122,39,31,63,240,33,234,170,68,62,91,0,107,240,4,100,61,135,188,
-  54,132,128,201,180,109,72,196,115,153,101,75,30,57,153,216,193,70,6,100,130,239,40,148,77,16,82,146,104,144,221,5,
-  220,222,188,76,214,174,47,26,179,181,248,165,135,246,44,47,31,56,180,238,199,9,23,38,161,152,189,102,123,214,201,8,
-  194,48,198,207,58,109,240,73,35,101,172,105,71,239,200,4,35,20,82,120,88,171,48,177,192,234,152,56,106,18,199,117,
-  50,74,144,207,101,233,44,118,160,148,64,8,155,154,134,197,17,174,114,136,181,161,21,182,200,229,178,40,165,136,162,40,
-  181,93,69,16,39,26,147,24,116,18,35,176,233,186,134,147,163,190,88,167,195,87,188,229,103,95,203,192,96,23,113,28,
-  34,76,130,52,169,105,153,227,148,153,152,147,124,238,171,123,25,159,126,156,43,182,230,56,118,110,145,226,250,87,17,200,
-  83,28,57,118,138,153,133,73,74,125,17,214,134,228,76,68,227,224,103,89,59,186,130,135,74,46,141,184,138,51,221,98,
-  195,117,183,136,207,157,155,88,251,115,115,83,183,102,211,61,252,135,214,97,79,91,152,95,132,166,110,143,234,159,95,13,
-  191,148,115,102,124,154,63,254,240,157,252,226,91,95,69,169,144,251,118,32,244,131,7,64,34,37,172,127,104,73,232,23,
-  3,251,58,29,133,46,14,10,158,54,150,21,157,79,61,51,240,212,95,254,237,198,219,126,227,151,92,55,163,211,241,173,
-  112,144,210,65,58,10,157,196,116,117,22,201,186,110,219,98,254,219,191,105,124,82,187,139,92,251,223,242,218,119,175,32,
-  29,127,47,145,225,23,99,120,107,195,221,236,126,219,213,100,60,7,225,58,24,207,97,112,172,143,161,223,255,124,184,23,
-  142,187,240,240,198,74,107,205,125,7,14,118,30,187,252,106,119,231,245,87,112,245,107,95,133,151,205,165,62,214,66,32,
-  76,140,176,130,254,129,33,166,207,157,167,111,69,63,21,23,62,253,241,79,4,87,117,14,76,140,40,213,202,11,37,202,
-  25,95,120,70,91,143,36,84,74,218,80,91,217,178,86,33,5,197,40,168,106,173,109,36,85,102,198,58,217,166,116,164,
-  201,122,194,31,232,43,228,123,123,138,185,82,103,169,156,119,133,34,65,52,234,216,48,192,201,186,236,220,190,131,207,52,
-  131,242,157,23,78,101,87,68,161,94,117,229,14,113,122,110,254,226,126,138,16,92,180,187,16,237,63,91,97,145,210,66,
-  146,144,104,77,28,6,228,178,138,109,219,215,176,115,231,6,6,7,250,89,190,124,53,205,38,60,240,192,19,68,113,66,
-  20,69,88,107,209,73,154,15,102,109,66,146,164,6,244,105,56,161,67,46,155,199,81,46,129,14,169,213,2,114,37,15,
-  43,99,172,208,104,13,141,106,147,122,117,134,164,187,132,49,25,164,128,216,90,4,25,180,245,249,223,239,251,3,62,251,
-  165,123,121,219,107,199,24,240,135,137,206,62,192,200,174,215,80,11,86,80,218,182,140,214,228,126,142,31,219,199,80,209,
-  208,229,58,76,28,121,152,142,242,28,17,167,56,51,211,203,224,192,24,121,109,96,235,86,255,35,247,126,109,219,59,160,
-  80,196,174,183,112,48,134,67,83,105,209,59,57,13,181,24,194,206,151,89,170,92,152,154,227,125,159,248,10,255,242,109,
-  175,254,39,7,66,63,212,99,248,229,47,226,139,22,83,222,37,249,5,107,207,157,135,7,59,96,244,204,135,63,222,115,
-  244,214,107,135,151,111,94,78,38,223,131,240,28,60,215,195,145,30,49,134,216,106,98,215,83,34,245,20,194,182,57,155,
-  111,30,193,215,72,125,148,243,109,82,90,191,192,164,107,137,80,92,250,92,235,169,19,108,253,177,63,97,247,117,235,185,
-  80,240,177,203,122,25,60,62,193,44,216,5,88,40,73,113,176,104,236,225,222,61,251,214,53,174,189,105,224,198,31,123,
-  39,137,14,48,109,163,117,4,104,41,17,214,160,74,29,248,3,9,65,163,202,174,155,47,35,249,244,167,247,190,101,252,
-  100,3,40,170,116,75,221,145,96,36,68,6,90,54,157,110,77,89,88,84,208,20,233,194,68,70,67,46,134,76,12,158,
-  62,66,182,226,120,229,179,29,221,195,251,7,7,135,157,161,193,242,178,190,65,183,47,223,75,172,96,172,91,240,234,157,
-  235,57,209,95,114,31,218,119,76,252,233,246,157,226,209,15,126,100,137,12,189,152,151,150,122,46,153,52,5,68,184,72,
-  35,48,73,72,49,235,242,138,155,174,225,182,219,94,201,166,77,107,40,20,178,104,29,226,120,89,246,236,57,77,24,5,
-  68,113,154,59,18,197,26,41,51,8,35,48,164,153,97,126,166,221,138,216,152,40,12,41,20,242,212,91,13,130,88,147,
-  49,160,109,4,186,65,172,21,81,51,166,213,146,248,8,116,220,66,40,69,44,92,156,156,207,211,207,62,195,125,15,222,
-  69,94,249,100,84,39,121,191,128,27,79,83,158,184,11,71,75,142,78,187,148,70,54,51,58,230,33,22,158,37,8,103,
-  48,174,230,208,161,51,172,217,212,65,159,189,158,82,239,54,130,112,158,203,86,95,205,201,181,55,149,255,122,247,23,182,
-  118,28,220,189,106,103,189,181,107,53,28,207,194,129,24,14,78,193,97,5,167,47,164,34,209,208,121,25,111,246,241,233,
-  121,254,236,19,95,229,23,223,254,42,138,249,236,119,228,132,46,157,255,11,0,52,255,34,191,240,33,160,15,90,87,194,
-  65,3,247,14,158,155,26,222,243,71,239,237,44,253,206,111,229,50,190,38,155,45,80,46,101,80,66,18,217,24,183,152,
-  71,45,27,245,245,225,227,217,16,252,155,64,190,234,121,165,244,18,152,212,73,85,176,159,123,9,99,85,155,104,114,137,
-  102,242,171,123,56,6,76,57,138,13,214,162,64,14,9,242,218,82,114,32,211,165,49,61,235,151,131,202,226,196,97,10,
-  131,82,96,219,53,153,149,14,177,213,20,122,122,177,81,196,230,109,91,184,48,50,224,150,78,76,22,45,20,3,48,115,
-  105,118,151,246,192,22,192,203,66,38,129,121,224,209,4,206,56,16,9,200,56,80,80,144,247,133,40,88,232,238,76,162,
-  193,177,217,137,179,149,217,137,206,211,251,232,61,151,43,244,62,211,213,215,39,58,186,243,163,235,199,252,98,206,119,94,
-  185,113,187,200,116,244,184,217,98,153,201,233,185,148,116,54,105,194,136,84,41,63,99,76,140,181,6,87,248,68,97,204,
-  202,229,67,252,198,175,255,75,182,108,92,1,54,33,138,18,162,86,12,196,40,233,50,59,51,135,239,231,9,163,6,202,
-  241,240,132,106,251,100,91,176,54,245,90,106,171,160,43,149,10,165,82,137,122,195,16,39,1,82,1,184,8,225,96,73,
-  147,48,144,138,133,74,19,163,178,104,33,17,38,196,8,129,181,25,190,240,169,123,136,155,10,95,41,102,79,44,128,238,
-  161,123,32,198,149,103,136,231,224,194,211,179,68,227,231,89,185,186,159,242,138,141,116,13,140,112,254,244,9,78,79,45,
-  32,131,26,29,180,144,241,33,186,202,89,84,255,50,70,54,111,96,230,71,222,236,76,158,61,85,126,232,209,251,202,143,
-  60,122,215,242,213,39,246,110,223,105,146,211,157,112,32,129,103,38,96,183,129,83,59,97,177,6,177,247,18,11,162,243,
-  211,115,188,255,227,151,64,232,159,204,20,108,233,60,145,146,192,118,21,44,28,21,226,41,215,218,209,206,47,62,52,248,
-  212,234,79,109,187,230,23,127,222,169,45,44,96,35,7,71,129,16,22,47,227,209,185,105,75,46,184,235,222,225,2,12,
-  20,225,248,40,180,158,239,187,236,1,79,146,202,173,95,108,95,255,66,239,182,237,137,22,245,52,126,106,192,71,108,149,
-  214,222,120,24,113,249,131,163,3,93,255,250,218,45,128,64,75,137,37,73,53,52,86,32,177,160,5,170,157,79,223,85,
-  238,66,74,151,248,242,29,195,247,156,248,242,254,72,8,119,118,217,80,39,91,55,149,71,134,150,201,206,76,54,62,188,
-  119,95,179,116,255,253,23,182,65,103,132,141,52,156,146,80,237,2,49,14,202,3,71,88,155,113,32,167,5,29,210,210,
-  215,1,195,87,192,232,21,205,122,223,98,179,222,213,56,127,178,120,240,192,147,189,143,143,173,222,184,113,219,206,206,219,
-  55,174,227,238,191,249,36,103,206,78,226,100,50,237,69,223,246,30,152,76,147,79,165,6,75,68,54,47,120,219,219,223,
-  72,189,94,101,102,102,134,254,190,94,132,208,224,136,52,58,25,143,241,241,25,164,244,145,78,140,21,85,140,141,144,210,
-  18,69,26,99,12,82,42,98,157,16,5,33,66,8,226,56,68,74,129,54,45,170,213,6,249,108,9,95,229,9,194,42,
-  74,65,146,68,84,26,139,212,77,3,43,19,124,1,190,155,101,247,238,125,124,245,174,123,208,72,106,113,131,103,15,159,
-  226,252,226,38,186,199,58,81,73,196,196,220,34,187,118,173,101,245,152,33,227,183,72,58,60,76,118,136,51,213,5,182,
-  223,242,106,238,251,227,15,179,102,125,63,214,137,105,212,22,41,134,103,144,11,130,78,127,3,185,194,50,198,222,244,11,
-  52,238,248,113,239,252,222,199,134,62,243,192,157,125,35,123,239,91,179,179,190,176,173,11,182,37,240,164,134,253,187,225,
-  108,17,230,55,65,180,159,23,31,113,187,4,66,191,240,182,87,81,42,92,2,161,127,84,0,250,173,23,249,133,173,116,
-  212,205,40,232,9,107,199,107,130,7,187,12,253,231,223,251,151,217,71,58,10,235,95,249,83,111,144,141,197,102,26,27,
-  44,5,50,134,193,45,235,220,211,66,140,89,107,215,122,176,111,1,90,30,207,173,44,36,237,10,40,251,18,30,240,18,
-  80,133,164,41,21,243,160,174,130,114,21,214,10,184,38,177,246,154,61,142,186,108,207,170,141,203,6,174,190,76,14,246,
-  244,3,150,216,73,144,218,32,100,6,131,64,137,246,35,176,26,161,65,228,178,20,125,151,107,127,250,39,250,159,90,179,
-  166,227,242,237,59,221,205,87,94,33,7,6,250,241,148,2,145,97,174,81,235,254,47,111,253,137,98,249,203,95,93,92,
-  14,235,76,90,13,86,130,174,156,126,56,159,73,134,39,170,97,54,209,205,94,168,248,150,201,4,142,251,144,55,208,165,
-  5,253,121,33,6,242,198,142,45,179,108,89,125,230,76,240,145,102,115,235,202,222,219,123,243,139,117,250,154,77,78,233,
-  136,166,35,145,56,68,113,219,78,22,137,84,6,99,45,90,27,86,173,92,201,232,112,7,199,142,31,96,177,209,162,191,
-  103,144,114,49,135,213,17,137,129,153,233,89,194,216,67,71,154,160,25,163,117,76,34,99,4,14,142,82,132,65,11,107,
-  92,28,71,96,180,69,107,75,146,88,28,233,225,170,136,66,206,39,104,52,83,123,85,44,73,28,179,48,211,64,73,69,
-  147,26,245,106,130,176,17,7,142,28,229,45,239,124,35,171,214,174,38,95,40,208,221,81,102,213,202,110,30,127,226,107,
-  116,84,78,179,110,101,142,19,167,78,81,77,178,116,14,150,168,31,127,132,19,39,190,192,230,155,111,227,177,199,31,230,
-  83,159,123,128,119,47,191,149,238,145,45,212,235,17,65,116,146,188,62,75,71,253,36,121,14,80,173,116,18,184,27,88,
-  189,227,54,130,237,183,58,11,103,158,233,125,234,177,207,117,116,223,251,197,209,229,11,211,91,123,96,127,2,79,207,195,
-  211,111,134,83,175,72,205,79,227,23,107,195,122,126,122,142,247,125,252,43,252,203,183,191,250,18,8,253,99,2,208,111,
-  191,20,77,66,27,136,28,104,97,57,28,193,151,70,19,237,156,248,159,127,168,30,42,103,215,94,251,250,183,136,168,122,
-  6,27,43,84,162,232,218,190,220,185,176,105,213,160,222,127,124,125,219,246,116,166,10,250,239,218,213,79,22,216,192,119,
-  182,86,48,237,15,153,2,160,152,6,25,167,252,76,126,27,244,44,194,38,7,174,31,151,188,242,220,192,200,202,198,142,
-  173,165,155,70,134,193,8,62,255,39,31,226,186,55,188,154,245,87,109,66,219,24,107,52,162,157,154,97,37,88,43,81,
-  70,98,173,68,72,197,77,183,190,138,155,110,123,109,38,93,116,139,176,198,164,22,22,38,160,59,239,243,250,255,248,43,
-  29,119,223,119,223,192,170,86,184,50,132,21,37,152,94,86,240,26,215,254,206,91,57,114,190,198,226,175,127,212,58,25,
-  87,111,74,52,43,180,73,230,33,248,40,44,92,111,57,171,172,205,70,136,161,38,200,85,54,46,141,9,102,14,206,84,
-  122,175,236,233,228,45,219,119,176,122,120,144,175,29,220,195,133,153,69,140,147,5,41,177,73,76,36,99,148,200,98,2,
-  203,201,99,167,184,238,154,215,176,108,85,63,205,0,142,31,62,199,252,220,34,35,195,125,4,65,68,101,113,30,215,45,
-  179,56,191,136,159,201,17,71,146,70,163,74,198,151,248,25,159,68,71,88,155,32,132,135,231,101,136,99,112,28,31,97,
-  20,229,130,139,235,89,164,155,38,126,26,12,94,198,101,97,174,202,212,133,26,142,239,208,217,53,72,177,84,226,95,239,
-  188,140,92,71,169,189,165,223,78,26,181,146,7,190,250,36,193,66,200,232,96,204,230,245,203,153,89,116,104,212,170,68,
-  97,139,93,215,92,201,195,15,31,226,191,253,225,167,89,140,28,238,255,242,39,120,203,143,175,162,232,119,81,149,155,168,
-  216,117,212,90,169,101,71,201,158,98,212,59,77,101,106,15,161,222,66,121,232,42,226,119,110,119,155,55,189,97,240,209,
-  251,239,238,203,220,243,169,53,151,205,143,111,238,130,13,9,60,213,5,71,3,56,223,130,249,22,132,241,139,0,162,241,
-  153,121,222,251,137,175,240,115,111,185,149,174,66,238,82,202,233,63,6,0,245,190,140,111,10,129,255,15,170,151,195,238,
-  10,176,170,169,197,233,255,244,135,60,214,146,107,118,188,253,102,105,91,85,130,160,2,133,50,133,55,189,161,103,126,255,
-  239,109,40,192,218,105,56,243,21,88,252,111,237,159,147,5,254,15,208,249,45,52,28,73,123,244,222,9,162,9,238,83,
-  224,143,65,105,10,186,61,232,3,134,93,88,101,96,203,161,156,191,109,239,154,13,99,219,215,95,38,70,50,9,173,184,
-  142,107,50,20,206,204,241,224,255,249,48,65,229,141,108,191,237,26,52,139,168,56,1,229,161,201,34,173,196,72,137,176,
-  81,106,178,166,117,59,58,90,96,81,72,233,32,150,242,233,117,194,117,87,95,197,3,183,221,56,52,243,185,175,12,118,
-  10,54,212,45,199,15,159,93,12,30,251,47,159,215,255,106,253,16,191,1,140,253,250,79,51,246,181,199,40,61,178,151,
-  66,250,248,205,221,16,174,135,112,25,214,137,97,110,26,244,181,63,254,182,242,132,163,168,157,153,32,235,101,217,62,180,
-  140,85,195,203,248,155,175,125,153,163,11,149,180,66,145,16,11,137,65,35,177,92,56,63,142,137,13,97,208,164,92,234,
-  229,198,155,174,103,252,252,105,46,156,63,207,137,35,231,168,53,91,148,74,5,114,121,143,86,171,73,18,71,120,202,69,
-  181,165,157,158,155,199,203,164,177,58,73,172,145,42,38,8,234,100,28,15,199,247,136,146,16,17,164,137,24,253,189,125,
-  108,218,124,5,235,214,173,160,111,176,135,98,169,132,114,220,52,230,89,199,36,173,58,66,73,140,86,168,140,225,228,193,
-  11,220,249,247,15,115,235,149,189,140,207,88,38,38,103,240,179,89,100,70,160,114,61,124,242,238,125,124,232,131,79,17,
-  219,60,142,155,240,212,83,95,100,126,113,130,155,110,254,49,150,173,186,145,200,207,18,20,251,72,138,61,232,214,10,26,
-  245,167,41,101,46,48,160,38,89,152,57,78,203,221,72,166,107,35,234,45,219,85,237,230,55,247,62,250,192,103,58,7,
-  191,242,209,209,85,243,211,59,58,224,176,128,61,6,246,238,131,147,62,204,119,180,239,109,223,110,247,112,106,122,158,123,
-  190,254,36,111,127,235,109,196,65,240,131,55,134,151,75,130,211,31,82,0,122,185,126,199,191,10,246,189,80,217,6,207,
-  84,128,229,141,186,158,252,205,255,126,243,151,159,217,179,110,251,123,126,202,31,30,44,18,53,91,148,111,186,193,187,240,
-  55,31,93,95,56,123,97,151,134,99,151,195,193,253,16,149,219,163,245,163,164,118,120,226,121,31,103,64,172,2,121,1,
-  252,13,144,143,82,109,98,95,19,134,29,88,230,195,88,13,70,23,97,240,28,12,76,45,27,232,17,107,175,204,245,101,
-  186,168,207,46,226,118,65,156,5,145,40,10,194,195,209,154,167,62,254,85,148,231,177,229,198,109,152,164,133,21,46,137,
-  214,120,74,99,117,64,101,126,30,41,92,202,93,253,128,131,69,182,251,61,139,21,150,176,85,33,137,19,10,221,195,236,
-  250,137,31,237,57,240,185,175,12,189,2,177,198,40,177,34,214,102,241,213,199,38,235,61,199,38,77,2,12,61,190,15,
-  198,103,196,49,144,66,8,222,97,173,249,20,216,38,72,3,29,64,127,0,114,213,101,59,212,198,177,17,190,246,251,127,
-  198,154,142,110,116,28,208,159,205,241,234,203,118,113,246,174,47,147,56,30,194,58,56,66,18,91,131,116,4,71,143,29,
-  199,154,52,251,188,85,171,34,112,25,89,53,198,208,178,101,140,173,88,164,220,55,200,163,143,62,205,161,67,7,169,54,
-  230,112,132,67,198,201,18,39,49,186,73,154,124,42,36,142,43,144,74,160,140,193,10,77,98,18,58,187,58,89,179,110,
-  5,155,54,111,100,205,186,181,12,13,15,145,205,57,24,27,166,22,32,6,76,146,164,105,35,78,123,173,195,182,141,234,
-  85,7,239,253,203,223,224,169,19,71,40,117,110,228,240,196,113,150,173,42,49,49,222,224,236,217,179,52,131,152,179,147,
-  139,44,38,77,148,235,33,181,79,87,214,99,252,228,147,124,224,240,1,54,111,121,45,175,188,253,205,116,47,91,142,113,
-  61,34,53,74,152,93,206,108,120,6,79,239,102,48,123,148,188,152,226,216,228,50,146,194,46,74,29,235,176,111,251,101,
-  167,250,138,59,70,239,187,251,115,195,197,175,126,120,227,206,122,101,103,55,236,9,225,225,8,30,219,2,167,239,132,240,
-  150,111,83,101,91,32,115,248,36,79,62,186,151,61,219,214,254,192,105,110,172,182,120,113,130,211,158,16,255,176,97,145,
-  243,114,191,177,9,252,28,216,223,129,234,245,240,76,12,97,95,146,204,21,62,249,185,27,158,120,122,239,142,158,159,121,
-  91,207,21,119,220,72,255,166,85,28,124,215,219,6,103,255,251,31,94,221,13,251,59,224,194,126,152,105,144,138,11,51,
-  41,143,195,29,224,132,224,27,200,157,132,156,16,162,179,0,35,218,218,81,23,70,66,24,173,193,232,108,70,13,85,151,
-  143,118,71,27,54,20,135,175,186,194,221,184,124,132,43,202,30,95,255,139,123,161,101,209,229,60,117,211,164,172,125,92,
-  153,37,16,146,216,9,232,52,134,7,63,242,25,138,3,93,44,91,191,10,27,7,184,142,38,104,85,169,77,77,18,6,
-  45,84,38,71,161,88,66,122,105,59,102,1,132,33,104,86,153,60,119,18,147,64,20,132,92,121,237,101,242,145,203,86,
-  13,5,79,159,24,113,16,87,186,66,196,85,107,199,31,135,202,27,160,89,252,202,35,145,0,91,149,82,72,229,48,26,
-  71,252,120,74,184,103,67,88,225,192,242,102,111,79,103,207,142,77,157,29,253,253,68,158,143,49,150,68,1,73,200,246,
-  101,99,108,26,26,100,247,248,56,158,244,17,86,146,88,141,116,28,206,157,31,103,97,190,194,208,96,153,90,163,73,117,
-  113,14,227,196,116,116,245,48,48,54,200,107,86,142,113,203,107,111,229,217,189,123,121,240,222,251,121,248,193,71,153,56,
-  63,133,148,30,57,183,128,227,42,194,168,65,173,17,226,251,14,125,125,93,172,90,179,156,173,91,183,177,117,219,22,134,
-  151,13,166,182,171,198,16,235,132,80,71,169,37,172,77,121,162,116,12,105,177,54,245,132,178,6,92,47,195,158,199,143,
-  241,249,79,61,194,181,175,249,121,250,10,27,89,49,154,165,48,178,154,199,38,79,240,245,103,31,192,77,36,82,101,80,
-  94,54,141,36,180,13,50,142,207,112,255,86,140,8,153,158,188,155,143,188,255,62,198,86,92,201,229,55,188,145,225,245,
-  151,33,61,151,68,46,71,139,229,28,174,236,161,211,28,98,77,247,121,102,91,179,204,206,174,193,45,109,39,91,90,71,
-  231,143,254,170,92,188,229,142,222,175,124,254,131,221,93,119,127,113,213,85,113,99,101,1,74,49,124,169,4,167,247,131,
-  254,78,237,152,186,247,9,214,61,178,7,97,127,0,5,209,75,202,248,31,70,0,250,249,31,187,227,101,127,115,40,4,
-  227,90,219,229,159,189,183,50,219,10,247,84,97,222,135,115,219,79,158,62,53,254,159,254,215,229,15,127,242,139,171,86,
-  190,243,71,75,249,85,171,229,190,130,191,188,175,30,12,7,144,149,164,194,195,129,182,14,104,64,144,47,35,70,98,107,
-  215,74,88,150,129,158,196,218,254,58,140,205,73,70,167,187,75,189,254,214,141,197,190,107,175,202,110,189,254,58,57,184,
-  97,61,157,93,93,228,60,15,18,77,117,110,150,51,219,78,179,247,177,253,100,108,142,156,91,38,12,99,84,70,226,34,
-  8,181,65,139,58,185,88,242,200,231,191,194,200,202,159,69,217,144,96,161,202,92,181,78,88,115,112,165,196,198,81,218,
-  178,184,217,54,87,4,104,77,88,91,64,154,4,172,96,126,250,2,163,203,71,89,249,230,215,246,237,121,250,143,187,175,
-  210,102,27,208,237,192,116,11,38,59,225,212,28,156,171,192,124,193,152,16,27,155,12,216,115,32,26,208,225,195,234,16,
-  58,162,203,118,12,12,44,31,201,0,204,218,132,185,249,10,29,253,69,140,78,200,36,17,55,239,184,156,221,231,63,77,
-  228,104,18,43,48,214,128,181,44,86,235,156,60,115,129,161,129,14,226,32,2,5,149,185,84,76,209,217,219,143,142,35,
-  28,79,114,249,149,219,185,226,202,29,252,196,79,253,24,251,246,28,224,137,199,158,230,240,225,163,8,145,142,224,215,174,
-  219,201,43,175,191,150,117,27,214,208,211,221,129,200,120,96,53,38,14,208,137,69,27,1,66,33,80,32,36,66,164,27,
-  249,105,46,156,68,155,36,213,39,161,48,49,188,247,15,255,128,53,27,174,99,243,85,111,34,23,204,176,125,85,7,103,
-  90,189,108,191,106,43,123,239,251,56,81,165,65,44,45,218,196,16,249,184,210,16,198,85,170,181,2,93,221,157,108,223,
-  178,21,76,194,249,201,3,220,245,201,67,44,95,249,74,118,92,119,7,157,99,43,9,173,193,235,221,202,124,180,146,218,
-  236,110,86,230,206,177,206,123,154,201,197,25,234,254,86,156,100,140,114,223,213,116,253,202,118,121,250,85,111,239,250,240,
-  159,254,215,87,190,227,216,222,74,25,14,206,193,248,209,212,219,233,219,15,55,180,70,54,91,63,144,23,169,37,221,16,
-  216,244,188,63,255,176,0,145,243,59,191,246,238,151,255,221,74,98,90,17,61,119,63,206,211,173,176,57,11,39,214,193,
-  124,0,39,70,44,123,226,221,7,118,46,238,254,205,205,161,159,233,93,158,36,167,53,92,80,16,68,164,251,92,127,159,
-  246,231,170,19,49,96,148,122,85,148,232,91,166,50,238,234,70,95,87,201,27,91,150,203,237,220,84,232,189,242,106,247,
-  170,237,155,233,31,91,70,33,95,64,160,177,38,65,199,49,81,171,133,43,28,10,249,78,174,184,126,23,123,158,217,71,
-  216,140,73,132,139,41,42,78,76,143,179,170,220,75,81,56,52,109,64,62,91,96,238,228,9,230,206,156,162,80,204,80,
-  169,212,121,252,201,61,124,240,125,127,205,205,175,190,142,119,253,228,59,8,91,33,174,159,180,125,28,52,205,197,69,90,
-  243,139,88,29,162,227,244,197,159,157,156,224,182,55,189,195,255,223,79,28,219,250,245,3,7,123,152,158,93,219,91,173,
-  47,174,176,204,101,96,82,192,73,13,135,106,112,2,107,103,14,64,115,17,148,11,125,2,198,166,17,197,145,87,189,106,
-  48,167,28,180,209,204,218,128,189,19,11,220,177,236,10,154,245,5,194,176,197,182,21,43,184,122,205,58,30,56,121,140,
-  68,185,169,233,153,84,4,81,200,161,163,39,184,250,170,205,184,78,10,14,214,66,101,118,22,79,42,114,165,46,180,128,
-  132,4,97,45,221,125,93,220,248,170,91,185,254,230,155,105,181,26,24,157,0,134,92,161,128,114,50,64,132,77,52,38,
-  137,48,86,35,5,40,71,130,78,9,122,179,148,45,212,78,154,21,50,181,106,148,86,97,181,197,205,149,249,226,167,238,
-  228,171,247,220,207,143,189,231,15,241,187,122,112,170,83,152,150,192,196,9,157,221,253,244,116,140,50,62,127,20,199,1,
-  45,35,146,88,224,186,89,92,229,210,106,213,152,153,75,144,86,179,124,120,140,203,119,172,38,138,66,90,213,89,206,237,
-  254,40,97,243,42,220,225,109,248,189,61,72,153,195,25,188,141,115,141,115,148,155,79,49,82,154,160,170,90,76,235,38,
-  51,147,5,230,239,188,11,142,236,182,203,42,139,13,9,201,210,82,243,146,31,211,203,145,120,188,148,243,188,148,170,212,
-  120,236,123,120,161,54,72,227,190,151,28,58,127,104,42,160,197,234,119,97,122,32,37,34,140,232,49,150,67,41,113,28,
-  119,192,244,2,84,90,112,202,194,238,14,88,39,130,176,23,152,116,225,169,24,42,187,218,252,79,51,125,98,101,221,218,
-  222,86,146,92,190,251,230,235,111,189,252,103,127,212,95,121,229,101,116,118,15,80,46,22,17,184,24,83,131,216,98,194,
-  6,70,91,148,148,72,161,144,210,96,116,140,112,44,107,215,175,99,120,120,132,202,153,89,180,74,149,136,98,237,0,182,
-  179,135,218,145,11,20,157,28,74,11,178,70,179,255,177,61,100,55,174,226,11,239,255,115,90,159,248,66,243,109,245,32,
-  56,60,61,145,63,123,219,171,51,43,150,151,83,235,83,37,105,213,106,212,230,22,176,173,16,107,12,2,7,169,52,97,
-  88,161,103,112,132,255,250,145,191,235,90,172,78,117,77,78,76,177,239,254,251,147,175,126,226,51,11,165,221,7,207,111,
-  105,197,43,251,97,163,129,19,9,28,158,128,83,82,136,166,180,118,5,208,51,49,48,210,189,245,149,215,148,209,49,74,
-  41,138,197,28,143,157,121,140,27,47,187,140,92,38,75,37,105,209,211,108,242,214,43,94,193,222,11,231,168,4,17,24,
-  139,81,2,141,228,196,153,51,233,84,80,72,28,41,72,140,1,163,89,24,159,34,9,5,229,254,126,140,6,173,99,140,
-  8,81,34,53,148,205,102,51,72,229,97,173,198,152,8,171,147,182,177,25,96,28,4,10,33,52,90,167,213,158,20,96,
-  218,161,201,194,62,39,180,177,88,148,80,8,165,8,170,1,127,249,23,31,76,35,169,29,135,140,205,97,18,7,215,19,
-  184,40,92,153,165,123,104,152,83,103,159,197,87,5,164,236,68,136,0,76,66,18,122,40,207,18,199,77,90,58,199,248,
-  220,4,129,182,148,187,202,20,250,124,60,71,160,226,99,36,103,26,52,167,71,233,27,217,4,57,65,208,53,68,93,189,
-  158,227,115,251,25,245,206,112,244,145,255,195,153,15,223,213,124,91,99,97,124,8,78,231,225,104,4,79,184,112,170,148,
-  210,129,47,171,98,88,2,18,251,188,95,29,210,165,105,219,150,144,44,217,201,214,158,167,103,211,237,143,239,213,70,255,
-  210,73,248,70,7,136,127,214,28,208,11,189,88,178,141,206,165,116,80,54,25,192,124,8,199,28,200,218,244,115,149,63,
-  133,232,227,41,127,196,111,183,65,8,33,68,108,173,51,186,125,179,115,213,107,95,77,78,129,112,92,146,184,137,49,18,
-  169,34,148,178,88,157,65,57,233,2,37,218,193,160,17,78,130,49,6,183,203,163,88,234,164,170,23,201,40,135,34,1,
-  117,163,25,216,186,134,163,231,39,232,52,30,74,122,148,242,125,124,237,107,15,17,253,201,159,52,55,62,125,224,244,6,
-  56,225,128,245,79,78,108,56,116,247,189,171,150,253,226,207,72,176,152,48,160,58,63,67,43,104,34,19,129,81,14,70,
-  217,246,210,173,67,125,113,138,98,169,72,127,95,137,254,254,78,182,109,223,225,220,246,227,111,239,125,228,254,71,122,159,
-  253,212,157,43,159,249,234,189,23,54,207,45,142,142,192,186,12,92,136,173,157,5,70,34,40,231,118,236,40,143,109,89,
-  35,34,211,196,35,139,239,120,156,91,152,227,240,228,4,87,141,142,225,196,17,213,102,157,145,114,47,55,172,219,204,157,
-  187,159,190,184,21,47,5,28,60,116,156,133,169,10,185,140,160,110,67,132,54,233,135,99,89,152,157,34,182,49,221,3,
-  253,8,52,173,122,19,37,21,158,239,35,165,194,232,180,181,82,202,37,165,59,20,194,166,246,159,82,24,76,210,74,237,
-  91,93,63,37,151,133,76,173,65,5,36,54,193,36,9,74,42,172,80,72,191,192,61,95,254,18,207,236,222,67,222,207,
-  227,187,14,82,104,10,94,157,130,235,96,19,23,233,186,116,116,14,225,56,30,142,167,176,198,65,138,12,218,36,88,101,
-  16,142,192,34,152,157,95,192,40,15,47,170,147,143,93,172,22,24,207,67,10,69,193,153,71,133,211,156,157,216,143,59,
-  186,158,190,213,155,241,115,93,36,93,59,152,138,214,51,123,236,110,251,182,198,194,248,6,184,43,128,251,66,56,161,96,
-  162,158,190,255,146,59,190,139,247,181,253,38,16,58,3,124,186,125,225,188,166,77,110,223,73,234,91,253,76,27,116,30,
-  2,238,227,59,135,47,188,220,51,7,140,243,226,133,151,255,44,0,232,155,79,41,181,38,13,247,66,232,183,53,68,69,
-  224,179,237,23,115,24,120,36,5,44,83,178,118,174,12,7,131,63,122,255,225,207,52,170,27,95,249,175,126,70,118,142,
-  173,32,235,58,184,145,70,203,24,227,104,192,199,42,131,197,32,172,147,218,149,138,4,240,208,54,193,70,17,69,199,197,
-  203,88,20,2,89,151,228,189,94,26,165,18,83,173,152,250,226,52,227,251,247,210,119,112,207,244,174,106,229,41,15,30,
-  138,5,207,198,144,235,181,188,250,244,103,191,208,177,248,99,111,237,45,120,14,213,233,243,52,26,243,196,26,92,225,161,
-  188,12,210,245,168,86,99,76,61,166,191,71,81,89,152,160,220,55,64,98,21,150,26,189,93,157,188,225,77,111,225,142,
-  55,190,174,124,232,208,193,242,189,31,248,208,216,222,79,126,121,124,228,236,248,248,58,107,23,59,64,237,67,248,157,175,
-  188,118,56,239,186,196,129,1,109,176,137,197,10,151,251,14,29,96,219,200,74,50,218,33,116,35,22,27,243,220,180,97,
-  51,15,31,57,200,124,162,209,113,140,231,42,46,156,155,228,194,133,25,214,172,30,36,177,49,174,21,40,229,146,96,16,
-  34,161,190,56,131,181,33,221,125,253,228,114,89,234,245,26,65,101,145,92,62,139,235,151,16,202,193,46,205,118,69,234,
-  21,36,226,144,168,89,37,136,106,56,158,2,161,176,184,32,228,115,222,223,74,34,176,200,118,66,71,210,108,242,129,15,
-  252,13,6,131,192,69,57,89,116,70,227,183,170,248,202,96,148,139,245,36,57,183,128,175,114,228,242,89,146,40,33,8,
-  44,202,85,196,196,248,94,9,37,37,36,134,176,90,37,204,250,84,106,46,197,66,9,98,8,26,53,180,235,145,205,100,
-  240,11,45,42,23,30,101,234,248,179,228,122,70,217,120,229,245,20,186,6,88,179,122,7,254,189,127,63,23,193,83,45,
-  184,127,57,204,45,19,66,127,170,220,203,121,225,80,196,126,203,41,152,32,245,87,202,72,73,40,21,179,97,131,147,213,
-  89,222,3,124,0,184,11,248,125,210,72,160,159,109,87,58,191,218,254,222,190,118,107,244,239,128,155,219,85,79,208,6,
-  136,137,239,227,69,219,221,6,186,195,237,235,42,119,9,128,190,69,135,246,60,17,225,82,170,68,0,188,11,248,205,246,
-  215,156,1,115,6,38,98,184,175,79,27,191,242,103,127,55,243,208,157,95,91,165,110,191,165,47,127,245,102,127,100,245,
-  58,58,70,87,146,207,57,148,221,58,94,94,98,93,23,171,12,18,9,70,128,91,96,113,186,78,208,168,50,176,58,79,
-  44,66,166,106,57,166,171,13,62,252,135,127,106,221,163,251,155,43,26,141,122,179,177,104,182,199,102,122,165,225,233,8,
-  190,14,60,222,99,153,88,128,162,6,213,247,204,225,222,163,255,237,79,110,244,254,159,127,87,8,91,53,112,50,248,126,
-  129,102,173,202,196,161,99,236,254,234,67,230,192,35,123,76,77,9,249,147,191,253,107,114,231,101,27,113,43,53,242,229,
-  158,84,200,168,53,134,58,174,48,108,219,184,142,173,255,223,255,155,63,255,171,239,89,243,213,143,125,110,249,103,62,244,
-  145,41,177,239,208,244,179,153,226,240,239,95,127,117,65,64,234,241,99,36,65,173,137,167,60,158,61,127,138,199,78,29,
-  225,218,21,43,73,154,243,52,77,72,87,161,155,235,86,175,229,179,207,238,70,41,31,7,65,16,180,56,53,51,193,198,
-  205,99,228,27,49,218,21,88,145,178,30,70,91,48,134,218,252,34,58,140,233,27,25,166,80,42,209,168,24,154,139,45,
-  16,33,126,54,79,198,207,130,84,104,44,113,28,17,53,234,36,65,19,235,8,156,76,14,161,60,140,21,237,8,238,118,
-  44,135,84,8,44,24,139,116,61,238,185,251,30,158,120,236,9,164,204,32,165,32,155,207,32,20,41,32,73,11,86,227,
-  56,2,63,171,200,100,28,162,40,198,36,186,189,100,107,137,163,152,197,133,5,114,126,22,207,245,48,81,196,252,252,2,
-  185,124,158,90,189,138,239,250,56,174,131,18,1,45,233,226,185,46,130,132,110,223,229,193,187,63,196,23,62,255,127,184,
-  229,77,239,166,35,174,165,51,17,168,10,168,127,26,244,96,255,24,79,245,175,16,70,27,123,252,133,232,75,33,136,132,
-  130,36,194,181,150,3,213,89,94,101,18,46,36,14,127,14,188,17,56,72,234,69,21,183,39,181,223,188,44,189,20,70,
-  240,173,184,160,239,231,201,2,203,219,143,173,126,9,128,94,26,65,119,152,52,217,50,76,117,64,118,11,212,119,192,179,
-  139,80,45,195,254,142,243,147,155,26,127,249,161,13,115,127,197,202,135,123,75,253,193,198,45,101,59,208,159,17,29,174,
-  83,30,232,35,55,188,140,114,247,32,94,28,81,244,5,34,59,192,193,67,167,56,126,244,0,141,146,203,236,252,130,205,
-  29,57,95,95,57,63,55,181,78,71,147,131,105,16,197,148,132,170,77,237,82,15,145,146,196,179,247,130,190,28,130,34,
-  60,174,160,167,250,55,31,41,63,45,212,21,93,111,185,53,27,5,33,167,31,123,218,94,248,250,61,65,241,240,201,249,
-  177,122,60,190,25,154,53,232,120,242,151,127,125,153,255,191,254,123,199,166,87,94,43,164,93,32,147,247,16,202,69,10,
-  23,99,85,59,23,62,97,120,176,151,127,241,43,191,232,206,252,204,59,71,190,242,229,123,71,234,95,249,58,179,211,19,
-  212,23,150,81,236,44,145,4,9,245,70,3,148,131,80,138,47,237,126,130,109,163,99,100,29,159,36,105,161,116,204,173,
-  219,118,240,236,133,115,140,87,234,40,41,8,195,144,233,249,25,172,35,200,40,73,160,4,137,146,88,29,227,41,7,172,
-  192,2,173,90,157,233,11,227,244,13,15,81,236,236,38,172,212,168,86,22,152,175,84,210,197,87,44,137,209,32,5,210,
-  88,60,199,161,88,236,38,155,43,160,73,3,28,219,126,176,96,117,250,103,210,120,25,41,21,79,60,254,20,81,148,224,
-  184,25,132,84,72,7,144,22,215,19,64,130,78,82,71,183,32,108,17,197,17,142,147,73,237,65,180,190,104,186,6,16,
-  132,1,58,73,200,21,10,212,27,117,102,103,102,25,28,24,68,72,65,38,147,65,74,73,20,4,216,36,0,82,23,131,
-  171,175,216,204,71,62,243,73,123,240,247,126,253,194,45,94,166,208,137,136,19,172,206,183,51,198,102,162,144,215,75,41,
-  173,49,198,55,198,10,251,141,224,51,23,54,217,216,92,224,125,115,147,252,86,28,114,167,73,216,108,45,3,207,123,159,
-  186,207,187,145,62,159,19,250,65,57,75,2,94,143,139,41,54,151,0,232,197,32,247,147,237,143,165,243,46,176,59,161,
-  218,7,135,154,112,182,9,207,248,176,108,196,176,98,217,84,117,5,83,15,143,54,160,187,9,158,150,66,170,82,209,9,
-  189,140,87,211,113,113,206,147,185,8,71,121,113,28,220,22,132,11,34,136,26,142,53,81,65,155,105,7,14,39,112,50,
-  134,243,110,90,25,55,27,208,234,128,186,128,214,211,96,63,6,60,0,250,55,97,60,17,226,254,162,181,170,241,215,127,
-  59,113,242,99,31,31,115,132,16,157,141,86,109,5,204,122,112,74,167,9,203,181,28,140,244,93,152,190,242,200,207,191,
-  231,242,125,111,123,195,178,235,126,226,93,106,205,154,49,92,39,64,100,36,42,159,65,74,175,221,96,42,172,209,244,229,
-  242,252,196,219,222,193,59,223,240,22,158,121,244,81,190,252,185,59,89,190,97,37,171,214,108,32,182,233,174,87,94,249,
-  140,207,47,178,255,220,5,174,26,25,193,177,9,149,70,147,114,161,196,91,174,185,129,191,248,234,151,48,194,144,203,120,
-  140,12,244,19,155,4,199,145,100,28,149,242,52,142,147,2,134,49,36,73,130,227,186,52,235,117,198,207,158,161,119,112,
-  132,76,103,39,29,74,48,55,53,65,18,181,82,133,183,214,88,11,137,177,168,92,14,199,203,130,112,48,86,98,72,23,
-  139,133,181,169,159,52,169,1,155,148,233,37,153,36,73,10,8,72,252,76,150,66,190,72,83,202,118,76,147,32,142,45,
-  81,108,72,76,186,221,239,186,46,73,148,18,239,82,74,146,36,193,247,125,76,146,198,122,87,106,21,138,249,34,139,11,
-  139,20,139,69,242,185,2,90,27,28,199,197,201,8,130,184,129,235,150,105,180,52,190,245,184,124,199,21,162,246,232,3,
-  185,245,173,74,171,19,188,6,244,199,208,251,223,32,122,255,252,68,156,205,20,116,165,123,72,202,48,178,66,63,55,188,
-  86,82,49,93,91,224,134,233,227,68,223,52,85,250,167,120,17,119,0,215,180,129,200,254,19,251,63,56,63,8,15,66,
-  183,251,218,61,16,91,88,92,9,213,6,156,179,176,55,130,14,13,189,121,40,21,193,193,88,97,23,171,66,67,78,66,
-  183,133,46,1,142,72,189,202,38,85,106,92,21,3,139,81,58,18,159,143,160,53,15,73,23,152,22,169,195,98,134,116,
-  195,127,233,109,57,11,81,100,237,241,30,168,249,176,119,172,25,244,164,77,7,45,155,38,131,76,59,48,23,65,104,161,
-  75,194,241,13,245,214,153,217,191,254,232,149,15,126,229,222,245,15,223,126,71,247,142,183,222,33,215,109,26,65,84,18,
-  28,39,75,54,87,38,147,205,164,182,111,66,160,77,21,28,151,93,215,95,199,182,203,183,112,230,252,25,246,29,220,71,
-  161,51,143,209,17,113,93,35,4,60,116,228,0,155,187,251,192,64,236,74,162,86,139,109,67,163,236,88,177,146,39,78,
-  30,98,229,216,8,91,214,175,79,39,227,158,32,231,102,144,198,208,50,26,29,155,116,148,174,210,216,29,132,32,168,55,
-  24,191,112,158,254,225,81,114,165,18,157,194,80,153,153,78,101,12,142,3,198,16,145,128,235,226,120,89,180,85,169,143,
-  180,48,169,254,199,74,108,234,68,157,166,112,96,0,69,127,255,0,74,165,127,151,36,22,99,12,142,114,240,51,30,142,
-  82,40,149,69,57,62,249,66,23,202,113,241,51,25,146,40,38,12,195,52,231,204,113,112,92,23,225,56,132,129,197,74,
-  73,179,213,36,155,201,50,63,63,79,33,95,164,179,195,35,73,18,188,140,143,227,103,8,2,131,163,4,73,43,98,197,
-  224,50,38,95,125,91,215,251,238,127,40,249,209,249,133,100,23,220,146,128,73,224,193,159,131,211,155,38,79,4,159,136,
-  154,230,129,242,16,195,78,6,169,35,148,77,249,65,7,251,79,18,108,190,213,185,177,253,235,103,191,203,106,104,105,216,
-  249,66,64,102,127,88,1,104,105,130,22,181,127,63,0,230,35,208,250,21,104,77,164,137,170,103,22,83,253,172,176,109,
-  240,112,193,77,192,183,144,117,64,68,16,89,104,212,32,30,2,83,77,61,124,226,205,96,159,4,142,181,53,20,29,47,
-  80,70,183,109,103,109,111,90,29,157,109,193,164,3,174,74,57,0,109,32,9,32,89,3,166,15,56,11,141,22,84,18,
-  56,219,3,7,123,199,39,47,155,248,171,191,222,246,196,103,63,181,102,239,107,95,213,179,253,45,175,87,171,55,172,32,
-  9,38,168,26,131,159,41,146,237,200,33,10,185,118,230,87,140,231,43,214,174,91,199,202,213,154,237,235,54,242,166,55,
-  190,158,143,124,232,99,60,249,212,51,236,62,190,159,67,43,86,177,105,108,25,113,220,34,1,188,48,224,214,157,59,57,
-  120,234,16,171,215,172,96,160,167,11,97,3,80,18,225,56,120,198,160,181,67,100,108,58,209,146,138,36,138,145,164,173,
-  85,24,71,204,205,78,147,205,46,35,83,44,211,227,231,169,204,205,210,172,86,17,90,227,229,114,148,251,250,17,78,134,
-  37,27,75,113,241,109,151,86,57,75,138,104,107,210,102,172,88,204,97,109,130,209,18,207,243,200,102,51,52,37,40,9,
-  81,20,34,133,139,69,80,234,236,70,58,138,56,138,200,102,179,196,113,140,214,26,165,84,90,169,73,121,145,23,66,164,
-  45,89,28,197,76,76,142,35,133,160,84,42,209,106,74,6,6,71,72,10,1,34,8,136,234,17,141,36,98,120,120,136,
-  210,27,223,236,220,249,236,177,209,35,7,158,44,190,182,217,236,44,65,201,194,125,231,172,57,54,54,119,161,22,55,107,
-  118,117,190,155,185,234,36,65,18,179,81,8,250,237,15,103,202,196,171,219,215,199,233,151,43,237,187,245,38,138,115,243,
-  204,61,179,7,233,121,208,174,16,83,45,149,133,40,38,250,30,78,224,156,31,196,39,209,182,201,106,128,65,176,14,68,
-  127,0,252,113,187,133,115,129,63,7,70,160,50,3,236,39,141,232,185,162,253,241,31,219,147,1,159,139,33,128,223,192,
-  65,125,43,194,220,1,150,129,249,91,8,126,25,2,213,254,89,91,128,25,210,165,198,101,109,128,44,195,226,126,216,31,
-  195,249,24,158,29,130,77,195,115,149,157,83,127,251,137,29,207,124,254,171,107,142,190,225,182,158,245,111,125,181,187,114,
-  205,90,146,102,196,66,117,1,145,115,201,117,116,145,45,150,144,202,193,152,8,33,160,163,167,200,237,111,126,45,55,189,
-  238,102,14,238,63,194,103,62,246,247,156,219,189,159,245,118,8,33,12,137,128,40,106,177,186,187,135,203,86,172,98,221,
-  230,245,120,133,44,178,25,33,148,2,165,80,66,224,42,23,235,88,172,11,113,172,193,145,8,157,102,139,121,74,224,9,
-  145,146,192,158,143,113,93,202,67,163,100,59,155,136,56,193,201,122,8,199,35,209,2,229,40,140,142,145,194,166,244,15,
-  50,245,38,146,22,97,204,197,59,97,162,99,44,38,181,246,48,49,25,79,160,51,18,209,212,40,149,250,25,107,12,185,
-  114,9,39,227,161,16,184,174,139,159,205,18,180,90,237,133,216,24,199,203,224,56,110,186,110,34,36,81,16,81,169,86,
-  208,70,51,206,56,153,140,143,239,101,9,234,77,108,62,38,215,161,40,117,15,147,13,13,141,100,134,142,216,208,187,235,
-  26,198,215,45,239,248,204,163,143,94,251,234,19,71,11,253,208,23,192,215,250,97,207,191,105,85,231,163,86,213,24,224,
-  66,251,117,206,192,15,85,5,180,116,252,246,175,43,218,215,5,75,96,241,29,226,33,230,129,207,190,230,86,110,253,139,
-  247,49,98,52,221,83,147,56,174,228,136,84,56,24,6,50,62,61,129,166,231,175,62,72,183,16,20,30,120,152,137,103,
-  246,124,67,48,196,203,105,0,157,127,10,79,234,82,117,212,106,127,44,69,52,47,137,190,158,4,150,50,238,159,31,106,
-  247,114,9,195,214,243,166,27,75,158,212,223,172,164,205,167,178,130,56,134,153,69,88,172,192,25,23,246,245,192,83,125,
-  11,149,203,170,31,248,228,246,103,63,245,149,181,123,126,228,166,190,45,239,120,131,179,105,229,106,104,105,42,141,73,234,
-  153,25,242,133,34,133,114,23,210,247,1,131,214,45,148,35,216,190,125,19,59,118,94,193,189,95,254,60,207,254,221,157,
-  44,235,236,69,39,9,6,77,220,108,240,218,107,175,167,123,243,102,18,29,227,96,113,164,72,163,119,132,197,113,21,82,
-  186,237,40,30,131,20,14,38,1,109,45,153,124,129,82,87,55,202,203,96,68,59,198,71,72,220,108,17,149,181,152,196,
-  64,34,80,210,1,13,66,56,128,193,202,84,246,96,76,123,13,67,210,142,39,2,180,69,161,210,93,181,56,162,90,173,
-  224,245,141,162,45,8,71,161,172,73,205,253,51,89,92,225,145,241,4,198,164,36,112,54,227,35,164,160,22,133,88,171,
-  83,0,138,116,218,24,73,136,147,152,102,171,73,146,36,184,174,199,208,192,32,218,196,100,101,142,108,174,68,100,13,89,
-  223,163,51,59,12,197,132,70,165,197,138,158,126,146,183,190,205,127,108,207,158,203,183,220,125,87,231,170,36,234,76,160,
-  83,194,83,29,48,62,10,225,89,248,161,143,59,94,58,183,181,193,231,2,16,89,251,130,23,251,125,82,242,176,82,124,
-  162,148,101,199,96,31,119,36,9,228,124,252,229,203,16,210,50,166,20,214,106,132,227,177,44,219,193,178,63,122,47,18,
-  139,174,207,209,251,145,143,193,227,79,131,235,96,58,74,72,235,178,99,235,14,68,154,197,242,195,3,64,223,170,109,19,
-  63,0,143,193,73,249,171,216,135,217,10,84,26,112,46,3,251,243,240,244,182,106,237,138,234,135,62,119,213,163,247,62,
-  177,254,203,183,221,80,188,254,213,55,137,213,43,135,41,169,12,141,185,57,26,243,21,178,29,93,100,59,75,72,191,136,
-  72,210,84,83,237,86,185,252,134,87,240,236,87,31,198,54,83,99,144,136,4,17,135,20,115,5,162,201,121,154,11,117,
-  148,167,41,229,178,184,74,97,132,64,184,18,29,130,39,37,74,164,36,178,245,124,188,108,150,76,190,3,161,92,172,0,
-  217,22,52,98,116,170,243,65,180,45,72,12,2,157,42,193,5,88,155,90,146,24,97,48,50,13,128,182,109,112,19,104,
-  50,25,15,129,197,65,129,181,212,155,17,101,45,209,56,88,233,226,181,127,180,151,201,82,200,228,136,117,3,207,241,176,
-  94,134,168,221,134,229,50,62,137,142,241,114,57,18,99,8,195,16,107,45,9,9,142,118,112,179,46,51,115,211,56,174,
-  162,175,175,15,89,149,68,42,71,190,3,176,17,210,228,32,235,210,211,81,38,104,54,169,87,155,100,174,187,81,29,237,
-  237,91,179,248,249,191,207,111,173,84,251,124,24,136,224,129,81,56,57,2,141,14,48,179,255,12,128,40,211,254,117,117,
-  179,69,156,36,212,129,74,251,243,79,9,201,111,43,197,61,158,67,36,21,125,82,226,39,58,29,98,104,131,49,49,82,
-  66,164,12,214,106,60,35,48,42,4,66,116,146,96,149,160,244,51,255,130,235,127,238,23,136,177,36,214,66,163,194,143,
-  190,253,29,92,121,197,46,126,235,143,222,135,251,183,31,33,8,130,111,107,56,232,112,233,124,79,42,180,98,90,21,197,
-  13,152,173,67,197,131,115,49,28,47,66,227,138,241,137,210,39,246,61,187,218,121,251,155,157,125,199,78,225,74,24,234,
-  235,101,100,104,144,122,101,145,74,109,145,66,161,76,161,80,70,102,60,84,226,82,204,22,216,120,227,149,156,251,194,61,
-  116,228,92,76,168,49,86,18,88,141,188,48,203,252,133,41,250,215,14,97,17,184,25,15,35,210,85,136,200,137,16,82,
-  224,23,20,210,113,17,174,151,110,246,91,121,209,58,35,69,151,54,202,144,122,69,199,34,37,129,211,225,187,70,152,246,
-  215,90,213,94,64,77,37,123,50,141,64,73,199,191,89,47,101,229,148,33,140,99,180,38,21,132,90,157,2,149,149,96,
-  28,130,40,68,155,164,29,145,108,200,23,10,56,97,72,181,90,197,245,50,72,157,78,200,60,99,169,215,235,88,107,113,
-  93,151,32,10,48,85,131,159,241,89,88,88,160,84,42,145,113,61,230,231,231,112,28,69,169,92,38,136,67,92,225,18,
-  134,17,126,33,79,38,87,32,12,66,202,155,54,139,153,129,158,225,123,62,253,247,165,93,39,78,246,116,194,64,19,30,
-  184,10,246,1,51,42,29,120,252,80,182,97,255,144,211,176,108,8,163,116,170,9,156,22,146,91,51,62,139,58,161,0,
-  20,172,109,79,2,237,139,191,243,106,131,105,214,254,1,23,100,162,128,213,171,86,242,193,247,255,41,51,191,244,139,204,
-  252,241,251,89,248,224,71,49,173,214,11,254,116,121,9,62,190,55,156,149,38,85,10,23,149,100,205,166,149,113,73,136,
-  57,13,39,45,156,93,180,4,87,190,242,122,110,121,213,237,220,241,250,55,179,126,199,229,156,188,48,197,157,95,185,155,
-  19,39,207,32,173,67,115,113,158,11,103,142,48,57,113,154,160,50,139,9,106,92,126,227,117,168,190,46,90,145,193,117,
-  50,104,12,218,104,220,70,200,153,61,7,73,34,77,173,25,208,10,99,164,116,177,66,225,248,57,220,124,17,167,88,66,
-  100,243,224,100,48,109,127,35,33,213,18,149,136,21,162,93,253,144,122,69,27,129,163,21,50,17,105,116,128,85,96,28,
-  172,149,169,49,91,170,45,111,127,127,250,125,133,114,39,94,214,71,170,52,220,208,117,60,16,26,217,38,128,116,148,160,
-  112,81,174,194,113,20,74,41,92,215,37,12,3,132,16,248,190,79,171,149,110,80,105,109,240,50,25,124,223,191,248,152,
-  16,208,10,90,196,73,76,162,19,78,159,60,197,252,252,60,97,24,50,55,55,71,189,94,187,168,101,74,172,166,21,70,
-  169,239,134,114,80,40,58,186,250,112,223,245,19,197,7,111,190,113,215,241,76,230,45,46,252,152,129,87,39,176,106,13,
-  100,71,254,9,143,222,95,238,141,114,137,182,72,218,124,145,252,62,92,11,81,24,98,26,139,244,174,89,197,150,247,255,
-  111,86,61,121,63,215,255,236,187,209,126,134,228,155,8,236,75,0,244,61,122,97,11,215,110,165,244,23,191,65,199,215,
-  223,199,201,95,251,9,62,150,246,220,158,128,210,98,185,84,216,250,186,219,157,184,85,35,142,99,70,71,151,243,198,183,
-  189,157,91,94,243,90,38,166,23,248,179,63,255,32,71,14,159,38,235,23,48,113,200,212,212,89,230,166,206,209,89,42,
-  50,118,249,118,154,77,141,171,124,12,224,68,26,7,48,243,77,206,236,63,65,189,209,98,110,122,134,185,137,105,130,70,
-  11,199,91,90,56,109,3,133,77,39,88,130,52,27,222,144,242,48,73,219,15,218,146,170,168,149,213,56,58,74,53,63,
-  8,18,36,186,237,196,8,233,254,151,112,74,224,116,32,156,34,80,64,73,31,109,210,189,48,99,192,26,141,181,6,163,
-  65,73,23,136,48,218,146,201,102,113,92,15,209,254,58,99,82,49,98,46,151,67,169,148,173,75,18,141,53,134,142,142,
-  14,178,217,180,104,183,198,162,28,69,43,108,17,4,45,140,78,56,119,246,44,73,146,10,25,167,166,166,9,131,16,129,
-  68,73,7,137,32,10,67,28,169,80,66,225,58,25,58,243,157,12,189,246,77,238,161,159,250,233,181,247,12,15,189,166,
-  9,239,116,224,13,30,108,207,64,231,111,129,204,241,195,181,224,249,98,222,175,223,111,225,162,5,116,16,18,53,42,244,
-  175,91,205,235,254,252,253,44,127,226,126,214,252,234,191,161,176,115,219,69,224,185,212,130,125,23,199,180,73,234,210,255,
-  243,47,136,223,118,43,145,54,184,74,17,158,28,103,52,213,16,117,88,24,74,6,122,74,67,107,87,96,226,38,160,136,
-  162,22,17,134,193,193,1,182,110,187,156,255,241,187,239,229,243,159,189,155,155,110,122,5,175,187,227,70,214,108,94,65,
-  216,106,82,57,119,150,45,187,182,49,241,240,51,152,200,226,40,133,147,24,34,215,144,23,25,78,61,117,16,191,183,3,
-  183,84,32,50,1,97,16,208,170,53,200,117,116,224,186,30,202,115,145,34,165,211,141,16,8,156,116,149,130,148,7,18,
-  194,164,122,31,218,58,37,82,165,143,112,188,20,112,144,128,70,7,13,230,102,43,60,187,247,49,142,28,57,194,209,19,
-  199,169,84,106,28,61,122,146,102,43,193,207,56,56,42,105,147,214,138,32,0,147,104,92,39,65,8,75,161,80,166,88,
-  238,32,8,26,120,153,12,82,73,226,40,194,113,20,229,114,153,106,181,138,209,134,140,239,224,121,30,97,54,36,73,146,
-  139,130,71,173,53,213,106,21,155,203,33,149,195,233,51,167,88,189,106,53,126,214,103,97,110,17,37,93,84,94,161,28,
-  7,19,39,32,13,158,227,166,109,32,22,153,24,134,215,110,161,241,158,213,93,247,126,225,179,215,238,120,248,161,238,101,
-  214,14,68,112,207,182,212,76,111,250,42,184,100,75,255,253,186,78,130,144,136,144,242,250,53,116,254,254,239,99,103,39,
-  248,179,233,169,75,0,244,114,80,253,249,228,119,63,233,86,242,248,31,124,20,241,135,31,7,173,113,114,25,114,198,178,
-  28,60,3,253,17,12,251,91,54,229,187,123,186,48,205,22,105,60,160,193,145,134,218,194,34,73,28,80,44,148,56,120,
-  232,60,31,254,232,151,248,252,231,190,196,101,215,109,227,71,223,248,35,92,181,115,39,133,129,46,198,46,95,207,249,199,
-  14,162,146,20,40,194,56,198,119,125,50,145,160,118,118,150,161,237,61,196,113,11,172,165,58,59,75,109,126,17,229,58,
-  160,210,145,186,163,36,142,151,69,121,62,158,231,225,185,46,74,201,148,10,34,181,221,176,34,109,205,194,102,131,218,204,
-  2,23,206,79,50,49,62,207,131,143,238,225,200,177,115,140,79,157,231,212,233,99,52,90,1,202,73,223,54,89,63,79,
-  46,87,194,81,146,40,10,8,227,38,89,225,130,200,131,53,56,78,68,146,52,241,157,44,158,235,163,164,68,88,112,149,
-  11,14,184,142,139,231,198,116,118,116,210,106,181,112,164,67,208,12,40,20,10,180,90,45,146,36,65,41,133,227,56,68,
-  97,72,144,196,20,50,30,245,122,141,169,169,41,186,186,123,8,90,49,70,27,250,7,7,241,179,89,50,94,134,56,12,
-  193,88,50,153,12,74,57,36,38,70,9,129,151,47,211,241,174,127,225,31,94,187,97,235,236,39,63,214,185,165,86,237,
-  143,161,95,192,67,255,17,206,84,161,53,115,233,109,254,253,3,162,48,194,132,115,80,200,210,95,94,117,9,128,94,44,
-  223,182,84,170,6,221,69,196,181,27,80,218,48,215,126,242,150,3,178,61,61,144,142,195,212,147,199,152,157,175,16,64,
-  193,194,138,166,80,203,54,189,243,237,57,229,56,36,66,97,173,69,160,81,54,66,135,117,226,86,29,19,69,224,68,72,
-  33,169,198,134,175,220,245,8,143,221,187,155,107,175,187,154,55,191,237,181,108,217,188,138,153,195,231,17,115,53,66,98,
-  76,148,208,116,35,138,110,142,233,125,39,176,89,65,71,119,145,156,155,33,227,103,81,66,96,226,136,56,136,16,114,73,
-  152,232,34,165,139,227,56,184,109,0,114,92,23,99,12,65,24,50,55,49,201,204,169,227,212,166,78,227,235,136,142,174,
-  46,170,147,1,31,252,139,191,162,34,11,72,71,225,59,138,124,161,204,115,121,209,32,132,38,142,66,130,32,100,97,113,
-  154,14,20,88,31,33,13,158,151,106,132,48,144,201,228,176,218,16,133,17,174,235,144,245,211,132,184,180,210,18,248,126,
-  22,185,180,162,17,37,20,75,69,106,213,218,197,10,72,72,73,20,199,52,91,45,178,94,150,233,233,105,124,207,167,80,
-  44,209,108,212,153,153,158,97,96,100,20,33,52,94,198,67,34,72,116,132,148,160,156,20,132,132,176,136,80,179,252,154,
-  235,229,252,216,138,177,199,255,238,3,165,45,199,143,246,20,161,59,134,251,11,112,120,13,84,138,96,47,5,55,127,31,
-  79,162,73,18,141,231,94,2,160,23,77,48,119,237,92,5,63,125,11,167,119,172,34,142,147,23,236,159,109,62,71,249,
-  61,127,65,117,166,34,61,232,49,176,198,172,95,55,180,252,218,43,68,208,172,3,42,157,120,91,129,213,22,97,4,7,
-  14,28,228,252,249,115,56,174,0,155,96,145,248,210,39,136,52,95,186,231,94,238,185,255,126,110,186,237,149,12,121,89,
-  174,232,30,196,183,224,8,65,172,35,60,153,26,238,139,4,142,61,245,8,61,158,143,182,2,63,159,167,216,221,135,223,
-  145,238,118,57,202,65,154,42,96,48,66,210,136,19,90,173,58,173,234,34,73,216,162,90,93,32,151,132,12,122,134,53,
-  195,14,121,175,139,76,161,72,166,152,167,171,203,39,138,29,132,80,8,173,211,165,84,155,198,246,8,33,104,181,90,100,
-  188,12,66,89,154,149,25,60,4,161,1,33,124,242,66,96,100,3,39,155,101,100,120,37,231,78,61,75,43,104,144,203,
-  117,147,196,26,207,115,201,248,89,130,86,61,53,63,75,12,89,223,167,218,38,151,93,215,33,142,19,176,233,106,137,144,
-  146,48,12,241,253,44,26,195,249,137,243,140,58,203,144,74,176,88,153,71,184,146,238,238,30,100,46,135,146,50,173,180,
-  68,186,80,155,113,61,12,26,35,32,105,54,233,30,24,70,255,187,95,239,220,247,137,15,95,179,242,235,247,116,13,192,
-  64,12,95,237,135,167,223,7,115,185,84,164,112,233,124,159,207,37,0,250,78,0,84,206,177,242,55,223,78,97,199,42,
-  28,11,241,124,125,105,10,253,15,201,61,33,57,143,37,1,47,3,195,13,88,237,92,113,69,103,174,171,19,130,26,200,
-  182,157,131,117,8,91,134,56,178,8,225,18,90,139,176,94,234,179,108,19,12,49,214,1,215,129,88,107,190,252,149,251,
-  209,38,130,155,111,229,142,181,27,104,132,53,116,20,17,37,13,172,7,11,103,166,216,212,157,165,91,47,18,197,134,160,
-  57,195,98,237,60,21,105,137,98,1,26,242,158,70,235,24,132,196,24,131,82,208,145,243,232,202,185,100,59,193,179,46,
-  33,9,129,114,208,78,30,145,43,19,213,27,36,42,139,138,45,74,90,180,1,99,82,0,18,237,134,84,107,67,172,45,
-  174,155,65,68,33,34,73,136,84,145,122,224,80,84,6,109,2,2,58,200,229,251,200,250,62,142,227,226,58,146,172,159,
-  67,39,233,184,189,213,170,147,203,103,169,87,91,180,146,136,124,190,192,212,204,20,65,216,74,205,207,172,33,227,186,196,
-  90,163,141,165,25,180,200,250,62,137,73,24,31,63,207,200,208,8,249,98,129,218,226,44,185,172,143,82,233,22,189,178,
-  110,10,100,66,181,157,52,221,116,191,77,9,162,48,0,41,232,121,231,187,114,199,135,134,183,206,125,226,227,229,245,81,
-  152,159,7,103,5,60,81,134,185,16,180,250,103,52,37,187,4,64,63,128,199,91,49,64,199,174,245,80,105,164,201,16,
-  124,139,119,164,35,241,166,23,217,120,126,134,253,144,179,48,90,135,145,149,175,185,53,231,180,219,47,41,82,247,63,105,
-  53,166,109,73,225,251,62,153,76,134,40,1,41,69,106,229,209,206,113,183,128,163,20,56,18,18,143,71,158,221,203,174,
-  177,21,120,128,107,72,205,200,200,112,238,248,105,114,45,232,89,110,41,120,154,66,162,233,83,146,4,151,216,180,72,226,
-  4,69,38,37,161,29,133,227,121,233,191,101,12,88,13,137,37,116,125,76,174,3,223,203,144,241,114,248,249,2,181,230,
-  34,97,164,177,194,71,155,182,72,13,153,122,253,8,145,114,58,194,162,117,128,235,100,105,181,2,162,56,192,136,50,21,
-  221,192,247,52,106,209,18,75,129,240,179,32,60,194,168,65,54,107,113,148,68,39,169,124,192,81,30,113,148,144,205,101,
-  169,84,43,184,142,147,90,223,11,73,28,199,72,41,105,180,90,169,37,145,76,249,32,107,12,25,207,35,20,33,179,243,
-  179,105,101,41,139,84,22,22,112,29,7,87,185,36,50,78,167,45,142,69,74,217,110,69,83,98,91,202,116,89,55,170,
-  69,244,188,226,6,103,118,112,112,213,254,15,126,192,221,50,53,237,89,40,238,134,39,94,7,231,207,64,216,184,116,25,
-  92,2,160,127,12,242,199,2,255,225,240,57,242,111,255,159,237,139,239,219,16,108,2,8,18,78,44,212,132,73,55,244,
-  87,200,177,177,190,161,93,59,132,14,67,116,98,176,194,34,164,66,0,113,156,198,221,44,44,204,147,196,26,219,94,198,
-  20,136,180,221,17,6,172,192,24,11,66,227,8,135,179,115,21,158,188,112,134,155,150,175,34,14,170,24,9,42,148,12,
-  100,75,28,57,126,150,229,43,150,83,246,154,248,82,35,66,11,73,130,114,219,91,238,90,161,68,146,122,60,199,33,56,
-  14,82,57,168,92,1,215,203,64,182,76,156,205,164,73,176,6,66,147,86,79,218,90,148,112,144,82,161,173,69,24,157,
-  174,122,0,190,239,183,219,81,75,172,35,170,139,85,220,36,194,201,100,72,156,14,90,97,3,234,211,148,186,7,137,202,
-  125,8,225,97,76,189,29,131,220,194,115,51,24,99,145,56,180,90,1,253,3,93,56,142,75,165,86,193,115,93,162,56,
-  68,170,244,249,114,93,151,36,73,136,227,228,226,90,147,163,20,158,239,81,169,84,112,84,202,109,37,218,32,133,32,231,
-  231,8,140,65,250,105,84,184,54,26,199,49,40,153,142,253,149,82,24,107,80,66,160,91,1,229,101,43,136,126,254,95,
-  143,238,251,240,7,95,189,230,196,241,238,28,244,41,184,127,59,156,116,160,230,94,42,132,46,1,208,247,227,44,245,249,
-  9,233,242,36,22,116,71,158,241,31,185,138,133,235,55,227,198,9,147,201,119,96,3,60,23,142,156,103,230,247,63,67,
-  0,153,44,140,6,176,62,185,106,87,111,247,72,31,81,216,192,81,109,245,133,0,163,53,73,28,33,133,192,243,188,246,
-  184,184,13,64,162,173,57,54,242,121,44,84,170,157,49,74,114,215,222,103,184,106,120,57,217,172,143,209,9,194,104,178,
-  34,131,34,203,185,169,144,174,117,62,166,217,66,198,2,233,132,184,133,44,20,58,49,38,3,218,34,173,70,74,129,227,
-  185,72,223,71,171,12,137,148,36,194,1,225,96,69,130,32,6,41,169,214,154,52,234,77,92,223,193,42,7,129,76,221,
-  17,17,8,37,72,18,147,106,114,172,70,11,77,117,161,134,215,168,113,228,232,126,190,254,236,125,76,157,59,77,148,127,
-  146,155,239,248,89,250,139,5,138,197,50,177,142,136,162,0,172,37,151,203,131,149,228,243,121,130,32,32,10,67,186,58,
-  59,152,157,157,65,199,49,38,142,113,189,12,113,18,183,129,39,125,129,172,16,88,1,65,20,81,173,215,112,149,199,204,
-  236,44,66,64,185,212,193,92,98,112,28,143,193,193,65,162,56,253,191,248,89,15,129,65,72,69,18,235,139,45,115,90,
-  57,41,140,209,200,254,97,17,254,155,95,233,127,236,99,31,190,113,211,99,143,116,15,64,95,8,247,70,176,231,231,97,
-  190,4,102,242,18,102,92,2,160,239,25,25,15,108,219,181,142,205,221,69,186,194,152,226,80,23,211,55,108,225,102,207,
-  101,98,160,19,25,166,214,3,223,241,248,30,162,210,196,79,127,102,65,192,170,10,172,236,186,225,218,172,227,8,116,144,
-  182,46,44,45,142,26,67,18,133,56,40,58,203,29,233,218,65,156,70,1,89,107,177,86,60,103,79,1,233,132,77,10,
-  140,167,56,51,53,205,67,135,14,242,234,203,47,35,172,46,160,140,129,48,98,89,103,63,11,103,23,176,43,251,16,109,
-  19,121,169,66,12,46,153,108,23,38,155,71,27,131,176,26,129,193,96,137,218,160,107,149,192,90,147,250,157,144,238,132,
-  105,35,105,133,49,219,119,172,231,192,193,99,180,106,6,63,151,79,125,161,69,42,102,140,163,4,221,230,86,98,27,179,
-  103,239,93,76,157,58,196,98,165,130,146,154,108,214,165,57,255,56,159,252,192,89,150,13,172,38,138,82,71,71,219,222,
-  130,15,154,1,126,54,143,16,130,124,46,155,146,197,94,134,124,46,143,49,154,140,231,81,15,154,44,9,31,4,224,184,
-  46,218,104,162,56,198,247,50,52,27,77,188,76,170,251,153,155,155,195,113,92,148,227,50,55,59,75,119,119,55,89,149,
-  218,128,184,142,76,247,221,108,2,66,166,147,53,241,60,208,23,2,27,71,184,142,67,199,187,127,42,255,100,111,239,229,
-  43,239,252,92,215,6,75,111,29,242,235,224,201,16,102,22,190,117,200,234,165,115,9,128,94,26,248,12,116,21,248,87,
-  255,225,141,172,29,234,166,22,167,187,74,54,78,24,176,22,147,68,47,108,248,251,2,39,171,44,247,43,203,33,16,111,
-  132,206,22,172,136,203,229,222,101,87,110,151,54,142,192,36,8,153,102,179,99,83,151,108,107,12,70,8,252,76,106,69,
-  33,180,65,182,243,183,82,140,50,207,245,130,128,181,26,109,64,185,89,30,56,122,152,171,182,110,199,117,92,116,28,99,
-  147,24,215,117,209,161,195,190,3,211,236,220,186,130,36,94,76,147,37,52,196,205,4,229,45,201,12,219,252,146,53,41,
-  207,34,117,234,120,212,254,207,10,36,72,197,108,181,198,186,45,59,248,236,47,254,42,15,125,253,1,62,243,233,47,240,
-  200,227,79,48,61,61,155,174,116,136,244,255,35,69,186,160,42,5,196,225,44,211,179,139,248,170,140,159,45,147,201,58,
-  100,85,1,226,26,141,234,73,10,165,30,226,8,48,26,107,192,38,134,70,189,134,21,6,173,99,114,126,17,41,4,171,
-  86,172,224,232,177,163,132,73,8,22,60,207,5,225,98,77,250,28,104,163,145,66,16,132,1,25,207,35,142,99,178,126,
-  26,63,61,61,61,157,182,139,90,115,226,216,49,70,199,198,200,102,179,40,41,200,249,153,246,243,107,47,62,183,66,164,
-  196,186,84,2,48,8,99,241,129,177,215,254,136,123,168,92,88,155,124,226,19,222,246,40,206,135,105,248,228,195,155,224,
-  204,187,33,248,219,75,216,113,9,128,190,11,122,135,0,216,91,206,241,200,131,251,137,155,209,119,178,75,249,246,252,143,
-  171,40,158,159,227,149,224,52,83,151,198,97,239,138,171,59,70,214,175,33,138,67,162,168,69,198,207,97,172,139,144,2,
-  99,211,181,3,229,58,184,174,131,148,18,41,68,106,99,33,190,177,245,122,110,196,102,112,141,69,187,25,206,87,170,60,
-  121,248,48,183,108,221,66,171,85,1,13,73,20,147,203,229,56,121,230,12,189,3,29,12,150,2,164,78,64,91,76,80,
-  195,77,124,28,79,97,52,88,218,91,241,8,208,54,213,199,72,129,65,128,112,81,46,44,212,22,232,90,182,21,63,231,
-  115,251,235,110,231,213,175,186,133,147,103,79,243,133,59,191,194,103,63,115,39,199,143,159,161,217,76,87,63,148,235,34,
-  99,133,75,142,76,33,143,167,92,60,199,193,87,25,28,89,194,45,130,227,8,114,25,129,117,115,52,72,39,128,74,57,
-  100,125,15,35,98,36,26,109,210,231,37,155,203,210,221,213,197,133,201,113,148,146,132,73,76,20,133,40,169,144,202,185,
-  8,32,66,136,180,146,65,18,4,1,74,130,14,2,38,38,38,88,54,186,140,36,78,24,191,112,129,174,238,238,148,195,
-  91,106,251,218,207,174,49,9,82,181,95,15,13,74,9,176,2,97,65,132,17,91,110,184,77,44,244,12,44,123,252,175,
-  254,50,187,177,86,237,85,80,94,132,175,254,71,56,161,33,108,94,154,144,93,2,160,151,123,44,176,239,212,52,242,143,
-  238,252,174,109,25,52,80,78,171,170,172,132,145,89,88,230,222,112,77,33,155,245,9,170,77,116,16,33,179,37,44,2,
-  71,74,130,56,73,167,96,206,243,114,167,236,18,215,163,17,66,126,67,11,38,132,196,8,77,198,74,180,21,68,158,195,
-  151,119,63,197,229,171,86,83,114,50,52,117,147,172,85,96,4,29,165,97,246,236,57,67,247,53,35,228,141,64,146,16,
-  55,154,104,55,192,233,44,145,40,23,35,36,82,39,8,97,144,10,72,156,212,149,82,25,12,160,173,75,66,134,124,169,
-  64,28,199,24,18,164,53,172,94,179,138,95,249,15,255,150,119,255,228,187,120,248,225,71,249,216,71,63,197,3,247,63,
-  68,171,213,32,227,122,196,65,72,98,193,235,44,180,195,84,61,148,35,200,248,46,249,98,145,140,159,37,12,2,124,64,
-  219,0,233,74,164,74,53,62,249,206,78,226,150,65,152,84,39,53,56,52,196,236,252,28,205,48,160,157,74,125,177,130,
-  115,29,7,173,77,10,28,214,98,140,166,86,171,226,103,92,242,249,18,205,48,96,110,97,30,41,211,240,196,106,165,130,
-  171,20,214,164,156,85,169,92,198,242,60,159,35,33,218,228,123,186,123,102,173,193,90,8,171,117,122,183,236,80,103,255,
-  237,175,15,220,245,161,191,45,222,112,252,136,219,1,98,6,238,250,87,112,34,130,214,225,75,24,114,9,128,94,42,240,
-  44,192,255,207,222,123,199,89,118,223,245,221,239,95,57,229,246,185,211,103,103,123,209,54,173,122,177,154,37,119,185,202,
-  13,8,37,33,129,0,126,72,131,39,161,132,36,15,36,36,144,74,66,66,28,74,32,4,176,41,46,184,55,92,100,201,
-  234,171,174,149,86,219,251,238,236,244,91,79,249,149,231,143,115,103,180,54,205,96,9,21,239,239,245,154,151,118,71,187,
-  51,103,239,156,243,185,223,242,41,156,2,126,128,111,186,203,250,75,143,4,42,80,23,176,105,57,174,76,95,246,186,107,
-  20,244,33,183,72,235,113,69,201,129,116,30,151,231,136,193,59,50,8,132,20,197,140,199,14,222,149,165,196,15,38,226,
-  43,96,4,133,113,188,207,13,62,84,156,238,119,249,232,93,119,243,247,223,248,38,66,151,98,113,96,37,97,20,211,239,
-  84,121,250,217,62,235,215,150,169,55,60,42,239,35,58,179,184,216,67,185,4,57,216,94,31,35,83,84,37,38,144,67,
-  24,47,176,54,3,21,146,187,18,41,154,80,123,180,47,200,139,86,40,50,3,222,180,169,15,149,120,251,123,110,231,205,
-  111,125,3,15,222,247,8,31,252,221,63,228,161,7,31,37,138,67,74,81,137,32,136,72,211,28,143,32,42,5,24,231,
-  17,40,36,18,147,91,242,204,162,148,64,105,89,16,6,131,128,74,84,193,135,130,220,230,56,60,81,28,177,121,203,86,
-  30,127,226,113,20,25,90,234,162,85,146,174,208,153,5,33,198,20,192,157,166,41,129,214,24,227,72,243,20,132,164,159,
-  37,164,54,167,159,244,9,195,128,86,171,77,225,40,154,227,128,90,163,134,84,18,55,88,48,40,169,17,82,23,44,174,
-  11,218,178,94,123,153,117,211,107,104,254,248,63,173,60,252,209,143,94,127,197,151,63,175,134,189,143,13,124,165,4,251,
-  183,192,82,5,252,69,29,217,69,0,250,166,42,149,73,10,13,215,142,231,177,124,86,192,19,32,231,138,66,104,58,219,
-  185,99,100,227,206,93,216,212,146,244,59,8,233,138,42,198,23,27,163,94,175,11,222,161,68,145,12,97,141,89,157,251,
-  60,199,50,94,49,79,24,84,65,120,140,42,102,53,101,35,80,42,230,107,7,158,100,219,206,77,188,97,199,165,244,150,
-  90,8,227,176,57,172,25,154,224,228,241,51,124,224,171,79,112,235,171,119,113,199,117,83,216,164,199,252,236,25,98,29,
-  81,206,45,54,201,88,204,12,102,104,136,145,53,101,130,64,35,188,4,165,153,157,159,71,72,69,183,211,197,171,25,234,
-  141,26,82,87,112,94,33,149,194,225,73,147,28,137,224,198,91,111,230,85,55,221,196,129,125,251,249,202,151,190,202,51,
-  207,60,195,220,220,18,74,4,100,169,37,233,91,42,165,50,161,87,68,198,147,24,143,49,142,60,205,80,113,9,107,29,
-  100,142,37,215,37,40,85,208,81,8,162,152,243,76,76,140,50,58,210,228,212,233,110,81,149,12,44,69,140,205,65,20,
-  91,49,47,32,140,35,140,49,56,147,99,58,150,48,138,152,95,92,160,223,79,152,28,155,196,26,67,179,217,196,123,79,
-  169,92,66,105,69,16,40,226,114,92,172,227,157,35,203,51,132,176,3,198,53,104,173,7,111,4,158,60,203,168,40,73,
-  237,111,127,79,233,228,244,248,117,230,247,63,80,155,204,237,68,79,138,207,143,35,30,250,53,231,230,43,224,22,120,101,
-  229,182,95,4,160,231,241,40,10,95,103,65,17,18,118,252,121,158,41,165,160,34,168,183,96,172,113,211,117,213,122,163,
-  129,107,183,240,214,160,66,129,84,2,225,5,46,203,200,211,180,136,151,246,126,96,71,81,188,227,66,65,144,91,25,64,
-  23,174,132,172,154,181,163,36,18,65,104,138,90,174,87,141,249,208,253,95,99,247,186,45,108,40,197,44,119,91,104,23,
-  146,229,142,169,181,27,105,239,123,154,95,248,149,79,32,126,244,157,212,134,234,220,125,239,62,226,44,229,71,190,235,38,
-  134,71,3,226,126,196,51,71,231,105,68,109,42,227,13,50,93,66,232,136,131,7,159,162,185,118,43,222,11,22,102,206,
-  211,91,156,163,212,104,82,105,142,82,173,84,10,95,34,95,120,10,101,105,23,240,236,216,179,149,75,118,108,227,220,153,
-  179,60,244,208,163,124,229,139,119,113,238,244,108,17,76,24,4,148,42,37,172,205,8,203,37,114,60,229,64,23,179,111,
-  40,230,58,78,128,41,82,53,34,165,137,163,136,32,214,92,126,249,30,218,189,22,110,169,69,154,24,210,36,91,229,4,
-  33,4,65,84,48,116,196,32,230,71,56,143,233,247,9,131,16,37,36,75,75,11,56,107,145,82,210,239,247,168,155,58,
-  81,20,130,244,88,107,168,86,171,72,41,144,97,136,179,30,231,138,24,162,149,159,197,10,113,209,123,240,221,140,145,215,
-  191,49,58,63,57,124,89,247,215,254,111,125,211,242,98,121,9,175,55,194,67,85,152,237,129,41,93,196,148,87,30,0,
-  125,171,206,117,9,133,153,188,164,176,164,204,95,128,235,139,139,55,191,81,7,35,91,46,223,173,36,96,50,131,183,25,
-  74,23,43,102,37,21,153,45,230,22,82,73,156,119,232,32,24,168,203,47,92,189,251,63,245,13,60,126,16,56,232,49,
-  20,179,26,25,132,204,183,219,124,224,11,159,231,159,221,241,86,156,42,24,214,74,20,38,242,223,247,198,55,241,95,126,
-  111,150,159,254,165,207,33,34,232,38,57,161,81,28,88,106,177,123,199,4,71,143,44,115,234,200,44,127,247,221,55,242,
-  222,219,47,195,36,243,232,234,40,235,199,199,120,228,153,253,116,83,197,182,29,59,49,198,50,127,110,134,246,210,34,181,
-  122,131,218,80,131,168,92,69,6,33,86,120,172,119,164,38,69,34,153,88,59,206,29,27,238,224,181,175,123,45,143,63,
-  246,36,95,250,226,157,28,63,114,140,36,55,84,170,117,84,168,177,222,147,103,6,99,220,96,123,101,168,196,101,164,119,
-  144,59,172,51,232,82,5,41,2,166,214,172,99,235,37,219,185,251,174,175,17,170,128,36,179,160,6,100,1,231,49,185,
-  41,12,213,100,97,65,43,133,24,48,206,29,89,158,211,106,183,240,206,17,134,154,90,173,86,204,216,36,76,6,147,184,
-  208,208,235,118,169,213,235,133,188,68,20,145,67,193,96,213,239,157,199,13,126,47,165,198,25,73,214,74,41,95,114,185,
-  92,248,7,63,182,57,251,205,255,169,183,207,156,143,60,52,143,193,253,13,56,190,5,146,228,34,174,188,178,0,40,191,
-  106,43,187,119,175,35,252,163,175,145,165,127,53,248,176,20,9,25,215,3,163,47,212,245,1,79,8,17,119,188,31,213,
-  245,198,232,212,13,87,107,156,197,101,25,206,228,56,95,2,81,112,115,140,205,201,77,134,180,6,165,195,1,35,167,168,
-  118,132,144,120,107,89,33,37,174,14,94,133,0,161,144,182,72,109,74,5,32,37,42,7,77,200,67,71,14,242,91,95,
-  249,10,239,186,246,90,148,51,72,163,201,101,78,173,164,248,238,55,190,129,95,248,208,23,104,231,61,100,73,145,89,205,
-  231,246,158,225,179,15,157,70,6,37,162,192,243,31,62,242,101,234,53,197,229,27,70,153,61,177,200,153,182,227,145,189,
-  79,241,203,191,254,33,182,238,220,205,187,223,253,110,94,253,170,203,8,69,74,107,110,158,197,249,121,42,245,58,245,225,
-  97,42,141,6,129,214,184,193,63,194,58,75,106,218,132,21,197,77,183,93,207,171,110,186,154,131,7,14,112,223,221,15,
-  243,181,59,247,226,241,148,43,49,46,246,180,187,93,202,141,152,176,20,145,38,9,222,43,2,29,32,101,72,174,37,113,
-  92,34,183,112,233,165,87,242,212,83,79,211,107,181,105,14,213,200,242,28,33,130,98,16,237,252,234,155,128,146,18,239,
-  92,17,83,173,60,233,128,240,24,230,41,51,51,231,200,243,140,181,211,107,73,251,125,150,151,150,145,82,18,132,30,209,
-  233,80,138,75,128,28,180,93,32,69,161,204,119,190,144,204,56,114,188,18,4,82,144,167,25,181,141,155,200,255,201,143,
-  173,127,246,183,126,227,246,245,135,142,78,148,97,84,194,157,167,224,224,20,180,215,95,92,144,189,124,1,72,0,121,163,
-  194,210,27,174,96,230,138,77,152,177,6,183,106,197,231,119,174,227,183,191,244,56,31,253,218,211,228,105,206,115,89,151,
-  127,246,113,20,121,222,55,12,90,176,244,5,122,1,159,5,209,246,190,9,172,173,108,217,212,28,222,52,141,181,6,147,
-  119,11,93,151,208,131,135,195,144,244,186,224,236,128,241,60,96,227,14,134,209,120,183,218,126,121,127,1,40,249,194,185,
-  112,229,239,32,5,94,120,148,3,229,21,46,138,248,236,19,251,104,150,154,220,182,117,19,89,146,162,51,75,191,103,216,
-  56,58,194,119,191,246,10,126,247,11,119,147,57,5,58,35,20,49,24,141,15,114,140,78,57,179,232,249,177,247,127,133,
-  225,102,133,110,39,101,177,221,38,115,22,21,132,156,252,218,131,60,242,200,19,188,241,182,27,121,215,59,223,202,174,221,
-  59,9,53,244,187,61,58,237,54,81,185,68,115,116,132,70,99,4,175,36,214,219,66,217,142,45,60,138,156,103,199,206,
-  75,216,121,217,101,220,254,246,183,240,229,47,124,145,189,247,61,200,236,217,89,134,202,117,92,167,133,75,3,164,183,164,
-  38,165,60,52,76,181,90,47,50,169,180,0,231,41,5,17,239,121,199,59,57,124,248,89,158,120,226,9,162,114,137,165,
-  229,22,97,28,145,13,88,210,114,16,249,147,101,57,30,79,54,72,96,245,56,58,221,54,181,106,13,99,114,58,221,22,
-  229,82,153,229,229,101,148,82,52,135,155,88,107,73,210,148,40,140,7,175,125,209,26,15,56,163,3,141,158,197,11,135,
-  247,2,169,21,89,158,32,106,227,200,31,253,137,177,187,255,240,119,94,189,235,129,251,135,214,195,104,15,190,252,42,120,
-  236,10,88,72,193,70,23,49,230,229,1,64,57,48,178,123,61,122,231,58,90,151,109,228,245,113,200,201,209,58,34,55,
-  8,99,73,115,203,244,112,149,159,255,222,219,248,123,175,187,156,234,151,30,71,126,237,105,236,159,83,17,57,138,16,194,
-  179,131,141,215,11,241,118,36,129,118,113,237,81,73,136,245,125,239,119,174,123,219,219,199,100,185,140,233,246,240,46,65,
-  137,0,208,197,128,217,249,85,63,100,6,156,151,40,12,137,226,136,86,183,187,26,123,188,34,205,88,253,62,82,146,187,
-  65,219,229,61,184,98,107,230,5,228,206,226,60,200,64,243,249,71,246,178,113,120,132,201,106,68,110,122,4,68,180,231,
-  123,188,113,247,30,22,151,58,124,244,190,135,16,97,8,34,35,84,93,80,224,92,97,89,177,216,207,152,235,167,68,90,
-  34,117,128,34,2,169,209,58,36,113,57,127,252,185,187,249,220,157,143,112,219,173,215,112,199,219,223,192,158,93,91,168,
-  148,98,156,49,204,30,63,193,114,121,137,161,137,9,170,181,234,192,243,87,161,131,16,132,162,211,233,179,124,254,12,179,
-  199,207,176,182,82,133,117,235,56,221,77,169,135,49,177,82,132,8,186,121,194,162,135,185,153,57,186,75,109,198,54,111,
-  33,8,36,105,150,99,109,78,24,106,190,255,251,190,143,143,125,252,227,124,249,43,119,2,30,107,114,180,208,171,131,107,
-  103,115,226,48,36,51,6,169,21,74,74,76,150,23,0,222,106,83,12,174,60,83,83,147,68,97,206,242,242,50,14,71,
-  115,120,152,74,69,145,101,57,229,178,94,5,33,6,111,16,206,186,98,254,230,67,112,133,246,77,106,79,154,56,74,81,
-  157,202,59,223,19,155,55,189,254,234,195,191,248,31,27,91,242,108,196,66,53,132,7,159,130,153,61,96,194,151,216,67,
-  191,79,202,213,88,171,111,43,0,74,128,141,19,67,252,236,15,188,129,70,28,98,7,15,85,131,34,139,107,221,244,8,
-  86,73,82,99,25,118,30,219,251,250,154,37,51,142,204,164,76,13,87,113,223,123,27,188,238,114,26,95,126,2,113,247,
-  62,220,5,64,180,18,45,123,30,88,203,11,231,247,187,242,3,108,65,213,123,191,149,56,222,210,124,219,235,171,18,75,
-  150,165,152,220,13,86,236,186,216,222,164,93,178,126,175,24,184,202,66,12,89,138,53,229,114,9,63,200,137,89,1,32,
-  107,221,170,254,85,136,98,85,239,133,192,27,139,240,14,137,194,121,143,8,20,210,58,188,55,44,187,156,15,221,123,55,
-  223,123,235,45,12,199,18,155,231,160,36,173,185,14,223,121,221,13,44,47,182,248,220,254,131,184,178,4,111,209,54,192,
-  88,80,202,18,72,80,14,228,64,29,39,40,252,155,17,16,149,202,248,146,198,163,120,248,209,253,156,58,113,134,93,219,
-  54,114,203,205,215,113,217,229,187,24,106,12,145,217,140,133,153,25,210,110,74,165,54,68,218,233,49,115,242,0,199,15,
-  28,96,230,228,9,226,126,159,114,98,112,78,210,64,49,57,189,113,176,66,79,200,77,142,202,12,205,80,179,182,92,99,
-  46,77,153,57,124,24,61,220,164,50,62,78,230,45,206,11,78,159,62,201,79,252,228,63,33,205,219,124,233,75,119,35,
-  69,165,160,38,136,140,40,14,169,196,53,58,221,14,90,10,188,45,242,230,65,160,85,17,1,237,188,35,205,51,150,150,
-  150,169,85,235,8,4,189,110,159,48,234,162,164,162,82,9,200,243,156,48,140,6,250,48,199,74,136,136,243,2,114,9,
-  42,196,147,35,29,84,194,144,78,107,137,209,106,153,239,251,233,127,166,62,181,110,253,246,189,63,246,99,149,43,186,189,
-  138,4,213,129,175,46,195,236,8,188,36,82,88,87,172,69,126,41,8,200,40,66,59,95,241,0,180,178,72,206,128,145,
-  177,6,191,250,99,119,176,126,98,168,48,154,186,96,78,99,160,248,92,246,156,115,255,159,91,45,25,7,38,133,225,42,
-  35,223,115,43,63,248,218,203,80,95,126,2,238,222,135,72,115,202,20,17,181,103,120,97,27,113,89,128,156,16,48,226,
-  96,115,112,217,158,53,205,203,182,8,159,27,112,41,185,145,72,157,82,81,10,188,34,239,247,17,198,34,80,24,28,126,
-  197,255,199,56,172,43,66,6,87,164,2,82,62,39,192,47,214,243,190,16,203,242,220,127,5,130,64,72,188,240,56,47,
-  201,133,227,208,226,57,62,255,248,227,124,199,245,55,19,136,20,200,201,156,193,119,3,190,247,213,111,96,222,165,220,115,
-  228,32,58,172,32,115,65,136,199,34,17,82,16,170,1,120,123,135,240,30,233,50,74,58,100,124,168,73,110,114,132,16,
-  140,141,77,17,149,74,156,154,79,249,228,103,239,229,190,251,30,229,170,61,59,216,126,201,70,58,237,22,237,185,46,237,
-  217,46,102,185,79,144,101,148,176,172,169,104,148,86,36,78,226,50,75,106,12,221,165,46,206,187,194,31,218,164,69,155,
-  3,216,180,79,83,8,106,181,26,11,189,148,238,169,179,232,161,17,42,245,58,231,103,23,233,245,230,249,169,159,252,127,
-  88,156,159,101,239,195,71,8,75,101,124,224,41,87,99,66,95,8,116,157,119,3,98,165,34,205,114,50,99,232,167,9,
-  121,158,34,148,196,26,75,28,197,76,140,79,33,165,160,221,106,19,4,33,82,104,84,173,134,181,133,231,145,82,43,222,
-  73,133,59,54,62,199,32,200,189,65,89,69,37,12,57,53,59,203,235,223,250,90,92,214,229,109,127,255,7,248,125,45,
-  215,126,242,239,255,240,107,223,105,221,9,7,79,206,192,124,11,220,139,61,16,18,131,89,232,191,14,67,238,148,154,186,
-  51,47,9,81,219,11,10,64,110,0,44,149,102,149,205,175,187,156,137,155,119,225,203,17,157,110,250,252,212,127,3,32,
-  154,24,174,97,191,231,86,14,189,254,114,110,253,195,175,177,243,209,195,0,172,249,27,120,1,123,16,28,131,113,11,235,
-  135,94,251,186,166,142,171,248,100,25,155,38,120,111,138,76,46,89,100,16,228,121,134,181,69,24,160,29,16,221,60,43,
-  186,172,1,24,15,124,130,254,172,90,235,66,113,170,117,5,143,104,69,84,169,156,67,11,137,11,35,30,58,122,144,241,
-  198,16,119,92,118,13,174,215,162,143,225,140,93,34,50,1,223,119,213,141,196,157,156,7,79,159,192,198,101,148,15,17,
-  214,227,188,199,42,129,86,133,69,170,181,6,231,114,242,212,50,115,110,142,56,148,132,74,50,111,78,49,57,53,65,189,
-  90,161,169,20,213,126,206,137,251,31,135,99,103,41,41,133,75,114,74,174,0,70,169,36,198,58,218,185,135,52,71,90,
-  135,177,22,99,29,153,207,200,172,41,146,58,132,7,45,80,229,18,149,114,131,70,189,1,65,192,194,177,19,44,204,204,
-  34,77,151,233,250,14,36,1,71,143,156,224,250,235,246,240,175,254,229,63,229,39,127,242,223,176,255,192,9,234,67,99,
-  52,74,195,88,99,16,84,233,13,50,168,60,158,48,12,81,74,209,207,82,4,144,101,25,222,58,206,156,57,131,115,48,
-  58,54,70,104,45,189,118,135,44,203,49,206,80,171,214,169,84,42,171,213,167,247,162,72,232,144,186,48,52,179,69,74,
-  108,146,37,84,107,21,46,185,100,43,38,203,9,189,164,50,58,202,130,84,177,178,174,44,64,71,20,113,226,238,69,126,
-  208,235,192,191,15,52,127,168,53,53,138,153,226,43,106,6,228,47,120,145,61,69,252,46,59,215,177,118,231,90,198,111,
-  217,77,216,168,144,37,25,100,230,121,111,62,141,177,96,44,253,225,58,63,243,67,111,34,253,185,15,242,244,249,165,23,
-  244,37,22,131,170,110,73,16,134,158,241,69,88,51,114,235,205,37,133,199,152,12,211,239,34,113,40,21,163,116,128,247,
-  22,147,167,5,104,120,135,148,69,128,173,214,130,56,14,87,163,108,87,212,217,95,255,157,10,209,164,64,192,128,211,178,
-  242,103,11,41,130,3,225,9,148,66,58,65,174,224,11,143,61,194,80,28,115,203,142,29,200,180,133,72,29,121,218,39,
-  182,154,247,190,234,213,52,158,126,156,135,79,31,165,235,10,143,105,97,4,210,41,2,87,172,178,5,158,32,140,40,7,
-  33,81,28,83,173,68,140,55,26,76,84,235,76,148,171,76,15,141,80,46,197,200,64,97,133,67,57,143,76,243,130,81,
-  237,13,185,243,128,66,234,136,126,55,37,21,150,196,103,232,40,160,50,90,165,54,50,78,169,89,163,57,49,78,117,104,
-  136,176,90,163,218,28,162,92,46,83,169,148,209,97,200,77,173,46,79,63,245,12,79,60,246,4,39,143,159,167,22,55,
-  57,123,102,134,94,111,43,107,166,70,249,133,127,247,207,249,231,63,253,139,204,205,166,3,110,133,39,75,83,148,146,88,
-  231,208,186,104,169,188,119,72,138,100,144,94,210,39,14,66,148,86,44,183,22,169,86,43,40,41,105,181,150,41,85,43,
-  69,180,144,43,140,203,74,165,18,74,169,194,187,9,81,196,71,231,14,44,232,72,50,59,115,158,75,182,111,165,222,168,
-  97,156,1,169,184,247,11,95,98,71,158,207,122,56,235,161,29,131,123,49,1,200,15,182,191,255,88,7,252,143,64,81,
-  161,224,139,188,84,236,102,181,80,223,130,24,65,74,86,254,190,6,42,151,111,66,150,66,244,45,187,177,149,24,49,61,
-  194,26,41,241,89,142,233,190,240,236,8,153,229,44,70,1,235,55,79,210,56,191,244,130,127,191,22,240,8,34,2,223,
-  12,54,108,104,214,247,236,146,152,156,60,233,99,6,201,12,222,151,65,106,172,205,72,147,94,97,52,54,0,24,231,44,
-  74,105,148,214,131,77,23,23,12,161,87,139,157,1,40,13,214,204,95,103,140,54,0,31,239,113,90,32,188,39,180,130,
-  80,132,244,69,202,239,63,240,85,114,109,184,117,227,86,84,219,98,8,200,164,71,59,203,219,118,238,225,198,75,182,178,
-  104,19,210,126,177,173,114,12,146,36,6,241,205,165,48,42,18,53,148,162,28,86,8,156,34,80,2,103,50,2,97,240,
-  89,23,111,53,90,106,188,150,248,40,32,32,66,100,142,126,150,179,220,239,211,241,45,214,111,219,204,216,218,81,202,163,
-  21,234,245,10,149,90,140,142,2,84,160,137,74,85,74,213,26,81,185,134,10,162,98,219,132,39,199,211,24,170,115,243,
-  173,183,112,243,205,55,113,238,212,57,158,120,252,25,14,60,251,52,135,14,157,96,251,182,141,108,219,182,149,127,243,175,
-  127,134,159,255,185,95,98,102,118,17,98,137,212,186,104,193,188,31,204,128,32,12,195,162,133,28,56,34,58,239,73,179,
-  156,115,231,207,227,61,108,222,180,165,104,7,251,69,102,189,53,57,173,214,50,206,123,106,213,106,33,135,65,226,173,67,
-  73,9,40,156,181,228,54,225,234,107,47,7,60,161,14,217,127,240,8,207,124,252,19,230,125,112,220,194,33,13,75,245,
-  193,140,243,197,60,95,144,138,223,213,154,0,255,146,99,106,107,51,119,254,91,40,3,4,56,199,145,127,242,118,110,202,
-  51,130,245,227,116,181,42,18,150,60,248,44,255,27,103,67,104,231,217,255,134,43,56,176,247,96,49,176,125,1,223,89,
-  98,96,220,251,178,135,145,112,203,214,161,161,169,113,92,178,68,210,235,146,103,25,82,41,52,26,33,4,73,210,37,75,
-  250,72,10,73,65,161,245,26,172,214,87,124,120,46,104,189,86,128,198,251,1,39,104,133,22,61,16,78,14,28,134,86,
-  127,14,210,121,156,176,24,41,138,12,83,173,233,248,156,143,221,127,15,83,81,149,45,19,83,164,73,15,103,12,185,201,
-  136,195,136,102,20,209,240,1,42,168,226,130,128,92,21,32,134,243,152,44,69,82,124,47,233,37,248,140,92,21,98,88,
-  165,4,50,0,173,3,194,160,132,150,154,204,89,82,233,88,204,61,243,2,206,217,148,182,75,72,178,46,75,157,121,174,
-  10,134,217,50,53,65,92,142,201,242,148,52,201,177,253,20,33,66,234,245,128,72,6,5,209,82,106,172,179,8,169,138,
-  170,33,77,16,192,228,218,49,38,55,76,114,211,242,13,28,126,246,32,207,30,58,129,240,134,29,59,118,242,19,63,245,
-  15,249,255,126,246,223,178,216,235,145,229,22,41,53,72,129,177,150,64,42,162,168,68,179,57,196,210,114,11,99,115,172,
-  241,228,121,78,160,53,173,78,139,243,179,231,24,29,29,195,121,8,130,238,160,237,114,116,41,194,22,203,229,74,97,215,
-  33,11,187,146,80,75,206,158,59,195,214,237,91,152,90,55,69,158,166,200,184,194,39,254,248,19,108,63,113,124,110,4,
-  158,78,224,72,31,250,127,60,24,246,190,88,51,160,42,240,159,2,205,146,128,202,75,113,13,159,30,125,246,91,255,42,
-  227,117,42,66,224,211,28,159,190,184,163,45,151,27,234,27,199,153,188,110,27,203,247,238,127,65,1,40,4,169,165,168,
-  230,206,143,235,230,112,69,73,143,119,166,240,44,70,96,156,163,44,11,73,64,175,219,42,230,63,190,40,199,133,4,33,
-  10,63,157,80,235,1,25,81,172,90,76,172,180,87,43,230,100,171,237,217,170,107,98,17,203,190,194,158,46,100,98,18,
-  163,5,214,21,32,18,137,128,212,102,252,225,125,247,242,206,91,110,102,107,115,24,233,28,38,10,200,180,68,100,14,97,
-  5,169,80,120,3,202,22,58,52,37,36,210,5,8,173,87,149,250,198,59,34,33,9,195,34,250,199,8,193,114,158,243,
-  236,249,25,90,253,62,105,32,201,227,128,32,174,162,194,26,178,222,164,108,42,212,228,48,253,190,225,206,187,30,224,190,
-  251,247,114,249,21,151,177,125,219,54,70,70,134,48,121,70,123,169,67,191,123,140,114,173,70,169,86,163,90,175,161,195,
-  16,164,196,123,71,38,61,69,253,97,193,164,196,213,144,61,87,95,193,214,254,37,28,218,255,52,15,61,250,8,227,163,
-  99,188,251,59,223,198,175,188,255,183,177,126,32,252,85,1,165,40,70,8,65,158,229,140,141,142,16,232,128,165,165,37,
-  210,52,197,25,143,113,142,94,191,207,169,51,103,136,227,18,163,195,99,152,44,167,211,110,175,254,12,236,192,132,191,82,
-  41,3,18,233,100,65,127,200,51,110,190,229,70,114,107,64,41,78,159,62,203,227,31,248,160,249,30,56,156,195,227,14,
-  78,255,79,48,159,124,177,159,240,32,36,148,138,242,138,156,231,165,6,64,66,61,15,99,32,99,95,244,33,219,215,129,
-  131,117,76,188,246,114,166,30,60,64,108,220,11,242,238,179,226,41,52,7,82,128,94,74,83,221,203,218,4,120,18,107,
-  81,113,140,116,150,168,84,198,153,140,164,215,43,252,119,156,67,106,133,27,112,160,165,210,132,97,225,197,44,132,94,5,
-  155,21,48,130,129,47,180,95,177,140,125,78,167,228,87,53,99,69,236,178,178,128,183,24,105,139,4,119,227,80,50,228,
-  88,210,227,255,124,233,203,220,176,105,59,87,109,222,204,84,165,76,32,32,215,6,35,28,194,57,20,30,159,231,132,74,
-  227,124,142,14,10,101,184,80,122,80,81,72,156,146,28,95,110,113,236,208,97,150,140,97,161,223,199,5,138,209,169,9,
-  26,149,6,54,243,136,92,130,233,17,42,77,164,21,198,42,130,32,102,116,122,10,25,6,28,62,182,200,129,3,119,51,
-  49,218,96,207,165,219,88,51,57,140,146,158,206,226,121,58,11,179,180,162,152,82,181,78,181,57,76,185,82,33,212,193,
-  192,56,3,44,186,176,6,17,150,168,164,184,236,218,107,217,190,123,55,167,78,28,229,202,252,42,246,92,246,0,247,61,
-  240,16,82,69,104,161,73,179,140,56,138,139,193,179,135,225,102,147,114,169,68,183,223,163,221,43,236,102,187,73,31,235,
-  28,71,143,29,199,89,201,154,169,73,100,36,105,181,90,24,107,168,84,138,90,83,105,73,92,138,9,101,192,169,83,167,
-  216,125,217,78,70,199,70,233,231,125,170,213,33,62,241,177,223,99,253,51,79,204,143,192,83,25,60,149,195,210,20,248,
-  247,240,226,144,237,34,224,30,41,57,174,36,218,191,116,73,217,175,72,49,170,207,12,193,166,9,22,174,217,198,254,251,
-  159,125,65,128,127,80,1,249,186,243,109,5,199,151,191,248,133,195,31,188,241,117,155,198,234,13,113,166,164,74,205,43,
-  246,68,107,174,190,92,236,186,118,13,209,112,25,99,115,220,64,107,36,173,69,200,24,239,51,180,202,10,107,10,21,124,
-  67,235,245,28,8,201,193,122,94,2,94,174,12,159,221,215,189,163,121,81,216,189,202,66,201,129,195,227,133,196,121,129,
-  208,154,101,39,249,204,254,125,124,245,232,1,118,142,141,115,249,212,90,214,77,140,83,46,151,8,181,38,116,2,97,13,
-  206,122,180,214,16,134,244,157,101,169,223,99,118,97,145,99,115,51,156,92,158,39,183,30,21,197,56,161,200,173,165,42,
-  171,72,25,98,157,196,107,72,76,78,40,161,84,9,139,217,150,113,24,160,223,206,104,84,98,42,186,142,12,60,231,206,
-  156,231,248,241,47,49,52,92,225,138,43,119,179,97,253,52,181,114,136,49,57,11,243,231,105,45,204,17,149,202,148,199,
-  70,168,150,107,148,75,53,132,18,88,145,98,189,197,122,143,77,19,164,128,205,219,182,178,105,203,22,174,186,250,58,62,
-  251,185,207,243,155,191,245,127,121,252,177,125,120,36,18,79,160,66,150,150,150,72,210,144,48,138,105,54,135,9,162,136,
-  126,175,143,71,146,152,148,124,113,158,44,55,244,210,46,107,166,166,136,227,152,165,197,37,242,204,210,28,86,164,73,66,
-  24,133,180,250,203,72,13,55,223,124,19,206,90,34,29,115,250,212,121,30,249,205,223,202,239,192,31,204,97,175,135,19,
-  215,65,246,134,23,241,25,56,43,4,215,135,209,115,109,250,69,0,250,27,110,197,172,99,232,245,87,176,254,225,67,168,
-  220,62,239,85,144,2,22,193,159,132,249,146,20,119,111,74,211,220,61,252,204,6,15,245,10,76,44,127,230,158,201,103,
-  66,53,126,248,250,43,71,174,252,169,31,175,142,111,223,36,122,102,22,235,52,113,10,50,116,56,45,64,36,131,97,178,
-  64,122,119,129,254,75,174,130,80,209,183,13,124,158,145,69,237,228,60,98,176,13,43,64,106,64,54,241,30,225,124,65,
-  58,19,96,241,104,83,36,107,248,80,145,56,203,3,167,143,179,247,212,113,154,97,133,169,70,147,201,161,49,214,84,203,
-  12,87,75,212,107,67,100,166,207,241,153,25,78,44,204,114,116,113,150,142,40,134,172,129,40,242,182,116,146,3,25,90,
-  41,98,165,48,253,62,11,221,46,94,64,20,197,140,143,143,19,148,11,51,51,75,225,225,147,154,132,133,158,39,10,3,
-  164,132,90,115,132,166,30,35,207,83,246,62,116,148,135,246,30,100,227,186,81,118,108,219,88,164,99,152,140,94,119,153,
-  243,201,34,101,25,51,90,110,82,30,31,161,92,175,160,148,198,51,240,215,150,146,196,230,72,33,104,140,84,248,222,31,
-  248,30,222,126,199,219,248,212,39,63,205,239,254,223,15,242,228,147,251,233,27,139,117,142,118,183,131,214,133,24,182,214,
-  168,81,42,149,232,245,122,88,231,48,194,51,215,158,163,125,184,205,252,252,28,155,55,109,97,120,120,152,110,167,131,214,
-  138,74,41,70,24,199,233,153,83,188,243,221,239,96,104,164,70,154,100,148,170,53,62,244,187,191,206,182,39,30,62,51,
-  10,15,36,240,112,11,22,62,51,40,88,95,140,83,1,126,55,8,56,73,193,253,185,8,64,47,198,201,12,126,227,4,
-  242,71,223,202,154,247,127,26,242,231,127,241,216,7,74,208,203,157,223,39,224,140,46,102,126,229,88,48,86,246,172,153,
-  202,236,166,252,238,189,59,14,221,255,247,46,219,123,199,235,54,237,252,225,247,68,235,39,183,145,36,139,72,221,66,136,
-  18,74,12,161,85,4,184,162,130,17,69,122,198,10,17,110,117,45,63,48,172,119,190,104,187,164,82,95,103,170,190,194,
-  7,186,112,139,38,87,62,47,37,129,82,88,99,11,251,213,48,194,11,232,144,115,176,53,195,254,185,211,72,233,9,164,
-  36,210,49,74,200,194,188,203,122,172,20,232,56,90,77,39,213,170,200,55,43,151,43,68,113,140,113,150,147,167,79,19,
-  199,37,198,199,39,24,105,142,96,115,203,236,204,44,113,28,175,234,245,162,40,30,236,44,86,248,75,57,214,22,60,29,
-  173,3,132,16,28,63,50,199,129,103,142,49,60,82,101,231,246,205,108,218,184,150,161,64,98,178,140,179,217,121,92,111,
-  137,122,28,81,175,84,24,106,142,16,68,85,242,129,161,91,110,7,196,186,44,71,7,146,239,249,219,223,199,219,223,254,
-  46,30,221,251,24,95,251,218,125,124,225,11,95,230,192,161,99,244,147,132,32,10,152,155,93,40,30,0,165,138,156,54,
-  239,80,66,225,242,156,153,153,51,100,105,159,203,47,191,156,145,145,113,242,60,163,221,105,211,237,119,216,184,105,3,59,
-  119,238,32,207,13,97,24,114,248,196,49,30,248,63,191,145,127,63,236,207,225,254,18,28,59,5,217,223,126,49,239,253,
-  48,68,42,93,204,125,46,2,208,139,119,68,146,49,127,249,102,178,31,125,43,27,223,255,105,196,243,12,66,26,232,22,
-  221,88,79,67,63,0,150,64,214,60,65,8,165,150,96,88,121,177,109,58,207,110,46,127,248,179,183,126,245,193,71,47,
-  189,228,39,126,108,232,134,215,95,67,175,107,7,3,77,129,51,5,229,95,14,252,160,221,138,6,98,208,146,173,216,77,
-  92,104,211,186,2,60,43,195,106,121,65,53,116,97,43,183,226,105,227,7,131,43,233,197,170,127,142,212,18,99,45,178,
-  172,81,66,32,45,24,36,198,175,204,47,67,74,186,200,143,151,10,226,74,9,111,29,165,114,133,48,138,200,178,12,188,
-  39,51,57,213,160,134,150,146,110,167,179,250,61,3,173,139,7,92,22,210,143,64,5,104,45,139,239,53,96,25,39,73,
-  138,14,52,113,80,162,57,60,133,195,210,89,94,228,179,95,184,151,102,57,224,150,203,118,49,185,110,140,242,196,8,153,
-  23,164,237,54,115,237,22,203,115,115,148,42,13,106,35,35,148,107,197,247,206,93,97,248,111,129,110,183,69,20,135,220,
-  124,219,141,188,250,53,175,230,71,254,193,63,224,222,123,31,230,163,127,252,97,238,249,218,221,164,105,70,20,70,196,113,
-  68,158,164,244,147,4,231,61,222,21,18,142,86,123,137,125,251,246,177,109,155,97,100,100,140,126,191,199,240,112,131,183,
-  189,237,205,224,29,89,106,208,181,144,79,125,224,119,184,250,240,129,99,117,184,191,7,251,2,104,189,19,120,138,63,61,
-  243,125,33,225,192,3,35,192,7,148,226,159,43,77,224,95,30,98,124,245,163,111,185,230,149,140,65,200,220,144,172,27,
-  163,191,126,140,161,71,14,33,158,71,82,252,50,48,63,88,135,43,10,233,199,97,240,195,96,110,133,36,135,37,7,231,
-  114,56,83,130,214,250,229,142,220,255,213,123,26,231,70,199,106,27,47,221,69,232,11,207,228,187,238,126,128,99,39,207,
-  34,6,98,157,21,95,232,213,56,154,193,44,200,57,55,208,39,125,189,80,245,27,65,103,229,215,95,71,106,84,18,47,
-  7,105,139,198,162,17,84,163,50,177,10,9,133,70,152,66,218,81,12,155,1,45,81,113,128,197,146,216,34,157,162,86,
-  169,33,164,32,77,19,58,157,46,253,94,15,132,96,100,120,152,90,165,86,180,70,222,23,219,62,32,138,162,85,66,95,
-  1,176,158,104,144,131,22,69,17,66,72,180,214,40,169,176,206,98,156,71,5,17,149,114,141,250,208,48,253,133,101,204,
-  233,89,78,31,58,68,167,213,166,17,86,168,13,213,32,16,228,182,24,236,247,151,151,232,245,150,17,18,202,165,26,82,
-  72,36,32,10,50,19,198,89,146,44,33,42,133,236,218,189,157,55,191,249,245,220,116,211,171,24,30,110,210,233,180,201,
-  147,140,230,240,8,65,16,210,239,245,113,110,197,255,71,18,6,17,173,86,139,209,209,81,192,241,93,127,235,93,76,77,
-  142,131,135,40,8,120,250,208,97,238,253,167,255,108,249,245,237,206,87,114,248,84,31,158,222,9,201,8,48,14,140,81,
-  144,0,199,6,191,127,161,63,246,42,201,63,14,34,114,254,108,171,97,33,64,13,136,173,43,213,180,82,197,27,66,16,
-  40,182,93,178,149,219,110,187,141,64,235,130,157,63,208,38,66,225,101,37,131,129,182,223,63,63,43,39,21,86,95,249,
-  0,180,10,66,235,199,72,166,71,105,60,114,248,121,3,161,229,98,11,198,32,114,144,14,133,58,126,18,216,58,184,249,
-  142,64,22,193,2,48,99,161,179,49,55,254,236,87,31,168,60,99,178,198,37,55,92,33,165,15,185,235,174,123,57,120,
-  248,40,98,0,38,43,244,255,21,32,185,16,100,86,254,255,159,85,13,21,22,18,254,235,51,197,6,21,149,23,69,154,
-  59,222,35,7,10,203,220,22,41,167,94,60,247,103,133,150,232,80,23,214,164,120,132,119,104,41,16,174,48,0,203,141,
-  25,196,240,20,128,40,133,160,90,173,82,46,149,9,244,192,169,144,130,252,87,42,149,208,90,175,138,58,181,46,182,124,
-  5,248,20,198,97,122,224,229,236,188,65,234,34,128,81,8,129,14,66,74,66,161,150,58,12,151,42,204,157,56,203,201,
-  253,135,56,183,52,143,174,151,41,85,170,68,74,131,119,164,105,135,110,167,67,191,157,130,49,4,90,19,69,1,171,92,
-  5,37,16,194,96,93,15,176,172,93,183,134,155,111,186,145,27,111,184,145,237,219,183,211,239,247,72,146,62,214,153,130,
-  57,237,4,90,6,228,198,144,36,61,188,201,121,247,187,223,206,21,87,237,128,65,36,80,88,42,243,191,126,225,23,217,
-  114,231,93,7,198,225,179,33,220,181,31,230,126,0,120,255,224,227,127,0,7,129,47,3,159,166,160,95,4,64,243,5,
-  186,207,127,58,8,216,43,20,241,159,215,17,92,4,160,23,25,132,54,140,147,172,29,165,241,232,17,132,115,207,27,0,
-  137,11,0,168,69,225,57,189,117,101,27,1,124,28,92,0,157,42,204,120,33,102,167,156,203,179,135,159,172,63,124,250,
-  196,208,182,215,223,166,31,121,120,31,79,238,219,143,80,23,182,96,69,98,231,133,192,115,97,69,115,97,160,222,133,31,
-  127,250,166,27,124,222,249,34,73,85,8,132,82,88,1,70,128,21,2,43,5,97,28,17,14,0,76,10,129,242,130,72,
-  40,68,110,136,229,74,52,51,40,165,136,194,16,147,229,148,202,101,132,148,88,99,168,148,43,40,161,80,74,209,104,52,
-  40,149,74,88,107,169,84,42,88,235,6,179,30,189,26,127,180,34,176,181,222,129,42,72,131,82,250,2,248,84,64,16,
-  134,4,129,98,105,126,150,10,138,166,138,8,132,100,118,97,129,243,11,45,250,25,116,146,156,70,115,132,48,8,144,94,
-  144,246,251,180,91,139,116,218,45,156,51,68,81,68,168,117,193,107,18,30,188,45,76,225,140,193,88,203,80,179,206,174,
-  221,59,185,245,214,155,217,115,217,110,202,229,98,91,54,55,191,136,16,146,40,10,177,38,225,59,223,115,7,111,127,251,
-  27,73,122,11,72,47,168,87,235,220,255,216,227,124,246,103,255,117,255,246,36,185,207,194,231,42,176,223,9,145,255,170,
-  82,76,3,63,2,188,9,248,126,224,106,224,243,131,22,105,106,80,17,189,16,231,83,82,241,184,148,127,46,219,249,34,
-  0,189,84,64,104,221,74,37,228,94,112,0,154,7,126,163,248,188,123,15,116,219,48,155,75,185,52,226,125,172,159,61,
-  54,246,200,153,51,213,67,185,212,199,142,159,64,72,113,65,11,245,167,65,230,27,103,60,23,126,254,194,57,209,133,109,
-  218,133,25,90,43,119,97,33,128,245,104,161,80,40,66,161,80,20,159,119,214,19,40,141,68,146,231,150,204,88,156,144,
-  196,229,242,64,246,225,168,86,170,200,193,48,58,12,2,130,32,32,142,34,148,146,228,121,70,28,199,212,106,85,140,177,
-  3,208,41,230,88,90,7,5,165,96,48,181,16,74,34,6,67,115,41,5,10,77,32,66,156,144,24,239,144,145,66,85,
-  35,252,82,155,138,0,161,5,37,17,19,82,97,98,221,86,206,39,57,15,62,254,52,210,72,42,81,68,169,22,162,180,
-  36,79,83,146,126,143,246,242,18,38,205,9,132,64,171,16,169,10,223,231,98,192,47,138,65,184,203,144,26,214,111,90,
-  203,141,55,189,138,55,221,126,59,91,54,95,194,210,252,34,103,207,158,225,166,87,93,197,143,255,216,15,147,167,139,96,
-  115,148,144,232,176,196,175,252,187,255,192,165,15,63,122,116,29,124,33,128,175,61,4,179,191,43,21,135,227,146,248,77,
-  107,185,213,123,70,6,85,112,23,248,42,176,133,194,30,230,133,114,230,252,152,122,249,1,208,183,93,44,143,234,38,180,
-  174,216,196,241,31,125,11,27,222,255,25,68,254,55,19,168,82,46,64,200,127,12,90,239,113,238,201,28,106,107,2,61,
-  162,62,123,119,248,137,122,109,109,80,10,3,147,101,5,176,32,240,190,96,224,202,11,218,178,11,129,71,107,189,170,156,
-  255,198,1,245,159,2,222,213,135,188,16,105,226,11,177,176,116,30,133,67,88,135,87,106,85,68,236,115,87,128,3,160,
-  116,0,74,174,182,94,74,106,130,32,64,169,34,125,52,75,83,2,29,144,36,9,165,184,132,214,138,52,77,72,211,180,
-  16,122,42,133,247,130,44,203,73,146,132,32,8,208,90,19,134,65,241,239,92,105,135,69,49,73,243,78,16,149,34,172,
-  183,32,61,149,177,81,146,133,14,121,110,136,69,70,100,37,73,63,99,97,102,158,181,87,238,161,149,24,172,136,176,132,
-  60,246,212,126,70,134,26,172,95,55,141,240,130,180,103,88,236,205,210,154,59,79,169,54,68,125,116,146,74,165,178,250,
-  58,20,129,140,30,164,163,159,180,208,82,50,54,222,224,123,254,238,119,113,199,187,222,198,125,119,223,69,123,105,142,3,
-  71,15,49,82,47,209,172,55,136,202,53,62,240,193,63,66,253,209,71,58,55,192,137,4,142,155,162,186,85,223,139,183,
-  239,77,19,191,222,89,186,20,130,207,139,190,172,223,198,91,176,63,31,132,210,191,113,16,26,36,103,240,105,224,59,160,
-  229,225,145,60,55,245,105,76,253,54,19,15,237,151,97,83,73,133,244,133,133,201,96,169,245,117,21,205,115,225,133,110,
-  117,229,126,97,149,115,33,0,93,88,49,201,65,98,132,148,5,168,136,193,12,200,225,177,24,130,32,40,218,160,193,106,
-  57,8,66,178,52,69,7,10,99,114,170,229,18,206,121,50,103,9,162,66,185,111,141,161,221,110,19,40,141,22,10,188,
-  164,215,235,83,173,86,17,162,112,23,140,99,63,72,251,240,171,177,67,118,85,205,47,16,190,144,91,40,89,128,159,147,
-  30,161,5,46,207,145,74,16,160,9,157,194,78,79,211,59,115,142,97,33,73,69,70,213,90,206,45,156,166,218,94,203,
-  214,233,181,180,150,231,217,126,229,53,148,71,154,236,123,252,81,206,157,122,130,169,241,49,214,173,157,70,135,138,126,218,
-  162,179,60,71,119,160,120,175,53,139,205,89,16,197,3,6,127,49,124,87,74,226,69,78,158,47,16,215,20,175,127,251,
-  107,49,73,194,241,99,199,120,230,137,39,57,113,98,158,196,30,227,127,252,231,255,225,254,89,97,183,90,115,176,205,192,
-  242,102,56,42,156,59,231,112,173,164,176,46,243,23,193,231,226,22,236,47,104,199,44,201,250,81,146,117,99,212,31,61,
-  140,176,127,245,178,242,155,105,193,78,3,95,24,108,41,174,0,62,3,236,46,90,179,100,20,18,15,147,75,129,222,113,
-  79,16,14,173,26,143,249,175,39,25,94,88,201,60,231,25,237,255,84,229,179,82,233,92,88,45,93,248,241,245,105,171,
-  98,0,72,2,37,139,217,77,173,86,43,42,22,87,228,198,107,173,6,118,20,121,81,53,88,91,248,47,11,177,202,221,
-  41,149,98,164,144,4,65,72,24,22,237,88,173,86,195,24,67,191,223,71,41,73,28,199,197,182,107,144,193,181,178,5,
-  91,189,14,85,72,116,133,148,148,162,98,132,170,131,194,196,204,216,156,64,23,100,199,186,214,4,145,38,208,10,149,24,
-  178,32,96,100,122,154,110,167,79,88,210,236,220,185,133,53,227,147,52,26,67,156,157,61,207,189,123,31,194,8,207,244,
-  154,117,133,87,146,77,201,210,132,94,183,69,175,189,140,201,82,162,149,175,39,20,82,42,112,133,246,12,1,206,228,120,
-  111,24,30,29,102,219,246,173,12,53,71,152,157,95,196,40,201,99,179,11,242,72,107,185,46,97,93,19,182,70,176,206,
-  64,195,129,232,64,222,1,227,193,141,83,240,197,190,10,108,186,216,130,253,233,25,208,255,243,230,171,191,109,209,87,100,
-  134,254,186,49,146,181,99,127,173,153,208,55,3,64,25,133,53,236,235,128,155,128,215,0,87,2,107,192,47,10,161,148,
-  16,235,231,2,125,197,93,81,52,94,216,119,20,55,134,251,51,222,63,47,36,27,94,72,58,252,198,13,217,10,224,92,
-  184,166,247,43,119,224,5,159,135,98,168,28,4,1,81,84,36,83,224,158,179,170,202,243,252,185,121,141,16,168,129,110,
-  80,14,4,158,90,43,226,48,162,92,42,35,101,1,52,65,16,124,29,96,73,41,87,179,207,86,190,215,55,94,183,146,
-  18,169,116,145,243,165,53,90,43,172,115,120,201,192,210,86,66,160,49,173,14,195,97,136,245,25,97,102,72,90,61,212,
-  88,19,89,169,113,254,236,121,54,78,79,81,27,169,177,216,91,230,247,127,255,35,254,254,223,254,80,254,216,159,124,213,
-  30,234,180,148,42,69,76,140,142,82,46,151,17,206,224,242,140,164,215,97,105,126,142,164,215,65,74,77,24,132,133,79,
-  211,224,7,106,125,1,150,198,26,28,158,106,181,198,246,93,59,120,211,91,222,44,118,191,229,109,106,118,235,246,210,231,
-  218,253,241,175,204,205,111,236,153,108,211,56,108,170,193,100,0,85,1,162,11,121,0,249,44,248,71,128,141,23,1,232,
-  79,207,128,38,38,38,94,54,120,241,66,181,212,246,205,55,96,190,244,56,225,190,111,45,174,112,197,34,227,194,27,96,
-  35,240,11,23,252,126,199,5,189,239,97,48,161,247,125,19,5,206,13,98,120,86,103,60,74,14,18,82,159,3,20,117,
-  1,251,249,194,10,231,66,160,249,70,55,197,231,42,158,194,33,80,12,188,94,87,190,222,74,85,149,37,105,225,43,228,
-  45,185,181,196,113,188,250,178,135,97,76,150,37,148,203,17,74,23,132,201,96,112,45,74,235,1,232,20,21,79,24,134,
-  69,64,160,16,171,243,22,231,28,229,114,153,52,43,28,33,117,16,20,195,231,193,133,25,87,248,236,228,185,193,5,97,
-  209,18,250,34,31,76,42,141,115,144,5,33,182,20,147,246,51,116,172,17,145,167,153,88,22,14,31,165,126,233,165,204,
-  182,150,56,126,242,4,179,251,186,252,187,159,248,41,191,227,145,39,91,63,14,103,37,36,119,254,250,31,76,252,247,15,
-  126,124,116,244,134,43,131,55,220,254,70,110,188,114,55,99,205,42,206,27,146,52,37,109,119,56,215,61,74,80,174,48,
-  52,220,164,54,84,71,69,49,129,150,216,188,216,70,122,33,48,214,147,166,61,132,16,92,178,101,138,75,127,252,135,249,
-  161,31,252,62,238,189,255,193,240,19,31,250,232,244,175,126,225,243,19,151,156,56,185,237,38,184,98,2,30,15,96,239,
-  89,120,188,15,199,190,23,218,7,94,58,62,96,47,157,25,80,24,134,47,151,107,245,47,220,23,86,200,231,1,212,11,
-  65,5,252,44,240,79,225,207,21,2,230,20,65,137,63,225,189,247,96,149,49,70,72,141,25,0,14,23,144,249,86,77,
-  199,248,122,210,225,74,43,243,141,159,91,153,15,93,56,59,90,169,128,132,120,78,200,170,68,49,68,214,74,81,173,86,
-  87,205,182,210,52,93,5,193,32,8,232,118,187,24,107,193,187,98,230,19,132,8,160,239,60,195,205,38,149,74,5,147,
-  23,128,83,46,151,87,1,111,229,250,87,6,214,174,184,217,176,198,224,188,71,5,1,38,207,65,136,2,180,156,37,140,
-  66,156,47,172,107,81,2,57,40,216,28,133,206,45,43,149,105,27,199,132,14,232,121,143,207,13,165,249,54,173,83,167,
-  104,14,53,249,157,223,251,48,159,253,253,15,154,239,59,63,159,222,10,173,20,142,59,152,191,3,230,111,233,244,183,61,
-  244,39,247,142,125,248,43,247,133,31,216,185,69,190,229,29,111,17,55,191,234,106,54,76,142,162,149,34,115,57,73,210,
-  102,230,84,155,133,185,136,122,115,152,90,189,73,20,215,48,190,104,73,133,0,169,139,64,73,99,251,216,110,66,41,82,
-  220,254,198,27,120,211,235,110,224,208,177,127,170,63,242,161,15,79,126,241,211,159,27,93,247,216,227,151,236,236,116,174,
-  24,134,7,26,112,87,0,143,77,194,185,114,81,20,95,60,43,0,228,47,142,202,120,62,94,131,112,48,239,249,183,20,
-  100,196,191,236,43,182,1,3,46,4,87,206,140,209,202,96,84,177,14,183,182,80,186,75,33,7,91,46,40,244,97,254,
-  235,170,158,11,183,98,43,45,149,187,192,162,99,5,152,164,148,24,91,228,207,23,218,44,177,10,94,206,58,242,126,74,
-  189,81,35,12,3,74,165,136,110,183,95,8,73,139,80,44,82,147,19,170,194,5,208,218,132,114,169,76,181,90,98,104,
-  104,168,104,159,2,137,28,204,145,86,218,172,149,108,245,149,225,55,23,108,239,140,41,18,73,181,214,104,253,28,123,90,
-  74,137,70,144,217,156,168,28,23,222,217,214,96,28,72,47,17,97,192,49,151,33,90,30,180,39,183,57,97,187,205,204,
-  99,143,179,254,85,215,81,171,14,83,93,88,238,93,9,157,54,4,22,38,34,8,45,148,70,192,191,13,220,205,198,243,
-  208,147,135,236,211,79,254,119,243,133,177,33,185,254,230,107,130,247,220,113,135,216,190,117,35,181,122,165,152,115,101,57,
-  115,167,79,51,119,118,134,90,189,201,208,96,104,141,42,108,79,156,181,104,41,7,105,181,134,44,105,225,17,108,89,63,
-  198,79,255,212,63,98,246,135,254,142,190,251,203,119,79,28,250,131,15,15,143,125,234,51,211,235,178,108,189,133,225,58,
-  124,41,40,110,147,231,221,189,230,132,16,124,94,74,162,151,217,222,77,95,196,224,111,97,144,61,120,1,203,192,239,3,
-  191,53,152,3,125,83,253,239,115,173,154,143,140,177,161,247,244,40,188,52,86,30,216,149,42,167,152,191,200,231,82,64,
-  47,168,124,86,54,75,23,174,227,197,64,114,113,97,251,182,98,249,90,128,128,194,89,183,250,224,59,239,72,211,148,94,
-  175,75,20,133,84,42,101,22,22,22,11,16,9,20,105,154,21,32,87,76,131,190,174,178,202,141,65,34,209,58,88,157,
-  37,73,41,139,54,234,2,64,52,198,224,6,243,158,149,107,14,130,160,152,177,24,51,240,20,95,177,25,113,88,99,81,
-  97,1,186,89,106,8,101,64,28,71,44,215,107,252,201,221,247,177,165,92,162,84,10,176,222,226,45,124,229,143,62,68,
-  180,102,154,242,230,237,165,223,58,125,202,212,148,10,250,121,190,45,200,51,43,173,113,147,222,186,141,144,55,65,93,5,
-  193,109,16,204,205,46,229,159,248,227,47,166,31,104,103,225,219,238,120,135,172,5,158,141,155,214,51,50,82,39,10,53,
-  185,201,232,46,157,167,187,52,71,189,57,76,99,100,132,82,185,134,212,26,235,5,43,221,110,241,227,114,88,215,199,164,
-  142,70,89,241,246,59,222,72,239,246,55,4,15,126,226,115,155,190,248,190,127,216,120,125,167,157,133,112,252,17,152,75,
-  161,255,124,62,120,49,240,39,66,114,94,8,94,110,217,244,223,246,0,228,129,72,73,30,84,146,57,254,108,13,205,159,
-  247,194,157,165,208,127,245,129,95,229,175,94,91,251,98,162,139,242,222,175,12,7,157,181,88,191,162,118,151,120,111,113,
-  206,15,120,65,254,207,229,4,5,90,99,173,35,179,41,184,175,31,151,121,231,86,245,86,197,223,187,160,234,27,128,88,
-  154,166,56,103,201,178,12,41,19,194,48,36,205,50,148,86,148,227,24,188,39,233,39,69,72,98,150,17,13,196,168,54,
-  55,12,53,134,6,3,242,66,130,177,242,245,86,42,177,114,185,24,82,155,60,95,53,81,211,90,127,93,219,168,180,198,
-  91,135,113,133,234,62,237,39,3,43,211,2,124,115,147,161,140,32,170,84,89,123,227,53,36,79,31,163,18,104,84,4,
-  65,47,97,107,86,38,157,235,114,199,149,215,4,217,149,215,12,103,70,144,228,150,110,150,144,184,54,39,90,139,254,104,
-  55,53,203,115,11,44,206,207,243,182,172,231,95,131,15,255,46,248,47,124,229,158,124,230,242,43,213,134,91,111,208,15,
-  238,125,148,102,179,196,174,93,219,25,31,29,198,228,133,60,163,179,180,64,187,181,72,165,58,68,115,100,156,32,174,160,
-  244,115,206,141,120,7,162,208,240,121,44,214,244,137,133,226,213,239,125,23,31,255,216,167,135,143,127,248,143,182,109,133,
-  53,195,16,206,64,127,246,175,112,175,253,101,167,2,220,163,95,158,143,242,183,61,0,5,74,210,109,117,249,254,133,54,
-  135,94,12,0,116,30,161,240,74,73,164,215,224,4,94,184,1,232,20,182,173,5,109,71,252,185,43,120,231,139,249,141,
-  240,224,132,28,232,187,184,224,255,23,155,45,53,144,118,172,240,129,164,44,196,160,94,21,153,92,248,162,85,11,130,162,
-  98,169,148,203,24,99,200,179,140,74,165,138,119,158,60,45,6,201,231,207,159,103,124,108,12,45,53,253,126,151,56,46,
-  97,109,78,191,223,93,173,12,86,218,176,110,183,139,28,180,103,43,109,151,25,16,27,87,255,77,206,225,114,143,213,26,
-  179,50,243,202,29,65,24,144,123,67,63,237,145,230,93,84,185,66,88,110,224,119,239,96,254,236,25,154,89,74,179,84,
-  163,30,164,88,44,198,88,60,1,78,121,188,4,89,142,208,97,9,53,57,41,188,44,5,29,239,57,221,235,241,216,147,
-  143,219,108,255,227,217,171,33,120,131,181,250,171,191,244,203,233,145,74,32,174,127,235,237,234,231,255,213,191,226,212,47,
-  189,159,55,191,243,173,220,254,166,55,178,97,106,2,157,165,100,54,161,215,94,162,215,94,34,46,149,169,52,134,9,226,
-  50,97,169,134,14,138,71,201,228,30,47,5,194,91,156,203,41,137,136,225,45,27,57,5,149,237,69,66,143,254,183,192,
-  195,207,235,122,70,18,73,73,153,151,31,241,241,219,26,128,60,80,137,67,126,243,179,15,114,232,204,220,139,118,25,66,
-  12,36,9,185,27,108,127,28,97,20,15,34,101,190,222,110,227,27,231,86,43,15,112,150,21,38,235,74,107,124,158,35,
-  7,131,95,9,127,106,118,100,76,65,62,92,221,168,249,98,104,92,138,99,164,145,171,179,155,44,203,138,234,42,12,177,
-  206,210,104,52,232,180,59,197,32,217,57,90,157,54,227,163,99,116,58,93,178,44,167,86,171,161,117,80,248,74,43,69,
-  167,211,193,90,75,189,94,39,138,99,202,23,108,197,46,188,30,127,129,207,245,133,28,40,147,231,228,89,182,218,190,9,
-  20,50,181,132,161,194,106,73,180,118,18,211,110,49,179,208,98,56,44,19,90,139,201,186,8,219,67,9,6,255,254,16,
-  155,129,145,2,71,70,37,210,92,90,41,177,254,218,107,212,93,101,229,245,99,143,154,219,156,87,183,122,252,190,255,244,
-  43,221,127,241,91,31,168,150,206,204,38,63,236,236,210,131,255,249,183,74,255,243,247,63,86,222,244,246,215,68,183,188,
-  245,118,118,109,216,136,179,69,234,109,175,219,163,211,238,34,164,162,92,169,50,52,60,76,92,107,12,6,238,131,159,13,
-  197,155,67,150,246,40,13,8,16,226,121,54,42,147,80,252,220,121,121,178,174,181,248,38,6,176,94,136,87,36,0,5,
-  74,114,126,169,195,175,124,252,158,23,251,82,68,97,77,33,73,243,108,176,249,178,95,183,126,191,208,120,236,194,141,88,
-  33,43,176,72,245,156,220,66,172,204,89,46,96,76,127,35,41,49,207,243,213,25,204,202,124,103,69,201,94,171,213,240,
-  222,211,233,116,200,178,98,254,179,242,255,148,86,197,60,202,88,250,189,62,105,154,49,52,52,132,177,197,26,62,8,10,
-  131,177,44,203,8,195,112,181,101,76,211,20,165,53,81,20,173,126,223,149,107,88,81,213,139,32,40,18,63,6,169,31,
-  22,86,171,55,49,232,29,51,151,97,41,252,137,164,128,160,62,132,13,74,156,57,55,79,61,205,9,130,136,72,43,148,
-  183,8,111,139,167,61,136,8,61,4,222,146,134,142,52,55,52,156,224,77,55,222,166,127,119,126,81,76,30,63,154,238,
-  128,238,206,126,178,212,56,117,46,105,192,137,38,254,216,110,208,189,211,11,235,246,255,234,71,182,253,218,135,62,191,97,
-  252,246,215,69,111,127,215,59,216,181,121,35,18,75,154,116,192,102,244,151,231,232,46,207,19,86,134,168,214,170,84,135,
-  234,232,48,196,91,71,47,203,232,236,223,239,47,131,118,6,61,1,246,223,82,240,197,190,85,187,212,50,112,191,84,252,
-  162,210,168,151,169,232,67,39,165,242,95,8,60,65,158,17,89,59,88,133,190,114,54,102,43,213,207,175,127,250,254,23,
-  179,250,1,16,218,58,167,115,235,173,64,104,165,48,3,34,226,133,91,172,11,43,134,149,7,248,194,85,188,86,186,0,
-  145,85,169,131,127,14,136,6,9,15,43,132,192,21,146,224,202,223,245,222,19,68,17,146,194,42,35,77,83,196,96,61,
-  190,2,122,121,158,175,242,123,242,44,39,142,34,154,205,38,105,150,50,51,151,16,40,141,51,150,48,140,104,54,203,116,
-  187,93,172,181,12,13,13,81,169,20,6,102,43,213,207,115,4,199,231,24,217,43,215,185,250,206,254,13,215,30,234,0,
-  165,4,137,183,164,38,69,101,80,139,74,32,53,190,18,163,55,213,233,44,204,211,95,92,32,76,44,85,52,35,213,58,
-  145,214,160,52,62,235,19,121,131,18,30,167,138,8,160,82,14,187,119,95,169,62,114,234,36,255,208,26,91,129,100,10,
-  191,15,184,179,15,143,74,200,53,172,191,26,174,222,62,223,185,254,238,15,124,124,215,255,252,228,159,76,172,123,211,107,
-  195,219,239,120,27,187,119,108,36,150,96,178,132,220,102,244,91,243,116,150,102,40,45,86,89,187,126,19,165,210,16,79,
-  28,61,65,250,208,195,189,13,112,58,133,153,12,50,1,12,125,139,55,141,2,122,192,31,234,96,53,212,242,101,9,64,
-  139,163,227,127,209,251,50,42,203,16,199,15,83,81,154,106,20,21,171,217,139,213,207,243,141,65,133,127,179,181,200,149,
-  182,228,2,87,196,213,85,246,224,172,0,193,202,92,200,88,75,150,103,148,227,210,234,223,231,207,40,201,173,181,95,71,
-  62,52,198,20,45,214,160,10,209,58,160,215,235,173,206,110,194,48,252,58,80,200,178,172,112,47,140,99,194,32,192,56,
-  135,25,84,72,65,89,19,14,192,107,121,185,181,186,142,95,1,62,231,28,225,192,7,104,5,120,130,32,40,4,173,131,
-  86,111,229,123,58,107,49,214,226,173,45,102,71,82,22,57,97,74,17,138,194,167,40,136,53,214,184,194,168,222,56,4,
-  158,225,13,27,17,155,54,211,106,183,56,115,226,56,159,190,243,43,92,177,121,11,187,166,167,25,174,213,200,136,145,73,
-  6,202,145,75,143,119,41,87,111,216,204,3,155,182,198,31,59,122,176,247,110,107,235,17,76,8,40,171,66,175,119,92,
-  192,225,12,14,68,240,212,91,224,154,155,91,189,171,30,253,208,167,182,253,241,231,191,60,246,241,27,175,41,95,255,166,
-  215,113,205,158,61,140,52,106,64,27,233,37,189,78,135,94,187,71,80,26,229,253,255,241,151,217,50,191,56,171,225,89,
-  3,167,114,72,119,125,139,237,146,167,144,246,252,11,29,240,172,148,148,95,206,0,244,23,201,15,132,16,24,33,216,55,
-  119,158,208,123,174,153,156,166,30,69,171,67,194,151,123,245,243,27,159,121,128,163,231,22,94,244,203,177,66,8,39,165,
-  80,30,172,53,8,41,86,3,9,87,230,49,43,60,153,11,213,241,43,109,145,16,130,64,7,132,65,56,112,31,20,44,
-  181,90,36,105,178,250,103,87,248,54,223,56,83,242,222,147,36,9,161,14,8,148,94,101,91,175,104,192,210,52,45,228,
-  11,131,207,25,99,80,90,17,199,49,73,146,32,7,127,206,24,75,187,221,38,73,82,148,210,12,15,15,147,166,41,73,
-  146,16,69,17,149,106,117,149,39,116,225,28,107,101,48,141,128,204,24,88,201,70,27,208,10,156,115,88,99,176,74,34,
-  188,165,174,52,10,137,183,158,92,128,177,25,101,25,18,7,1,253,110,11,39,5,165,106,137,203,110,188,142,153,229,121,
-  62,253,135,31,92,122,168,86,209,217,186,245,213,87,109,187,148,219,54,108,2,219,199,5,142,52,119,228,121,198,27,111,
-  121,141,252,108,84,105,250,220,149,166,142,62,91,190,62,239,165,85,88,246,208,139,225,204,60,28,140,97,198,195,254,18,
-  60,122,11,92,117,77,171,183,231,233,207,221,181,229,107,95,184,107,242,163,219,54,85,119,220,114,179,184,241,198,43,217,
-  186,101,35,81,37,226,248,92,139,255,253,239,126,146,214,7,254,168,117,19,60,154,194,163,17,204,236,1,103,249,214,34,
-  186,106,192,71,164,226,215,180,38,126,153,235,237,245,55,243,184,134,74,145,36,9,123,103,206,112,205,196,154,151,61,8,
-  5,74,50,187,220,125,137,84,63,224,149,20,102,144,199,190,210,130,173,156,231,136,133,114,53,177,211,185,231,126,191,2,
-  64,165,82,9,29,232,213,53,249,138,126,235,194,170,105,5,92,190,145,45,29,199,49,12,86,231,113,28,99,140,89,229,
-  243,36,73,66,150,101,171,85,138,16,130,126,82,172,227,123,221,238,115,178,139,82,137,82,41,166,217,28,161,84,42,175,
-  126,191,21,64,188,112,139,247,156,164,196,13,218,61,57,248,40,180,95,43,219,58,187,210,98,42,133,196,33,241,244,77,
-  94,132,57,170,112,224,68,233,201,92,74,181,86,98,106,106,130,109,27,54,50,57,62,78,185,28,115,229,213,187,249,103,
-  143,61,232,174,127,246,96,251,232,179,79,199,159,61,114,84,61,178,126,147,104,148,34,122,121,215,39,121,207,118,50,39,
-  95,117,253,205,242,159,252,227,31,86,111,254,142,247,86,62,252,145,79,85,62,243,207,126,252,198,239,94,94,232,89,200,
-  186,112,207,56,156,186,15,22,215,64,123,4,78,59,120,42,128,173,87,193,206,43,28,123,142,62,123,116,231,221,207,30,
-  93,251,137,15,124,184,220,216,48,173,171,99,195,114,225,216,73,179,246,228,217,243,223,9,15,123,248,140,135,71,66,104,
-  111,124,158,238,153,223,209,26,67,65,128,125,133,3,80,49,190,87,82,146,90,251,178,7,161,11,171,159,35,103,231,95,
-  2,205,23,3,134,113,225,137,140,179,5,33,111,181,109,242,3,23,65,143,16,138,21,73,156,181,102,181,34,82,74,21,
-  51,163,129,190,42,140,34,98,91,72,30,86,102,40,23,146,26,87,102,44,23,182,117,74,107,162,32,92,213,128,93,56,
-  35,42,170,167,149,245,127,33,82,93,94,90,34,10,67,70,199,70,49,166,216,144,85,202,85,226,56,94,117,67,92,249,
-  58,0,206,219,85,194,30,40,172,117,200,65,108,178,247,14,99,115,132,20,88,87,84,117,129,14,144,206,15,0,78,17,
-  73,79,20,72,140,82,244,83,67,158,230,140,12,85,152,94,51,194,182,29,219,216,180,125,51,81,169,136,146,206,109,74,
-  47,111,177,110,253,8,59,223,125,199,240,167,254,227,47,219,255,98,77,247,157,182,191,116,239,129,167,105,67,84,3,166,
-  192,30,17,68,95,242,95,27,254,251,255,224,45,162,119,226,253,220,241,234,75,248,204,85,219,26,31,253,202,3,55,222,
-  1,202,67,156,194,23,119,192,201,113,48,1,44,204,193,114,2,199,203,240,88,14,15,108,133,93,219,96,107,222,239,79,
-  183,247,31,26,207,246,67,13,206,85,224,217,12,30,1,30,11,225,204,231,192,206,62,15,131,231,95,15,66,190,58,88,
-  187,243,237,0,64,171,55,169,16,47,123,16,10,148,228,196,220,50,255,253,37,80,253,200,1,32,42,231,188,114,6,227,
-  37,88,139,210,133,77,134,117,190,16,66,122,0,183,106,82,182,82,65,88,187,226,52,168,233,39,253,213,200,158,114,92,
-  194,187,162,154,114,23,180,50,23,174,189,47,156,237,24,99,40,85,170,3,50,227,0,12,7,171,246,48,8,80,170,144,
-  216,22,12,103,69,146,244,241,222,19,199,49,253,164,143,247,20,145,205,81,12,136,85,250,192,74,139,24,4,33,73,210,
-  71,135,146,56,138,113,206,99,140,69,41,143,84,69,88,99,160,53,121,102,200,93,159,40,10,16,88,130,32,6,95,24,
-  154,73,41,233,247,123,116,146,46,211,235,183,113,217,149,87,176,235,210,73,154,67,18,69,72,234,13,137,73,208,94,17,
-  34,137,116,128,142,52,239,125,199,155,184,235,127,255,150,56,63,187,112,126,18,238,122,7,156,0,170,22,106,30,26,151,
-  122,54,29,62,55,115,233,71,62,252,209,202,143,191,117,138,118,251,52,127,247,125,63,192,151,47,191,97,234,19,239,255,
-  213,87,191,57,75,76,6,231,170,5,240,180,119,3,71,192,222,83,84,51,157,75,96,38,135,103,162,194,113,117,172,2,
-  99,213,34,63,242,188,129,51,227,48,235,160,253,24,216,127,241,188,220,192,33,82,169,87,4,248,252,149,1,232,149,0,
-  66,97,24,240,236,161,67,4,103,231,185,134,23,135,59,145,3,235,10,0,114,30,242,178,148,121,5,225,164,144,82,201,
-  16,111,61,94,8,4,69,91,226,188,43,204,213,97,96,105,186,146,152,250,28,49,49,205,178,213,121,141,28,56,24,22,
-  182,30,174,72,85,85,10,86,140,205,190,193,95,122,165,82,113,74,81,138,99,122,189,222,160,106,241,180,187,221,162,170,
-  137,34,164,42,230,81,73,146,160,6,109,185,119,80,169,214,88,88,88,164,20,198,171,121,101,245,122,189,168,148,150,151,
-  7,2,219,18,56,137,53,14,200,9,2,133,195,98,93,78,16,104,164,15,80,65,76,183,215,34,233,165,148,203,133,65,
-  126,28,133,244,122,29,146,92,48,181,118,45,183,223,188,155,157,59,182,83,137,99,108,178,64,150,245,232,199,160,100,76,
-  77,14,227,115,199,252,249,57,14,30,61,194,211,207,236,231,238,251,31,33,201,145,22,102,45,220,99,225,222,160,160,8,
-  197,22,154,33,92,247,247,50,155,254,193,175,255,201,213,223,243,213,241,202,223,249,222,157,124,215,119,220,192,77,215,190,
-  153,127,121,238,224,240,129,63,248,244,158,61,112,99,15,22,53,28,59,12,73,21,242,157,144,204,21,233,39,253,95,131,
-  126,6,51,215,66,16,13,186,162,37,200,230,32,255,106,16,184,207,72,197,130,27,36,226,126,43,15,171,82,104,165,94,
-  81,46,139,127,45,34,226,133,32,116,245,196,20,141,40,126,89,128,144,7,114,60,151,126,97,47,159,132,23,53,182,214,
-  3,45,48,61,88,172,88,63,55,153,154,116,127,160,75,2,95,8,30,157,69,42,139,195,226,68,134,20,1,160,159,99,
-  15,15,88,109,43,131,97,6,9,170,105,154,210,237,245,208,97,161,100,247,30,114,155,23,22,172,43,149,208,160,197,91,
-  5,161,129,236,220,230,134,174,237,98,108,1,50,214,59,116,16,96,156,165,213,237,16,5,69,164,78,16,20,155,42,173,
-  52,81,41,38,208,97,177,129,27,172,247,251,253,62,81,20,97,173,45,140,225,195,16,103,60,46,7,235,13,65,168,209,
-  129,68,74,141,201,13,89,102,16,164,4,2,130,64,98,93,136,115,138,126,63,37,142,45,219,47,217,202,158,203,47,99,
-  106,253,24,97,212,71,164,51,244,211,24,194,18,65,60,74,144,37,156,57,113,130,47,127,249,94,190,116,247,221,28,217,
-  255,172,239,159,62,237,242,86,151,27,44,173,239,86,122,46,18,242,73,227,221,193,16,142,29,131,52,2,49,5,97,2,
-  179,35,96,126,36,237,155,255,249,196,241,171,14,189,118,199,80,126,250,113,76,247,126,242,236,188,127,6,134,119,194,107,
-  203,48,12,28,113,176,32,96,113,35,156,222,10,167,247,194,252,113,72,26,66,184,159,10,163,172,3,249,15,228,153,187,
-  196,57,150,148,226,95,198,101,132,41,94,255,146,82,223,242,61,243,74,147,142,255,181,153,208,43,32,244,200,204,89,174,
-  126,153,84,66,58,10,88,126,234,56,159,187,247,41,190,213,77,196,183,122,28,176,6,242,6,44,74,239,231,107,34,207,
-  188,84,37,107,93,193,109,241,224,68,113,203,121,10,79,26,37,2,172,179,171,36,197,149,70,206,11,6,214,167,197,170,
-  60,73,18,170,213,234,64,237,110,10,112,241,30,59,224,232,10,64,12,92,8,205,10,25,208,59,210,204,80,42,151,209,
-  50,192,38,253,213,25,146,14,3,74,149,50,194,250,194,170,85,7,104,21,210,168,55,128,149,149,126,78,150,166,212,234,
-  117,226,184,96,113,175,172,214,87,54,119,133,252,162,184,0,33,21,94,56,180,10,9,116,76,150,118,73,109,138,195,208,
-  79,18,70,170,99,92,190,231,50,118,239,222,196,80,67,131,207,112,189,51,228,46,162,92,170,162,136,56,125,126,129,7,
-  31,122,148,135,191,120,15,247,222,251,0,237,67,207,158,127,167,243,226,53,208,108,20,65,145,167,198,224,17,97,205,163,
-  25,60,49,139,56,50,142,207,239,4,223,0,95,133,228,237,112,184,13,159,15,32,248,187,80,251,245,223,249,218,149,191,
-  184,212,209,239,124,245,110,126,228,123,222,34,62,89,154,174,253,183,15,124,114,247,20,118,203,8,180,154,208,217,2,115,
-  163,5,24,61,213,133,189,111,131,103,119,121,191,52,158,103,238,7,117,192,175,232,128,255,145,165,133,252,5,79,252,10,
-  5,143,23,21,128,86,64,40,177,150,135,103,206,188,44,64,168,36,37,127,231,143,239,226,99,238,165,113,43,252,12,248,
-  119,64,138,16,169,243,194,247,123,125,74,104,130,48,32,80,197,107,233,188,67,136,98,51,229,165,89,29,32,251,129,183,
-  143,25,200,46,16,197,0,214,73,48,206,162,164,36,138,35,108,226,209,66,224,6,62,60,43,44,99,49,200,85,151,162,
-  152,217,36,64,32,21,153,49,36,105,178,170,176,95,33,51,166,73,2,214,83,138,34,156,43,170,176,44,179,152,60,33,
-  77,18,26,245,6,89,150,145,231,249,42,137,113,197,107,202,185,162,133,244,88,16,154,60,55,228,166,88,193,91,45,72,
-  83,75,210,79,200,146,62,195,163,13,110,126,245,213,92,118,217,38,234,117,112,121,139,180,231,169,149,27,120,17,178,208,
-  106,243,244,147,251,216,251,213,175,241,209,79,126,129,103,79,46,32,109,137,53,19,147,184,205,219,199,190,60,59,107,190,
-  103,121,222,173,135,52,135,253,105,225,130,123,95,13,102,103,241,253,35,96,195,193,48,247,35,192,195,144,254,12,28,233,
-  195,87,134,96,226,187,22,186,67,255,245,195,15,109,126,253,235,47,83,55,110,232,81,249,225,55,197,15,204,44,199,229,
-  205,67,53,35,237,216,253,135,78,241,224,153,37,187,246,224,169,43,47,207,179,107,55,194,14,15,95,217,15,15,237,113,
-  238,204,127,201,179,236,45,66,176,3,248,80,209,44,191,92,177,65,92,80,120,189,96,33,175,223,178,22,236,229,2,66,
-  229,40,224,158,167,142,241,241,123,247,189,116,230,81,131,159,172,245,222,13,85,70,184,227,134,91,56,127,234,56,39,79,
-  157,98,126,113,1,235,28,106,176,90,71,136,162,101,49,22,196,96,51,38,4,114,32,236,92,49,154,95,145,56,204,46,
-  204,23,85,135,181,69,5,165,20,62,207,87,239,38,99,12,65,24,174,126,222,172,16,26,173,193,243,28,119,72,43,133,
-  183,174,16,60,70,17,245,198,16,189,110,159,40,140,137,194,8,194,152,102,163,73,185,84,194,154,156,94,191,143,181,142,
-  40,140,144,82,17,4,186,176,243,144,128,40,130,13,165,16,100,89,138,201,45,222,27,22,151,22,169,148,170,220,122,203,
-  205,92,127,243,46,42,117,135,164,143,233,89,170,97,21,33,60,7,159,58,206,99,251,246,99,204,97,166,226,156,93,99,
-  53,254,160,157,210,177,33,168,156,222,153,131,84,202,195,34,155,88,23,252,198,248,26,110,60,188,47,122,151,115,53,9,
-  210,67,107,45,116,52,69,100,114,95,8,62,161,20,39,164,226,113,224,90,147,39,55,57,119,208,192,87,182,192,212,107,
-  114,51,242,192,190,83,195,19,165,53,212,70,207,242,254,127,249,86,198,194,243,184,126,159,115,157,173,60,125,90,168,143,
-  124,234,145,225,79,30,57,54,180,235,192,209,137,215,56,214,120,24,202,225,75,175,247,254,68,201,123,115,39,80,113,150,
-  205,121,202,17,196,75,118,93,190,220,237,253,69,29,223,243,14,56,207,59,0,125,35,8,93,245,18,157,9,73,41,249,
-  111,127,124,23,47,37,3,182,213,85,59,176,102,88,249,31,253,185,31,36,112,158,179,167,207,243,228,190,131,60,241,196,
-  1,158,126,250,8,231,206,44,176,208,89,160,155,247,11,195,116,64,106,81,196,15,11,129,148,226,185,8,6,241,220,102,
-  107,176,59,195,251,226,123,72,165,80,131,129,179,31,172,236,173,115,152,34,178,162,120,109,132,64,105,133,51,6,41,6,
-  18,16,231,7,9,164,125,250,253,136,56,46,209,168,15,13,64,199,144,39,41,222,121,162,168,112,163,9,131,136,36,201,
-  16,162,79,181,82,37,10,74,228,62,195,120,135,55,197,32,189,223,239,210,235,181,25,159,104,240,250,107,175,227,154,43,
-  119,210,108,150,112,174,143,235,41,194,64,98,179,148,71,246,222,207,222,251,30,65,36,125,214,142,198,92,186,103,148,201,
-  24,194,160,201,77,87,93,198,19,159,219,75,32,21,129,14,232,245,251,204,31,120,134,109,59,118,211,122,251,119,213,14,
-  127,250,195,155,182,155,124,171,131,198,211,48,127,100,0,250,29,33,120,36,8,87,95,176,223,6,46,201,210,214,4,60,
-  147,195,179,187,83,119,205,127,251,181,7,154,187,214,191,87,92,219,60,67,205,159,101,230,76,153,251,246,206,185,110,119,
-  33,247,113,69,93,187,103,90,230,175,218,36,79,158,216,61,245,135,191,253,169,215,124,167,65,73,104,117,161,245,155,48,
-  255,223,87,122,231,36,1,41,249,86,114,87,180,14,8,165,228,149,120,158,55,53,252,115,32,116,246,37,55,152,126,41,
-  86,63,223,136,69,165,190,53,89,43,161,90,215,172,95,223,228,146,109,183,241,29,239,126,35,189,94,198,249,243,203,28,
-  58,126,150,199,158,216,199,190,125,207,112,226,212,25,102,103,103,233,116,123,197,170,94,7,40,93,84,24,214,121,148,28,
-  172,218,189,31,100,204,131,23,133,95,141,148,10,132,68,249,130,173,236,173,99,121,121,169,168,182,6,51,155,162,109,242,
-  148,74,33,194,67,160,20,81,41,38,203,115,148,82,196,113,72,28,5,148,74,49,54,203,11,189,21,80,138,203,68,81,
-  145,128,154,229,25,198,228,116,187,109,194,40,164,52,112,56,236,251,156,118,171,205,182,205,235,216,181,115,61,59,118,108,
-  64,71,2,159,167,100,253,46,81,88,69,88,120,230,169,103,57,250,204,83,44,159,63,202,166,137,42,215,108,158,102,88,
-  65,31,129,109,39,16,119,184,114,235,56,13,105,232,231,21,146,204,80,170,8,94,123,251,237,124,255,223,254,219,56,169,
-  120,232,115,31,171,237,52,249,176,134,210,62,144,191,9,174,60,104,9,127,60,77,248,62,10,61,85,213,123,22,138,185,
-  156,14,32,90,130,224,53,55,111,23,87,239,18,132,153,225,177,19,146,255,245,191,62,213,25,222,123,236,204,22,108,39,
-  131,176,23,4,149,71,26,209,248,219,127,234,7,43,251,191,247,13,245,79,253,223,63,185,246,61,112,220,194,145,119,66,
-  235,198,129,230,11,88,245,64,250,235,156,8,120,214,26,126,90,134,175,152,213,251,11,2,64,207,13,166,205,75,111,48,
-  45,37,255,249,99,119,243,82,179,159,29,100,71,121,128,48,199,229,169,67,250,12,227,60,173,110,159,178,74,169,6,138,
-  242,150,113,182,93,178,134,119,188,241,122,122,153,103,113,185,199,204,217,57,158,126,230,48,143,61,185,159,253,135,79,114,
-  244,228,97,58,221,101,146,126,66,55,77,87,13,194,180,214,232,32,64,135,18,231,115,108,238,8,116,132,240,2,155,102,
-  140,142,12,163,177,180,59,61,140,19,152,44,47,120,63,130,130,91,100,45,181,106,21,237,61,141,161,38,121,47,1,231,
-  10,65,129,205,129,194,30,67,233,0,99,115,66,17,20,148,1,239,168,85,202,131,86,78,146,39,41,189,212,18,84,43,
-  252,173,191,251,119,120,253,171,175,98,241,244,179,180,151,207,97,115,11,178,132,14,170,156,61,113,130,3,143,239,133,229,
-  57,46,223,56,198,186,29,187,136,108,135,60,239,208,77,5,58,140,144,218,227,130,156,53,235,70,169,197,138,161,250,90,
-  110,188,241,90,94,247,218,107,185,100,219,86,166,166,198,217,251,212,126,78,8,74,14,154,6,234,59,65,253,123,112,43,
-  117,196,144,115,104,10,81,104,27,8,32,246,176,193,193,182,131,181,234,200,123,223,179,142,178,106,243,248,137,26,63,250,
-  51,127,96,222,61,51,127,250,45,112,46,131,57,9,202,230,249,154,225,185,220,63,254,201,47,175,249,225,159,121,79,252,
-  75,143,31,31,59,249,216,129,61,83,176,41,132,103,95,15,89,157,111,221,123,85,2,111,177,150,131,210,242,155,175,32,
-  254,207,11,2,64,5,8,73,18,107,94,50,51,33,161,36,203,173,46,127,231,240,105,254,22,47,238,234,253,27,183,96,
-  235,138,119,224,60,130,190,233,206,219,133,222,105,182,143,94,70,63,53,24,23,16,226,193,165,96,139,253,149,140,37,37,
-  37,40,85,42,76,79,87,185,241,150,93,228,230,29,116,251,134,153,185,101,102,231,218,204,205,206,179,180,208,97,110,118,
-  145,67,135,15,242,196,83,143,49,59,127,142,94,43,39,75,66,156,205,232,185,148,74,53,30,196,225,8,100,32,105,12,
-  213,201,50,7,222,19,69,33,81,20,22,121,240,214,210,235,39,40,175,177,61,195,216,240,36,54,207,81,104,132,83,164,
-  73,74,46,28,97,12,113,37,68,40,64,73,74,213,42,30,65,110,44,66,132,76,172,27,231,85,183,188,138,43,174,190,
-  154,230,112,19,103,251,184,74,147,172,211,33,10,97,105,126,150,39,31,250,2,233,204,73,182,143,196,108,219,53,68,228,
-  123,100,221,54,125,93,69,69,138,168,98,177,193,16,157,4,206,156,237,112,190,167,248,161,31,122,31,59,118,92,205,244,
-  250,41,234,83,67,52,27,53,234,181,26,181,99,39,232,70,81,216,79,250,227,33,140,70,16,156,129,188,53,232,82,175,
-  133,85,251,210,62,176,4,141,26,108,159,129,45,126,199,198,242,214,181,195,56,99,249,195,63,121,138,205,51,243,11,111,
-  132,51,125,184,211,193,163,190,16,171,190,102,23,92,245,216,177,153,106,88,169,197,239,248,225,55,203,131,63,122,112,98,
-  26,63,145,64,105,14,90,125,158,31,243,231,18,240,3,38,231,247,47,240,120,186,8,64,47,19,16,82,129,102,249,83,
-  247,33,206,46,80,127,137,189,248,75,224,71,160,229,224,240,68,43,61,254,197,159,253,207,99,71,222,246,198,96,237,101,
-  151,49,189,101,51,107,38,155,84,130,10,206,148,16,190,87,188,243,187,0,239,37,194,123,92,158,161,165,160,57,164,24,
-  25,31,65,134,235,64,86,128,106,241,158,110,61,203,115,243,204,156,159,225,244,241,147,156,59,125,134,133,197,5,22,151,
-  22,57,112,240,8,7,15,30,165,223,205,9,196,40,82,25,202,129,193,26,75,185,84,42,152,206,229,10,90,199,248,33,
-  65,185,20,161,164,160,217,24,90,181,103,53,38,47,12,208,188,199,102,20,13,12,26,29,133,12,141,55,25,30,31,102,
-  199,158,29,236,217,115,41,107,38,199,208,10,200,115,76,190,128,144,154,201,233,13,152,126,151,175,253,201,39,57,247,212,
-  35,108,31,42,179,253,146,181,52,117,15,210,22,97,92,33,168,213,105,59,152,79,60,179,11,17,199,206,205,179,212,17,
-  120,93,195,196,13,202,205,38,135,15,31,98,219,246,181,172,221,48,129,73,83,114,147,80,174,196,196,113,232,252,50,101,
-  89,176,158,245,135,128,227,131,155,94,81,100,116,101,69,155,35,43,208,20,176,105,6,198,235,155,135,85,197,58,206,46,
-  88,14,239,61,216,251,91,112,198,195,147,192,231,66,120,58,131,97,15,211,18,182,145,137,94,167,103,217,181,125,130,211,
-  235,215,214,237,137,147,227,10,170,15,193,121,247,60,154,143,85,188,231,173,214,240,81,165,137,46,2,208,95,13,132,174,
-  154,88,67,227,69,2,161,220,57,54,125,249,209,213,160,192,151,218,121,12,150,22,224,225,245,48,58,250,196,209,185,51,
-  79,252,218,244,113,21,12,221,63,61,217,172,110,24,11,118,95,125,169,26,90,187,46,104,108,89,175,171,155,214,211,156,
-  154,162,28,7,196,74,128,177,24,11,121,90,184,41,138,164,131,84,41,82,46,35,84,33,157,104,140,72,26,195,35,92,
-  178,163,9,110,119,161,53,203,45,75,203,93,14,30,60,202,145,67,39,201,82,79,146,246,73,211,62,46,55,132,81,136,
-  7,180,46,17,148,234,228,22,112,14,147,246,65,72,194,40,64,202,98,125,223,235,117,25,30,25,97,253,166,205,148,235,
-  85,42,165,50,35,163,195,140,142,14,17,70,65,65,114,204,19,114,103,201,16,132,74,162,163,18,249,82,139,71,239,187,
-  135,199,239,254,28,81,119,150,119,92,191,141,122,4,157,158,229,124,94,165,151,151,56,191,208,99,190,183,68,39,243,88,
-  57,198,82,94,195,232,26,178,38,112,222,210,179,125,230,22,12,19,149,17,102,230,219,172,179,125,16,69,182,89,179,217,
-  192,53,234,42,155,57,95,45,193,120,31,42,191,3,173,234,224,117,207,40,76,193,84,241,95,113,2,2,15,37,1,106,
-  98,44,64,100,158,115,231,19,162,153,197,246,58,88,202,225,176,131,19,37,232,100,80,22,144,59,240,125,107,92,102,202,
-  212,42,150,160,86,14,129,161,8,74,29,16,191,8,254,249,2,11,1,100,121,14,66,62,151,219,125,17,128,190,89,16,
-  58,205,213,19,211,47,26,8,189,148,207,233,98,88,121,80,67,191,12,79,108,133,41,97,243,9,119,226,228,100,126,226,
-  100,57,189,251,145,242,18,140,207,73,57,121,118,98,108,124,97,199,182,70,237,242,75,163,137,93,219,88,187,101,3,155,
-  54,173,103,120,184,73,41,174,33,133,192,186,20,99,83,242,172,143,115,16,170,24,41,52,74,4,152,172,208,120,153,172,
-  131,119,41,91,54,142,178,109,227,40,89,154,145,228,9,72,135,207,13,43,89,238,65,92,103,106,211,118,116,92,1,163,
-  193,40,208,2,148,24,176,25,87,212,108,114,208,108,12,118,61,222,227,141,199,153,12,225,138,73,70,16,133,32,115,206,
-  30,62,204,157,95,252,10,15,63,242,4,103,142,159,98,251,250,73,154,18,126,251,147,79,208,233,91,90,189,46,34,170,
-  224,130,58,34,26,162,52,212,164,57,220,68,42,69,234,50,140,77,17,153,65,24,143,42,135,32,83,122,249,50,139,173,
-  5,188,243,120,103,9,116,68,164,20,163,55,222,24,62,113,224,200,228,171,113,219,37,76,157,128,217,93,131,139,156,3,
-  190,60,184,242,16,220,24,44,43,56,85,130,229,212,48,153,101,57,11,243,243,100,89,38,6,149,140,5,92,225,51,128,
-  22,16,166,160,109,185,92,174,212,170,120,221,193,24,231,11,168,198,103,3,144,123,62,89,64,10,216,232,28,39,165,124,
-  197,120,41,235,237,215,125,255,95,250,135,118,190,234,7,94,222,255,202,183,252,244,243,240,22,244,194,16,202,230,193,111,
-  129,238,52,28,122,22,78,54,10,141,82,213,65,173,4,97,0,113,21,38,112,110,211,248,217,153,141,253,179,51,107,231,
-  190,242,181,169,115,48,186,175,82,26,90,90,51,94,141,47,217,16,213,182,92,34,215,93,186,135,77,91,55,177,110,221,
-  52,227,99,163,212,235,53,4,10,172,7,161,81,218,16,153,20,163,20,221,238,50,253,36,37,75,250,56,151,99,173,195,
-  24,139,205,179,2,95,148,38,51,125,122,173,46,37,81,65,72,139,84,249,64,28,43,112,214,32,132,71,72,5,126,224,
-  130,236,44,48,176,214,208,234,185,192,196,52,229,169,135,30,228,83,159,248,20,159,252,212,167,121,246,192,81,134,134,39,
-  153,94,179,129,67,39,159,198,165,9,65,24,144,89,75,158,24,188,235,208,28,73,89,187,62,160,81,30,161,155,229,100,
-  157,22,82,116,113,222,19,251,18,37,221,192,102,14,173,61,129,43,234,17,225,11,78,212,137,163,135,57,125,242,60,187,
-  174,186,150,15,127,244,19,35,87,183,150,54,150,97,106,1,246,255,22,152,54,69,60,242,123,41,34,109,90,224,247,22,
-  18,139,103,134,224,248,249,179,201,150,174,117,186,82,18,68,165,216,38,105,55,174,192,90,9,19,14,18,9,163,10,198,
-  150,160,22,143,143,212,167,39,154,28,63,118,142,116,102,174,39,97,182,15,221,119,130,255,199,23,51,247,94,220,10,232,
-  226,249,230,78,13,168,131,61,6,189,59,160,167,96,113,22,196,99,32,42,69,193,29,55,160,238,97,36,132,241,13,48,
-  181,9,166,124,183,191,38,59,120,124,124,249,224,241,137,51,220,53,118,66,136,230,61,81,84,203,214,175,47,169,177,97,
-  61,178,101,61,83,91,215,203,203,118,109,102,108,98,140,198,240,48,205,161,97,170,113,137,122,189,74,181,86,34,203,250,
-  180,219,203,244,218,93,132,19,104,237,112,206,14,12,226,193,59,129,24,36,138,131,197,123,135,31,176,154,11,242,163,67,
-  72,141,64,22,149,17,142,180,215,102,97,118,142,179,167,207,114,255,67,123,185,239,206,59,217,251,240,227,28,63,187,132,
-  138,43,132,229,17,82,99,153,61,123,130,56,138,9,75,53,82,91,200,62,42,245,18,120,139,53,150,180,227,153,63,181,
-  136,14,219,68,97,76,88,170,224,194,12,143,32,245,41,65,20,144,91,139,164,132,233,105,78,159,56,195,236,185,25,146,
-  36,97,98,108,138,160,52,130,109,14,171,126,107,169,82,134,138,5,245,243,192,177,193,235,254,159,40,24,209,6,248,73,
-  232,141,194,177,97,56,80,189,247,153,43,230,111,223,58,62,62,218,160,182,102,72,204,47,117,101,29,46,205,225,53,2,
-  142,10,216,162,96,247,41,168,238,186,118,103,60,210,28,227,211,31,250,34,122,105,113,94,192,153,26,180,38,46,42,47,
-  46,2,208,203,237,136,193,59,178,2,95,3,255,191,128,219,193,46,64,126,51,116,54,193,57,7,7,90,16,59,168,9,
-  104,56,24,26,130,177,49,152,188,202,251,41,155,36,19,201,193,131,163,173,3,126,125,251,158,251,39,62,14,245,135,95,
-  123,121,227,150,171,183,115,250,204,25,162,184,74,84,170,17,150,107,140,78,174,97,124,106,13,229,74,153,56,14,8,180,
-  164,26,23,170,119,235,61,181,122,131,122,179,129,8,229,192,59,68,130,144,8,28,72,69,183,219,165,215,107,113,234,216,
-  9,150,206,157,243,51,39,79,249,7,159,120,74,238,59,112,136,217,147,231,124,127,118,209,170,86,175,119,14,66,83,10,
-  227,70,173,76,106,5,88,139,80,14,41,53,94,120,82,239,233,165,93,148,201,176,174,224,135,15,53,74,180,211,37,12,
-  61,70,154,67,152,220,226,130,0,85,46,225,28,228,72,172,51,120,145,177,220,159,229,236,188,102,236,116,149,161,230,48,
-  59,118,78,17,4,17,227,169,101,253,166,13,162,127,252,72,12,84,5,4,255,230,130,215,219,83,172,225,37,176,17,204,
-  83,136,153,53,248,253,147,179,11,39,239,253,227,189,35,111,251,161,215,168,87,191,243,186,177,47,30,254,68,242,174,212,
-  6,99,240,118,15,109,15,141,253,48,218,222,62,93,126,243,13,91,229,204,124,143,19,31,185,167,255,42,56,145,195,233,
-  16,250,209,197,219,249,34,0,189,220,193,104,69,54,145,20,31,190,10,118,67,145,170,144,92,1,203,17,156,217,14,114,
-  166,224,171,149,50,168,43,168,105,239,135,71,97,207,180,144,175,171,70,225,149,207,148,101,245,251,47,27,87,233,166,136,
-  158,177,44,165,158,185,36,229,236,217,3,28,58,184,143,249,133,54,70,123,124,32,144,66,210,79,18,148,86,108,222,188,
-  133,241,137,137,66,212,42,29,169,21,28,56,126,26,209,109,229,242,228,114,110,103,23,242,74,107,174,107,78,157,79,226,
-  229,180,31,56,223,159,0,53,14,98,24,178,33,208,147,80,250,96,40,235,255,7,166,3,11,161,42,106,169,32,44,177,
-  156,230,180,230,23,64,158,69,135,33,90,6,196,145,165,189,176,72,167,221,98,114,98,146,198,218,245,244,18,207,240,112,
-  132,245,9,190,163,72,178,140,52,77,16,10,194,40,70,14,53,88,78,187,60,240,208,19,32,61,111,122,243,107,216,188,
-  97,29,37,229,137,167,70,196,113,24,222,0,19,10,170,18,22,30,24,188,198,155,129,27,7,211,171,57,96,10,191,224,
-  225,137,9,120,228,177,47,62,59,253,145,117,83,147,111,189,121,157,154,123,223,237,27,126,227,55,190,84,190,177,151,196,
-  101,200,14,197,98,88,93,119,201,200,119,124,231,107,75,91,166,199,248,226,167,191,66,248,232,35,115,21,216,239,225,228,
-  83,144,253,48,133,19,217,197,115,17,128,94,17,71,14,192,72,20,115,35,18,240,21,176,91,192,238,131,252,36,116,46,
-  133,185,46,200,114,145,139,126,44,241,174,63,148,36,85,113,102,97,108,169,213,85,165,164,77,89,9,226,178,100,114,56,
-  226,202,75,154,56,225,49,182,137,203,99,146,158,167,211,109,23,246,24,66,160,116,134,74,79,23,158,212,222,209,11,135,
-  249,224,71,190,148,223,126,240,196,177,29,48,43,224,148,132,101,93,16,138,151,129,150,4,35,6,131,88,15,83,33,92,
-  95,113,178,57,57,62,65,218,238,98,114,83,240,139,140,65,74,129,86,10,139,193,25,139,5,226,74,141,13,187,47,229,
-  240,193,195,156,58,125,134,36,201,24,29,29,195,227,73,211,4,173,53,181,90,13,169,11,211,251,70,163,62,8,69,244,
-  116,243,28,235,50,142,30,57,193,214,13,235,9,117,200,229,55,220,200,129,63,250,88,93,88,51,166,160,218,1,121,15,
-  184,0,56,5,236,40,218,95,12,176,31,210,237,176,223,193,151,111,117,102,205,199,127,247,206,87,255,210,177,29,181,27,
-  110,189,90,92,250,125,183,140,223,249,208,147,195,59,154,147,249,141,215,174,141,174,223,51,42,17,130,15,127,250,65,158,
-  250,95,31,105,189,23,30,205,224,33,13,103,95,3,182,126,241,150,189,8,64,175,244,10,105,229,244,6,160,212,25,110,
-  250,131,205,134,93,58,63,107,175,109,119,79,84,181,126,56,52,230,138,238,153,214,149,109,162,168,28,22,218,45,105,5,
-  34,151,184,229,28,1,4,206,17,200,30,85,105,104,148,12,178,92,48,161,133,112,3,235,12,137,179,146,188,30,115,221,
-  134,166,217,112,240,68,47,128,167,172,20,95,180,206,159,116,208,11,161,107,33,233,22,193,208,94,129,80,176,93,194,88,
-  207,178,115,253,154,77,196,74,112,244,200,17,58,221,46,89,154,225,133,64,75,13,14,188,5,111,29,175,186,246,122,38,
-  39,38,56,127,110,150,164,215,47,116,103,67,67,148,202,21,26,205,26,245,122,131,48,12,240,94,144,101,57,74,6,104,
-  21,32,132,70,5,10,111,224,244,233,25,150,91,29,180,244,76,175,89,195,147,97,16,250,190,25,235,67,115,59,232,255,
-  8,217,138,51,207,255,11,60,58,248,245,187,192,189,19,22,246,193,131,26,134,223,149,25,185,247,75,79,237,121,252,208,
-  252,216,174,107,214,234,155,223,124,165,170,52,135,181,79,13,95,190,251,44,95,123,226,4,242,161,131,203,223,105,204,3,
-  17,124,214,194,35,21,104,109,0,54,92,188,69,47,2,208,183,211,113,83,19,44,252,192,247,50,188,126,154,143,252,246,
-  31,224,238,123,200,190,93,208,50,208,206,123,89,190,152,27,166,26,49,105,175,143,196,163,40,188,168,133,87,40,43,200,
-  148,47,108,223,125,48,136,193,25,152,224,83,248,66,75,29,82,25,169,49,186,99,93,176,240,197,199,101,3,150,188,231,
-  96,9,14,244,33,17,224,215,20,196,74,4,48,3,242,68,209,242,204,220,234,205,204,153,78,107,104,242,170,87,149,135,
-  170,77,22,22,23,201,77,78,102,114,28,5,243,186,211,110,177,48,187,128,179,142,44,73,217,181,99,7,195,35,163,84,
-  42,85,116,80,100,95,25,155,208,79,250,88,55,176,166,149,122,160,247,76,168,86,27,68,165,42,253,196,113,238,252,121,
-  238,186,235,46,174,187,250,74,238,191,239,126,150,250,125,37,96,212,193,72,0,186,83,152,205,83,5,222,116,1,0,181,
-  128,115,96,99,56,157,195,23,21,44,222,12,87,181,142,159,189,116,230,248,217,173,7,84,80,127,186,81,169,45,90,167,
-  77,43,177,219,124,222,121,3,254,145,10,124,34,133,47,215,224,244,221,96,211,65,251,252,170,139,183,229,69,0,250,118,
-  168,130,134,223,242,70,26,183,221,76,84,175,97,146,84,76,13,15,249,33,240,206,24,43,133,200,109,175,103,79,37,37,
-  174,220,115,9,174,223,66,249,28,235,44,120,135,82,18,229,29,214,131,68,161,181,92,181,0,89,177,252,80,74,33,188,
-  70,212,37,211,87,108,211,251,27,149,198,37,203,221,77,185,247,123,114,232,89,56,103,161,55,13,70,2,243,141,42,98,
-  215,70,39,239,123,106,209,195,145,117,48,183,113,238,252,154,92,7,229,230,208,48,181,90,157,86,187,77,16,6,131,88,
-  159,12,49,54,73,111,178,87,8,95,131,8,173,131,129,149,71,66,150,103,196,113,132,64,144,13,162,154,181,10,16,97,
-  225,85,148,36,9,185,113,68,253,132,126,214,102,124,188,193,198,141,27,24,159,152,96,253,244,26,191,8,153,129,146,132,
-  178,5,245,143,120,110,19,118,225,249,18,112,5,240,17,72,167,224,168,45,10,203,39,202,176,99,35,92,177,193,230,235,
-  174,88,88,26,77,11,32,115,1,28,17,112,103,14,119,213,225,248,207,67,246,137,111,24,114,95,60,23,1,232,149,59,
-  164,110,212,217,247,246,55,211,125,235,155,217,150,36,120,239,105,59,207,142,64,115,165,16,116,189,79,74,176,56,109,252,
-  242,209,99,167,39,237,27,174,195,7,2,47,13,129,26,228,194,11,240,88,180,1,37,3,132,44,124,36,44,30,55,8,
-  6,76,7,126,67,42,207,152,110,14,241,7,245,242,200,174,94,186,125,60,55,42,47,102,185,79,90,56,180,23,206,4,
-  208,146,73,154,29,58,122,150,42,180,135,224,128,18,28,191,172,181,188,225,107,231,79,143,150,154,77,149,180,179,65,212,
-  50,228,105,74,191,223,39,12,2,192,145,246,83,162,56,162,223,235,145,244,43,69,245,99,12,81,20,144,101,41,2,49,
-  8,106,84,244,122,125,230,231,231,209,90,83,42,101,196,177,230,250,235,174,226,134,27,174,102,168,86,248,16,110,219,182,
-  149,3,149,170,179,221,142,18,160,67,16,215,1,179,65,72,62,136,164,6,138,106,108,64,146,237,0,219,33,95,128,249,
-  47,192,210,142,98,179,245,132,128,33,7,163,162,248,112,18,78,117,225,208,90,56,255,31,33,255,196,197,219,242,34,0,
-  125,59,12,163,37,16,215,106,168,95,254,207,44,76,77,48,209,235,147,196,49,86,41,68,171,229,127,231,137,167,57,224,
-  61,255,16,250,30,63,55,10,173,39,79,30,243,56,35,164,119,40,99,81,94,22,188,65,4,74,134,69,150,186,51,120,
-  81,176,156,189,0,239,192,230,41,161,20,133,133,173,115,108,27,30,97,121,98,93,229,39,23,186,219,94,27,48,246,234,
-  212,109,158,118,254,42,233,253,129,4,246,37,240,116,150,230,199,55,156,155,95,152,128,252,12,28,55,158,199,54,165,189,
-  109,15,28,124,106,77,239,234,107,170,81,8,214,130,247,57,120,75,165,20,22,238,140,170,204,210,210,18,103,207,158,197,
-  218,124,53,39,205,121,71,191,215,45,140,236,189,165,181,188,76,187,221,65,74,77,185,92,102,120,116,140,184,18,115,237,
-  181,123,184,241,166,27,8,66,129,119,6,139,103,108,205,26,49,51,54,90,237,119,59,149,10,52,129,248,111,9,217,186,
-  87,107,102,133,64,175,12,247,133,160,159,38,171,175,177,162,160,62,255,30,216,159,131,37,15,45,11,50,40,200,161,161,
-  7,50,72,246,64,118,28,220,177,139,183,230,69,0,122,37,159,128,98,215,126,28,120,24,248,114,158,19,255,247,247,35,
-  173,165,7,188,15,120,167,16,252,215,52,67,29,61,193,247,131,114,208,80,48,18,10,21,157,93,108,139,204,59,148,42,
-  30,46,33,61,82,136,194,224,204,155,129,255,52,131,225,115,33,173,112,194,23,90,46,57,176,65,115,158,106,213,115,245,
-  149,235,248,204,254,131,165,195,81,163,244,39,50,29,191,50,207,214,223,108,237,37,187,172,189,162,238,121,54,133,71,12,
-  236,157,135,163,14,22,44,236,45,121,191,249,182,163,135,71,254,200,166,91,231,155,35,177,203,29,65,16,82,142,202,104,
-  37,137,74,37,116,16,225,40,92,18,17,208,238,180,240,120,22,151,150,57,55,115,22,53,240,59,170,84,170,140,143,79,
-  48,50,58,86,132,22,106,205,80,189,198,21,151,239,33,12,21,144,35,148,67,0,19,35,77,122,107,55,132,231,142,29,
-  107,108,131,181,93,24,218,229,221,236,118,107,253,169,11,42,32,6,32,116,161,101,139,184,0,240,123,224,158,1,183,9,
-  204,229,208,203,129,125,192,26,224,244,197,118,235,34,0,189,18,43,29,53,248,200,129,135,128,95,6,142,80,172,143,73,
-  18,120,228,177,213,63,191,56,0,169,53,192,111,128,170,195,154,30,220,164,165,186,233,161,90,48,189,239,124,143,133,118,
-  194,186,50,248,92,224,165,4,21,129,243,56,145,129,0,233,229,32,56,112,197,5,177,176,129,45,174,199,147,251,24,35,
-  36,187,166,107,12,37,22,167,5,115,26,245,105,17,14,127,1,221,188,50,79,166,111,79,211,141,215,122,191,85,194,250,
-  30,124,193,22,46,168,135,114,184,115,155,177,35,239,62,121,50,252,23,231,231,182,46,73,45,99,1,101,95,38,172,6,
-  4,177,64,26,138,181,251,248,48,97,57,226,158,7,238,69,15,12,243,165,80,12,213,154,140,143,143,179,113,227,70,198,
-  199,199,87,141,238,179,44,199,59,75,24,6,104,161,177,54,195,81,68,92,87,171,21,154,235,55,201,199,249,234,208,46,
-  216,100,96,179,135,179,151,102,105,251,75,20,86,28,223,76,187,43,191,97,238,118,225,231,46,158,139,0,244,138,57,126,
-  240,67,73,6,15,199,255,6,22,6,31,127,217,89,6,222,11,250,16,76,117,224,213,177,16,119,252,65,168,111,254,61,
-  236,72,48,55,199,137,83,103,217,176,107,109,49,244,21,133,188,66,10,89,44,204,133,3,235,87,99,148,189,16,72,33,
-  46,176,36,47,172,95,173,205,153,152,24,165,82,138,232,11,192,56,34,47,72,201,196,3,81,84,123,84,168,218,219,109,
-  191,249,221,150,114,232,92,71,120,206,106,56,105,138,124,244,53,151,24,63,125,117,224,38,254,68,209,200,77,78,223,38,
-  116,151,123,164,243,93,34,89,12,154,159,126,118,63,198,89,186,253,30,34,145,40,37,25,106,12,179,118,237,90,38,39,
-  39,209,90,51,55,55,143,247,131,156,51,15,222,103,124,228,35,127,204,238,221,151,177,118,237,52,195,35,117,20,160,149,
-  230,242,171,46,229,87,62,72,253,102,216,185,6,110,152,131,147,59,224,192,15,22,94,76,95,87,101,30,0,210,139,183,
-  225,69,0,250,118,4,30,63,0,158,47,1,63,55,40,241,191,217,179,161,0,160,82,38,196,250,16,110,18,222,191,229,
-  127,135,209,109,31,13,228,8,94,146,167,57,39,207,205,195,101,91,16,178,91,0,138,16,43,145,96,224,197,64,219,229,
-  7,96,35,193,23,15,183,240,224,10,89,59,206,121,214,173,157,98,100,180,206,177,249,101,2,10,206,144,146,162,136,241,
-  9,34,62,150,155,225,237,222,172,189,21,182,58,24,61,11,39,171,112,62,16,60,161,189,191,242,170,220,108,249,114,16,
-  52,144,18,239,40,90,60,29,20,154,89,41,56,114,252,88,145,119,95,174,96,237,202,160,92,112,250,244,25,206,156,57,
-  75,16,4,40,165,8,195,16,107,29,165,82,76,165,82,103,97,33,225,228,169,5,154,205,50,235,214,79,176,123,247,78,
-  166,215,108,224,29,111,124,43,7,126,232,168,254,191,127,248,123,219,222,215,90,190,182,6,15,108,47,218,195,60,185,160,
-  146,81,60,71,74,188,216,82,93,4,160,111,139,147,15,110,248,50,80,1,126,1,248,234,224,255,93,251,77,180,6,51,
-  192,15,0,187,10,150,239,142,208,251,215,57,120,221,199,75,149,43,63,22,139,17,107,50,148,137,200,172,101,223,193,83,
-  184,219,175,47,36,93,66,12,4,254,174,248,74,131,141,216,10,223,90,8,57,120,240,139,100,213,213,105,137,51,148,99,
-  69,163,22,194,156,35,80,69,112,33,222,128,213,164,169,231,93,239,253,110,17,156,56,84,239,223,251,181,161,16,234,1,
-  168,25,200,182,121,206,59,56,59,105,109,187,12,62,149,66,56,233,240,72,148,10,112,182,224,79,7,90,35,132,196,120,
-  143,10,52,214,89,150,150,151,49,253,156,198,80,131,122,189,78,173,86,163,82,169,18,199,37,202,229,50,72,73,84,138,
-  209,58,34,77,4,207,62,125,154,82,216,100,114,124,61,165,178,228,231,255,245,207,240,131,7,247,71,199,239,252,210,240,
-  229,133,46,76,253,18,112,248,207,120,93,35,224,141,23,31,142,139,0,244,237,112,222,77,33,64,45,1,223,55,248,248,
-  171,156,7,11,200,168,44,195,174,42,188,245,172,224,45,255,167,92,218,253,181,32,46,121,103,80,190,224,252,122,1,7,
-  14,159,33,51,30,137,7,87,4,31,10,236,32,190,65,32,6,182,26,43,109,13,12,76,237,253,115,21,129,115,142,56,
-  150,52,234,49,74,8,130,160,132,183,57,121,238,169,70,37,222,247,227,63,192,123,190,243,187,248,147,127,251,239,195,62,
-  68,10,74,77,144,83,133,71,78,23,88,28,242,190,87,177,206,116,33,208,222,226,172,199,75,191,90,69,57,15,222,90,
-  164,80,68,81,192,248,240,36,235,166,166,105,84,170,212,107,53,162,184,72,99,245,64,16,4,120,239,72,115,75,24,150,
-  80,90,83,175,87,217,118,201,22,182,109,221,194,190,167,158,225,240,145,131,212,155,195,28,59,120,144,184,120,41,228,95,
-  84,229,164,192,191,161,96,73,171,139,183,232,69,0,122,41,156,215,189,128,63,0,199,95,221,59,216,82,236,148,255,65,
-  193,109,217,80,134,55,158,144,226,237,255,169,84,185,236,105,233,149,50,109,132,139,240,94,35,149,65,4,130,227,39,230,
-  152,95,88,102,178,38,138,21,188,119,56,225,138,172,120,231,89,137,133,6,135,144,131,49,235,74,37,228,28,194,21,195,
-  233,176,172,24,27,174,17,42,141,183,144,102,25,245,106,147,31,249,123,63,196,59,223,115,19,67,13,205,182,233,233,220,
-  20,224,40,50,16,187,129,253,133,139,96,167,38,72,34,103,157,87,69,58,42,20,241,63,198,185,34,181,195,67,41,42,
-  209,108,52,217,181,107,23,107,166,214,80,45,87,240,222,145,231,57,214,88,188,240,56,231,177,174,216,118,149,162,18,227,
-  19,147,172,89,211,228,234,171,118,35,4,60,187,255,41,102,102,230,168,86,135,248,195,15,255,49,115,167,79,249,38,194,
-  88,188,141,192,95,79,225,11,244,103,101,118,61,53,152,7,133,23,111,253,139,0,244,82,56,175,127,137,93,143,27,180,
-  108,145,16,101,188,223,217,22,220,242,191,170,181,221,251,4,74,231,30,79,9,43,50,188,43,188,252,132,146,204,156,91,
-  224,212,137,51,108,188,98,29,125,91,184,25,22,98,11,177,106,30,38,86,36,175,162,0,31,111,61,110,80,25,121,231,
-  81,94,80,14,67,166,166,70,233,118,30,166,20,72,46,221,115,25,87,94,117,25,141,122,141,102,99,132,201,169,113,246,
-  251,126,222,43,170,12,57,68,65,238,235,128,40,131,172,120,196,168,208,249,113,124,36,112,72,100,33,173,16,16,232,128,
-  209,145,81,46,217,114,9,107,166,166,89,55,189,22,129,160,159,244,73,173,67,106,93,52,141,82,226,115,67,58,96,70,
-  215,170,130,13,235,135,184,245,182,155,112,38,231,216,177,147,76,142,79,179,115,199,30,158,120,242,81,190,118,255,221,236,
-  12,117,214,200,178,37,160,149,130,249,135,252,249,142,133,154,130,45,109,46,222,250,23,1,232,165,112,174,123,9,94,211,
-  192,178,163,166,97,243,211,65,180,249,113,108,232,51,135,245,113,97,24,166,52,94,166,224,21,82,4,36,253,132,19,167,
-  206,227,175,90,11,62,7,4,14,3,34,44,160,200,249,1,15,72,172,14,156,157,119,184,66,147,138,214,10,239,53,4,
-  33,67,205,58,245,106,153,239,120,247,247,176,126,243,6,114,155,51,185,102,146,161,102,5,103,115,116,92,97,48,85,18,
-  150,98,163,103,33,244,66,12,133,80,90,111,93,239,33,29,84,157,7,229,65,120,135,84,69,250,170,86,154,229,229,37,
-  146,94,66,187,221,102,168,222,64,107,77,84,142,144,82,16,133,1,214,58,80,138,204,230,56,239,89,106,45,243,232,99,
-  251,112,214,176,107,215,22,54,109,154,162,82,174,35,101,196,254,103,75,140,140,76,17,47,119,140,134,89,96,126,25,204,
-  201,191,164,197,242,131,121,144,189,120,251,95,4,160,23,251,76,191,4,193,39,7,206,123,31,199,48,124,88,138,70,99,
-  98,138,106,59,97,110,118,17,35,250,8,47,145,212,80,58,67,136,132,4,203,211,71,23,200,165,2,215,67,16,23,187,
-  103,167,17,94,34,164,29,12,157,45,120,139,16,197,0,88,138,128,48,16,72,37,232,186,58,123,15,180,104,140,172,225,
-  167,127,250,199,144,170,142,115,33,213,74,133,52,239,144,89,143,146,30,19,202,192,21,69,141,110,23,12,98,74,5,214,
-  196,66,16,189,38,235,157,251,92,216,168,38,65,165,44,179,30,18,139,117,138,229,86,155,36,205,88,88,94,34,77,83,
-  180,86,72,45,169,132,49,163,245,33,226,82,137,40,42,81,169,84,137,74,37,26,205,38,205,161,6,22,203,236,242,18,
-  159,251,220,253,220,253,149,199,185,100,251,122,118,239,94,207,244,134,53,188,254,246,155,121,231,99,111,227,225,95,248,69,
-  43,160,21,66,119,30,236,255,230,185,104,158,63,235,100,20,118,29,53,10,191,160,139,231,34,0,189,168,45,207,75,13,
-  128,6,251,43,7,100,77,155,102,54,77,89,183,126,19,235,214,173,167,155,46,49,115,126,129,78,59,37,79,83,84,160,
-  80,50,228,233,103,14,147,244,37,129,45,33,209,8,233,80,72,156,8,112,88,36,96,109,62,232,194,4,58,208,132,129,
-  166,155,123,14,29,155,227,201,103,102,88,232,69,68,141,105,140,212,68,161,38,75,22,233,46,11,170,149,53,104,145,16,
-  136,58,93,21,199,182,88,238,53,60,68,39,161,83,7,35,240,253,204,35,246,120,228,143,244,58,79,254,102,197,93,149,
-  196,34,200,19,137,246,197,171,236,125,97,207,90,252,90,224,12,116,178,30,121,63,39,140,66,172,177,32,36,185,177,212,
-  235,117,166,38,167,104,12,53,104,14,15,81,111,52,232,119,251,236,123,230,0,135,142,28,96,100,180,206,117,175,186,134,
-  207,125,250,19,220,14,139,1,204,247,160,183,13,252,251,191,201,7,164,51,168,148,46,174,230,47,2,208,139,118,142,191,
-  196,174,103,133,176,88,21,162,155,195,204,30,47,230,245,226,210,244,227,179,79,210,104,196,76,174,153,100,203,166,75,8,
-  180,32,237,119,88,110,167,204,207,44,112,232,232,57,206,47,100,108,24,109,128,237,227,101,134,243,57,194,150,240,214,225,
-  144,168,160,76,138,164,159,195,249,249,22,231,102,206,114,236,76,151,197,126,128,46,13,227,43,49,185,146,148,202,101,146,
-  52,33,77,36,198,12,102,73,153,7,235,104,197,97,220,130,177,113,216,24,194,104,13,22,13,116,173,231,24,130,115,192,
-  206,203,181,72,74,185,205,19,17,7,222,231,120,231,8,163,8,6,22,32,141,70,131,126,63,161,92,174,224,140,69,139,
-  194,34,68,135,1,90,43,66,231,201,179,156,131,7,15,161,165,162,82,46,177,231,242,203,24,25,155,64,139,128,44,73,
-  8,84,157,51,103,102,88,56,122,156,221,136,89,131,63,13,116,142,81,108,187,196,55,241,58,91,96,248,226,35,112,17,
-  128,94,204,243,95,94,98,215,99,128,49,224,31,120,223,13,224,244,136,245,51,147,222,154,185,32,210,237,118,155,229,103,
-  59,184,92,80,175,133,76,77,78,176,105,235,86,110,121,245,109,52,68,198,3,79,156,96,97,195,36,181,74,64,84,146,
-  4,65,68,137,162,95,234,244,115,206,47,182,57,126,182,197,249,165,140,110,30,96,9,208,165,117,136,90,72,46,13,173,
-  222,34,218,164,180,250,45,188,21,144,137,34,129,79,131,166,2,14,108,37,16,167,161,118,21,236,202,96,23,112,62,130,
-  94,7,14,43,207,19,61,33,214,253,161,100,251,178,242,101,209,55,132,50,196,169,98,160,12,20,110,137,65,72,110,44,
-  105,146,16,71,49,66,20,219,50,173,52,121,110,8,195,16,188,99,180,89,101,120,120,132,201,137,73,74,149,8,169,60,
-  206,11,130,82,5,39,52,79,221,247,128,255,206,126,119,121,13,254,132,135,147,25,244,254,3,112,146,111,110,203,213,5,
-  110,5,110,227,162,252,226,34,0,189,72,103,221,75,176,37,44,1,11,144,141,195,233,10,226,200,173,214,93,249,180,204,
-  71,141,112,88,43,144,66,145,36,41,71,79,156,224,196,185,179,4,58,100,203,212,52,71,142,214,24,170,55,80,90,82,
-  46,69,52,135,70,9,17,116,90,139,24,15,72,69,230,4,137,51,232,168,76,165,86,67,137,5,44,65,161,13,243,17,
-  206,20,137,24,38,233,19,12,146,127,58,253,46,86,120,2,33,25,91,183,65,252,110,173,217,188,178,189,184,117,28,110,
-  238,65,55,128,243,18,154,66,136,94,46,232,156,80,58,244,97,5,37,28,214,22,2,8,235,44,74,42,162,40,38,10,
-  2,112,30,225,60,54,55,244,243,156,82,169,132,146,154,168,28,51,49,49,65,189,86,99,168,209,160,217,28,70,75,137,
-  193,144,123,67,154,36,212,202,53,70,199,155,44,60,60,107,111,183,98,57,192,207,121,88,60,15,230,125,131,1,243,55,
-  219,86,245,41,12,203,236,197,86,236,34,0,189,24,231,255,188,68,175,235,36,228,7,224,140,240,126,223,245,38,191,254,
-  163,165,112,244,180,9,16,206,34,149,71,200,128,220,231,228,105,130,49,134,39,158,125,150,103,15,41,130,40,64,133,138,
-  90,181,70,37,170,163,162,128,48,10,152,24,159,68,75,133,205,60,90,104,42,101,79,191,221,163,50,236,209,21,141,18,
-  17,129,42,33,92,177,150,215,178,80,206,219,204,97,179,4,21,105,142,30,60,76,103,185,207,252,218,13,149,163,207,44,
-  70,27,225,218,0,70,92,241,12,107,229,253,90,9,161,206,93,230,227,40,10,171,134,110,167,131,203,139,73,139,117,142,
-  126,63,41,248,62,182,32,73,214,42,17,227,35,195,140,142,140,48,54,62,78,185,92,70,8,65,20,69,132,97,136,195,
-  97,188,47,88,222,30,134,155,37,94,115,235,141,92,113,217,78,246,239,154,214,159,124,232,222,230,219,230,59,99,14,38,
-  214,66,244,86,200,155,127,141,215,123,255,96,30,148,93,4,162,139,0,244,55,121,222,248,18,188,166,21,50,226,251,96,
-  217,195,201,9,231,103,174,116,62,61,165,227,72,248,46,206,25,50,35,33,16,8,161,48,153,65,160,232,91,79,47,233,
-  33,140,163,211,239,17,171,101,84,84,38,140,42,232,176,74,163,90,71,88,143,12,32,199,18,7,154,82,169,134,14,66,
-  132,119,40,85,100,135,57,60,120,133,113,10,37,28,19,195,35,28,56,116,138,167,247,237,227,181,175,187,141,255,241,203,
-  129,123,8,22,79,104,61,229,188,92,103,181,150,21,33,92,57,64,46,161,117,59,170,134,161,75,81,222,131,85,56,71,
-  225,196,40,11,167,67,147,27,194,32,64,41,73,20,6,84,74,101,134,26,13,162,32,32,237,247,169,54,26,232,64,99,
-  173,33,8,227,85,7,199,169,201,9,110,189,237,58,46,217,58,141,20,150,235,111,186,154,167,254,209,251,234,135,126,238,
-  63,239,188,4,174,204,225,217,99,112,164,249,215,216,45,236,24,84,159,103,185,200,146,190,8,64,127,131,231,170,151,224,
-  53,57,10,95,227,65,10,69,38,4,182,42,165,143,131,136,110,210,41,18,75,157,7,227,16,210,162,101,128,49,133,221,
-  134,0,92,46,177,94,18,232,18,181,48,160,18,56,122,243,243,140,148,107,84,26,53,148,10,64,74,226,161,6,42,8,
-  200,250,73,33,4,149,26,132,32,8,11,20,244,214,161,132,103,238,252,121,164,150,188,238,117,175,165,90,21,172,153,26,
-  13,207,93,115,203,229,95,61,115,60,156,154,92,167,174,189,226,90,230,44,72,161,169,212,74,220,184,116,134,207,124,242,
-  19,136,68,18,171,8,21,12,70,194,206,35,4,72,37,49,214,32,148,198,98,49,121,70,175,219,69,107,77,181,90,197,
-  230,25,137,183,84,42,101,180,86,244,147,62,151,94,186,155,55,190,225,54,154,67,1,206,119,81,129,32,235,119,120,239,
-  251,126,132,223,253,220,87,214,245,239,127,248,234,0,30,109,195,185,195,208,9,254,26,149,140,184,160,45,63,199,75,111,
-  67,122,17,128,94,129,231,223,188,68,175,75,22,109,129,159,7,183,224,157,168,199,37,182,148,202,246,169,118,87,9,153,
-  35,2,1,46,66,122,131,215,33,66,245,17,89,134,114,154,76,10,82,219,65,228,41,36,26,235,202,172,153,154,64,201,
-  152,40,168,34,20,212,26,85,2,93,162,159,244,208,218,33,116,128,84,17,74,122,2,157,17,8,65,154,90,38,39,167,
-  216,188,101,29,155,55,77,115,234,244,49,198,71,167,24,25,25,99,120,100,93,105,118,110,158,157,107,215,177,102,184,78,
-  47,43,172,99,227,200,147,14,130,14,51,233,49,94,96,242,124,224,69,45,176,214,21,50,11,33,200,83,135,23,109,210,
-  180,143,33,35,138,67,74,165,152,80,23,154,176,21,213,126,160,2,170,37,73,179,161,49,166,139,214,18,239,11,185,199,
-  80,179,204,238,31,127,95,229,228,119,255,200,206,109,206,93,106,224,169,3,208,221,86,24,63,254,181,78,147,98,25,176,
-  27,248,247,20,12,199,139,85,209,69,0,122,65,206,31,189,68,175,203,1,13,176,35,48,223,176,156,152,232,180,58,253,
-  114,117,180,26,196,136,32,34,174,134,196,186,138,182,146,118,183,67,43,179,100,42,64,228,154,177,48,227,93,239,184,137,
-  237,107,70,233,217,18,103,90,57,194,149,169,54,70,136,75,21,84,160,9,227,152,78,59,33,38,70,0,214,88,140,109,
-  81,171,86,24,170,15,17,70,138,93,59,118,16,87,34,230,230,103,249,204,103,238,100,105,97,137,55,189,233,117,100,166,
-  207,252,252,28,89,102,9,130,18,201,32,215,203,11,48,182,136,127,86,74,209,79,123,8,61,16,205,14,56,64,98,96,
-  134,239,189,39,203,50,242,44,3,231,232,245,82,164,10,112,192,232,200,24,198,58,242,60,3,4,129,214,236,125,248,49,
-  214,174,159,224,242,203,183,35,101,177,206,199,65,158,165,92,121,219,109,60,181,115,251,164,219,247,204,118,9,147,33,156,
-  121,166,224,115,126,75,175,191,167,16,20,87,46,206,133,46,2,208,11,117,190,239,37,124,109,39,33,63,4,103,34,120,
-  102,93,183,119,110,36,10,70,71,118,236,164,147,245,176,174,143,237,59,172,140,73,236,60,86,122,84,73,97,179,14,183,
-  223,176,153,159,253,123,155,137,84,10,229,6,169,108,98,41,225,228,24,186,60,73,88,153,192,137,6,157,174,225,247,255,
-  224,83,204,205,45,115,242,216,97,254,249,79,255,191,28,58,116,152,251,239,123,136,230,72,147,153,153,51,68,113,68,146,
-  88,164,8,145,42,230,240,225,83,44,183,114,218,157,30,149,106,153,82,57,194,59,79,158,231,8,37,145,66,226,133,160,
-  214,168,179,116,182,93,180,92,131,217,207,138,53,170,49,6,231,28,222,23,45,89,24,6,24,239,56,122,226,24,75,75,
-  75,108,88,191,145,141,27,55,161,117,33,205,16,194,35,68,192,87,239,188,135,233,233,73,214,172,25,193,56,131,24,24,
-  240,215,26,13,162,87,93,85,93,220,247,204,244,16,140,151,32,88,15,185,184,120,123,95,4,160,151,250,249,249,29,91,
-  191,181,86,73,10,122,189,132,67,199,78,174,38,156,246,6,239,160,1,127,125,23,62,75,145,111,254,67,133,33,226,177,
-  181,130,179,99,237,206,142,153,209,80,215,74,17,129,170,16,170,18,105,234,24,31,242,44,247,115,172,208,228,181,30,83,
-  163,107,8,114,205,222,199,78,145,5,150,141,107,107,132,13,141,170,70,104,175,200,243,148,74,217,48,180,174,70,179,17,
-  240,129,15,126,17,242,148,7,30,188,143,61,151,95,205,210,167,238,164,159,229,180,147,121,42,165,33,182,111,221,77,167,
-  187,196,114,107,137,71,31,125,156,115,231,218,68,113,25,17,44,34,148,69,135,33,185,183,164,105,138,146,18,235,29,185,
-  177,168,32,192,121,139,214,154,44,203,96,80,25,173,176,161,87,0,41,183,14,235,115,132,23,204,204,207,34,148,164,90,
-  171,50,210,28,37,12,61,198,88,148,22,156,57,59,207,222,189,143,243,174,119,223,142,183,14,63,16,211,134,129,100,250,
-  150,87,169,153,223,252,192,240,48,212,13,4,107,249,139,229,24,23,207,69,0,122,73,156,31,187,255,179,223,210,223,47,
-  151,75,220,119,223,94,110,185,245,157,171,159,251,238,65,217,254,216,96,142,176,226,126,250,87,121,71,94,81,197,183,33,
-  13,133,56,27,59,78,78,119,251,189,195,46,173,135,185,32,119,125,124,45,192,235,128,106,125,152,225,177,6,97,92,129,
-  60,195,233,46,198,78,112,223,125,207,240,197,131,135,185,100,119,137,218,80,7,212,12,167,207,156,36,77,186,236,216,178,
-  129,127,243,255,253,4,27,55,77,49,50,220,96,100,104,156,123,238,185,143,137,137,105,208,37,130,40,166,89,213,156,63,
-  187,72,107,185,195,249,185,89,188,128,135,30,121,4,80,236,218,185,147,165,86,225,96,152,231,22,131,35,75,115,178,52,
-  37,203,83,180,214,224,41,204,200,212,115,96,99,173,69,74,185,218,142,57,239,87,171,35,231,138,225,250,201,211,167,233,
-  245,82,46,191,116,15,235,214,173,71,107,61,168,162,44,173,78,191,176,154,5,148,84,32,37,198,231,108,189,226,82,230,
-  154,181,154,91,108,55,83,40,125,165,136,150,126,65,206,27,47,62,54,23,1,232,249,58,85,251,173,237,58,132,117,148,
-  221,215,79,8,110,43,230,55,84,129,255,10,156,31,252,186,207,115,62,65,249,55,1,72,10,120,24,76,15,102,180,243,
-  135,174,108,47,207,124,117,126,182,174,70,214,160,93,68,63,237,35,101,128,86,101,210,110,70,156,27,226,72,112,170,53,
-  71,223,247,152,152,108,32,207,72,74,19,163,76,111,220,76,175,103,57,112,114,14,239,20,79,63,123,134,179,231,186,172,
-  157,30,167,94,21,132,186,196,194,210,89,30,122,244,49,78,159,63,199,184,24,98,98,106,140,36,57,195,236,220,89,42,
-  149,33,54,109,222,204,179,7,247,243,192,131,247,48,221,29,193,121,69,210,179,40,209,199,88,135,49,22,155,27,112,48,
-  212,24,98,126,113,97,80,233,72,130,32,36,203,50,164,44,92,25,165,112,40,165,177,174,168,140,242,60,71,41,85,152,
-  232,75,197,242,242,34,243,139,11,76,79,175,41,116,109,58,196,139,136,32,136,17,74,129,43,190,142,160,240,179,30,25,
-  109,34,170,149,42,139,237,241,16,42,103,64,156,42,162,164,47,2,208,69,0,122,233,158,11,227,90,254,90,0,228,253,
-  159,250,26,43,70,100,43,118,15,45,224,43,3,16,42,81,48,117,71,41,72,111,127,225,215,6,198,192,183,97,209,10,
-  177,127,93,150,31,158,154,111,109,156,31,153,10,162,40,34,176,142,146,6,157,27,50,81,168,219,51,23,112,248,196,34,
-  39,103,151,216,176,105,8,189,183,69,127,169,11,137,35,150,130,169,225,33,150,206,157,102,235,230,38,190,127,156,114,216,
-  163,81,83,216,20,186,189,20,227,44,141,161,26,179,179,51,132,90,18,137,152,245,235,54,80,169,85,185,246,186,203,121,
-  226,233,251,184,251,222,47,241,244,211,251,208,170,138,223,185,7,173,53,173,238,50,198,24,194,64,67,110,169,150,203,68,
-  113,140,77,0,239,17,12,236,95,61,197,38,204,250,34,169,195,129,212,98,144,146,225,177,206,98,93,138,22,154,118,167,
-  77,146,37,4,161,64,168,16,188,98,121,185,75,158,231,32,36,14,129,146,10,165,4,214,89,150,189,11,37,212,98,8,
-  143,131,248,217,23,104,118,252,211,23,31,155,191,124,52,113,241,37,120,233,28,65,161,77,154,0,190,8,28,5,182,3,
-  123,254,146,143,75,129,203,129,0,223,75,225,217,80,240,192,119,47,47,28,12,14,62,109,246,31,57,202,242,226,18,42,
-  112,136,114,142,174,26,116,41,64,135,101,156,141,57,188,255,8,219,71,21,35,21,201,98,199,18,148,170,148,43,37,74,
-  33,216,108,129,157,219,198,56,115,232,46,204,242,51,76,52,43,56,159,163,180,160,181,180,204,200,200,16,185,201,72,186,
-  125,230,231,150,144,42,100,211,166,9,46,191,98,51,165,82,132,146,101,112,33,206,164,180,58,139,228,89,142,201,114,242,
-  44,39,75,51,140,117,40,169,16,222,83,45,151,169,213,106,68,97,56,112,201,231,185,132,14,60,90,235,194,255,200,217,
-  255,191,189,55,15,146,44,59,175,251,126,247,222,183,230,90,123,117,109,221,93,189,47,211,131,30,204,210,192,12,177,11,
-  6,33,130,32,77,82,166,68,195,33,129,4,69,74,54,35,108,81,98,72,38,195,132,21,150,195,225,176,197,144,130,52,
-  29,148,40,155,146,76,64,65,90,129,85,32,9,96,176,204,0,179,47,61,189,77,239,93,189,212,94,149,149,251,91,238,
-  226,63,94,246,0,8,1,3,96,134,164,12,76,158,142,138,138,232,204,172,170,124,121,227,188,239,126,247,59,231,96,116,
-  94,52,166,17,4,65,136,117,133,43,163,31,4,164,73,74,110,50,150,110,221,228,206,242,90,17,43,141,195,106,7,86,
-  160,173,163,107,139,248,69,59,176,224,31,98,72,64,67,124,155,202,104,21,248,220,160,89,125,237,123,32,175,147,14,61,
-  226,220,45,141,248,242,209,180,251,213,191,209,106,172,244,182,26,92,190,241,50,23,206,93,228,230,237,37,122,237,109,186,
-  93,135,113,154,209,160,196,75,55,218,4,85,159,233,168,205,230,214,38,205,78,147,64,132,4,50,166,229,199,252,238,191,
-  127,130,243,151,215,153,240,50,38,188,30,41,45,140,77,8,114,159,74,28,144,226,104,247,52,123,22,231,216,119,116,47,
-  111,121,248,1,68,232,168,213,107,56,87,70,250,1,202,47,230,122,250,253,4,147,107,116,154,99,181,195,104,71,41,46,
-  19,123,62,229,40,24,156,98,57,130,208,7,97,113,194,96,156,6,225,6,22,172,134,220,228,32,239,58,56,10,148,10,
-  25,169,141,81,10,107,24,93,228,153,42,101,200,18,205,210,213,6,74,21,75,92,72,176,100,32,60,66,43,173,43,10,
-  75,163,192,149,138,10,242,207,253,107,136,225,22,236,7,186,26,130,194,80,231,171,20,230,245,63,69,113,90,230,190,195,
-  157,35,7,222,3,157,147,206,157,239,195,243,7,123,189,147,123,43,229,217,139,8,181,186,177,196,78,179,196,214,102,13,
-  188,6,50,72,169,170,58,47,118,251,236,252,181,26,7,22,119,113,231,220,26,221,213,179,116,243,189,140,149,170,148,168,
-  48,58,162,8,186,91,160,199,136,199,171,44,63,187,66,185,84,97,125,189,69,73,167,28,61,124,136,249,137,69,14,44,
-  78,115,234,129,123,168,213,203,128,102,110,102,2,103,18,162,168,134,114,146,70,99,27,207,11,200,146,12,173,13,189,94,
-  15,99,13,158,39,208,218,160,133,37,203,50,58,157,14,8,129,49,166,120,159,178,120,167,58,207,64,184,194,138,213,57,
-  36,130,114,185,202,194,204,28,81,24,98,157,197,243,36,82,21,179,205,58,55,220,185,179,140,179,135,94,73,251,80,190,
-  162,219,92,102,188,215,105,1,155,29,232,253,44,184,15,51,156,102,30,18,208,16,175,250,33,245,129,255,7,120,43,69,
-  148,204,93,147,172,111,70,27,120,59,184,16,58,73,161,60,111,151,132,176,66,250,202,137,30,185,145,180,58,61,172,200,
-  208,46,99,211,180,184,157,106,254,241,239,126,145,191,245,254,123,120,231,67,251,161,26,3,77,164,55,193,187,222,249,1,
-  22,230,15,50,165,110,178,185,125,145,140,132,67,71,70,153,156,156,68,246,124,42,163,37,198,103,107,236,158,154,224,212,
-  91,78,48,49,81,198,217,14,66,195,7,127,252,199,248,228,39,30,229,169,167,159,38,10,2,218,237,22,213,106,141,36,
-  73,73,211,140,36,73,16,82,48,57,53,73,84,46,115,103,229,14,150,98,230,199,1,158,231,21,122,48,99,94,177,232,
-  16,131,198,51,22,172,113,4,129,79,92,42,84,243,206,26,114,173,145,158,135,64,160,148,160,209,216,38,77,19,124,79,
-  96,172,66,18,211,90,90,33,110,117,219,2,182,29,244,119,81,56,30,14,49,36,160,33,190,7,252,52,240,19,192,19,
-  131,187,118,192,183,118,80,13,176,14,166,94,88,143,54,234,190,49,70,91,223,19,49,198,129,181,109,180,177,56,23,163,
-  2,139,245,53,159,124,244,44,207,188,120,139,221,187,103,56,116,120,140,67,11,83,212,107,109,234,11,33,75,91,207,179,
-  122,227,50,89,175,203,242,102,204,244,244,4,181,186,207,248,220,12,165,114,141,217,189,99,220,127,114,150,209,233,58,56,
-  9,248,160,51,198,167,39,248,231,255,236,127,229,23,126,254,111,115,249,234,18,189,44,101,187,181,131,29,196,237,36,3,
-  151,198,173,157,109,148,231,35,165,194,225,80,210,67,155,98,0,209,31,52,156,139,237,150,43,236,98,1,207,11,144,158,
-  164,86,174,18,120,10,37,7,30,214,131,92,69,229,43,132,146,8,225,13,46,142,68,160,145,100,172,222,106,210,3,95,
-  64,41,6,191,199,95,156,197,198,232,112,185,14,9,232,135,13,201,96,91,246,78,224,89,96,135,226,196,204,251,166,173,
-  89,9,180,131,237,0,54,102,250,173,190,243,227,200,90,129,24,244,85,144,10,103,2,140,235,33,124,31,75,153,91,89,
-  206,202,173,45,158,91,234,17,184,171,40,207,65,52,69,165,62,198,226,158,89,142,239,153,39,40,69,108,53,183,88,223,
-  218,225,186,189,138,146,17,60,99,248,220,103,29,229,72,49,59,63,203,190,125,139,28,61,122,15,179,51,33,187,15,44,
-  242,214,71,30,230,220,203,87,144,73,194,198,214,6,113,24,147,244,19,250,253,62,97,28,209,207,18,154,173,22,210,83,
-  131,137,103,129,211,69,211,217,57,135,26,76,71,91,7,74,250,72,36,129,23,18,135,33,161,31,130,83,148,203,37,162,
-  56,194,104,135,31,201,98,230,40,213,196,113,140,231,21,77,109,15,9,120,172,145,114,14,198,143,195,180,134,242,93,253,
-  214,144,128,134,4,52,196,247,8,67,161,196,126,25,248,53,160,1,108,125,211,227,191,10,246,175,67,223,65,183,158,219,
-  76,6,2,161,114,156,17,88,235,33,132,69,73,141,209,6,163,66,156,144,248,57,196,158,163,54,82,161,82,153,97,98,
-  215,36,181,177,17,42,21,71,111,167,207,185,11,231,89,93,91,102,179,177,77,179,185,137,116,22,233,43,178,212,130,246,
-  17,46,39,174,88,164,231,51,90,155,224,239,254,242,207,243,139,31,249,48,207,61,253,44,22,104,118,90,196,186,68,191,
-  215,199,58,135,167,20,221,126,151,92,107,242,60,47,234,27,103,177,182,168,114,140,49,4,65,128,239,251,197,228,52,69,
-  142,189,51,130,122,165,198,216,232,24,66,72,74,229,50,81,185,130,148,30,65,24,19,134,1,202,43,198,30,234,245,42,
-  158,95,2,231,112,169,98,237,218,10,127,242,249,47,176,0,210,129,151,22,38,144,194,127,229,220,109,136,33,1,13,241,
-  61,33,7,222,11,60,10,252,51,96,134,98,34,186,15,28,43,66,119,50,5,189,58,46,19,206,33,132,1,225,144,162,
-  140,19,41,214,36,40,1,206,106,164,176,164,38,71,152,18,19,81,133,242,72,68,63,111,177,115,169,69,171,113,135,78,
-  163,73,39,181,164,182,143,39,3,112,17,34,16,120,65,113,196,45,194,24,79,122,8,149,224,16,172,110,36,116,58,154,
-  151,47,92,228,244,75,103,200,45,228,198,224,229,26,231,123,36,73,31,41,69,17,52,168,53,89,94,76,58,69,81,132,
-  49,26,41,213,43,83,208,214,218,34,185,213,82,16,82,224,227,73,15,171,161,82,45,17,132,33,97,28,33,253,0,49,
-  232,138,9,225,136,162,144,99,199,143,131,133,139,231,175,114,250,249,179,156,127,249,44,243,147,7,153,226,139,29,96,51,
-  130,238,24,184,128,161,136,116,72,64,67,188,38,56,138,38,234,223,1,62,62,40,251,19,32,130,158,7,235,39,181,219,
-  156,205,211,217,155,24,229,91,64,152,65,12,188,68,8,131,20,26,235,4,82,56,66,149,147,52,239,208,182,101,178,92,
-  225,76,137,82,181,74,125,122,138,78,71,226,59,77,57,14,112,78,17,198,49,35,99,163,148,226,16,63,18,84,226,49,
-  38,198,166,200,109,70,189,50,202,47,254,194,127,193,191,253,131,255,131,237,198,54,65,101,12,144,228,90,19,197,33,214,
-  26,180,30,68,66,15,100,22,206,21,249,207,190,31,144,164,41,97,24,17,69,33,58,215,69,106,171,18,4,65,64,41,
-  42,225,6,178,140,60,211,4,126,81,37,1,120,126,64,181,90,193,138,132,82,20,209,106,237,240,135,31,251,18,215,175,
-  44,225,203,136,95,252,165,95,224,137,175,127,157,165,223,163,237,96,93,65,183,207,95,156,187,225,196,112,121,14,9,232,
-  141,64,64,119,19,50,182,128,83,192,20,112,186,24,31,218,30,15,131,246,131,165,48,189,209,77,74,86,170,162,242,81,
-  69,195,215,90,240,60,31,79,80,104,165,140,132,176,140,8,170,76,78,140,16,71,49,129,31,82,42,151,145,162,208,89,
-  5,161,162,94,169,50,179,107,134,137,137,73,28,14,39,13,158,240,80,8,226,146,224,145,135,239,71,216,46,173,110,78,
-  92,25,33,51,133,61,172,181,57,73,226,80,170,176,227,112,20,138,119,156,35,80,30,105,47,65,248,30,126,20,227,71,
-  49,206,8,20,130,114,88,34,183,57,185,179,36,58,167,28,151,145,30,72,233,104,55,155,148,226,152,82,165,130,51,57,
-  59,141,62,78,100,228,229,140,79,253,135,79,83,138,75,188,253,221,239,226,161,83,135,9,227,18,238,209,62,1,100,18,
-  82,13,230,189,124,247,25,171,215,243,217,12,49,36,160,55,84,37,180,14,236,20,125,213,178,47,68,252,114,158,151,190,
-  214,151,37,127,208,233,16,66,97,140,123,197,248,235,174,221,133,4,130,80,224,156,161,92,41,225,123,146,40,10,8,131,
-  128,192,87,72,169,136,162,144,74,165,68,224,251,197,105,150,148,120,74,18,68,49,161,10,169,87,75,28,62,62,77,37,
-  86,124,229,203,79,178,111,225,94,222,254,240,187,121,244,241,79,225,251,1,86,23,106,119,165,84,49,84,168,53,82,74,
-  164,82,228,70,227,121,62,115,51,211,136,129,126,203,24,131,53,134,192,15,232,38,224,140,38,12,67,170,213,26,213,82,
-  137,145,250,40,56,208,58,71,98,73,91,77,16,29,250,253,62,219,166,207,187,223,243,54,62,240,193,247,49,58,85,197,
-  101,109,176,134,104,106,156,185,169,41,161,215,215,221,247,18,203,51,196,144,128,222,208,149,141,25,124,72,187,40,74,154,
-  18,223,26,21,156,2,71,65,156,7,239,33,8,182,161,156,20,102,91,71,34,231,30,74,60,127,98,203,6,56,12,190,
-  39,9,195,136,110,183,251,138,202,28,4,214,22,19,200,89,146,208,100,7,33,4,181,106,141,44,203,25,31,31,35,46,
-  197,72,225,176,78,179,211,106,80,142,74,4,126,128,53,154,137,169,41,132,130,169,201,136,131,251,22,200,82,201,179,79,
-  159,69,103,57,247,221,119,148,185,133,159,231,133,211,143,227,132,36,85,26,1,36,73,242,202,239,191,75,132,206,83,164,
-  24,86,183,214,241,157,0,235,240,252,16,148,96,164,28,226,17,146,181,13,58,213,244,58,29,226,32,160,213,106,146,166,
-  41,165,94,137,76,103,88,44,113,24,80,45,141,16,71,117,118,205,206,50,58,91,67,247,183,49,82,32,156,34,8,66,
-  162,169,113,225,214,215,69,34,165,192,14,71,16,135,4,52,68,209,60,29,144,75,31,216,11,140,23,167,234,126,8,234,
-  223,12,134,159,75,224,54,192,104,208,17,216,117,80,83,66,212,183,157,219,19,192,108,9,102,29,28,234,195,177,203,176,
-  248,25,231,38,115,44,146,162,250,81,74,49,49,49,65,154,166,228,121,142,181,208,235,246,72,242,12,223,243,200,179,46,
-  253,110,74,171,212,33,10,183,185,117,243,22,190,239,227,251,62,113,92,98,116,108,132,133,185,57,234,181,58,155,91,107,
-  204,206,142,112,248,224,62,230,231,75,52,183,55,184,118,169,199,72,117,146,227,199,15,82,29,243,216,189,56,202,169,251,
-  30,225,107,79,63,131,31,22,39,89,189,94,239,149,247,92,88,108,0,72,20,2,157,102,120,126,0,14,242,44,67,42,
-  133,116,146,241,145,17,118,207,237,197,89,139,201,52,82,120,40,165,216,181,107,154,241,137,9,132,242,176,194,81,41,133,
-  72,25,163,115,88,222,218,192,184,62,218,43,92,151,60,167,88,156,159,231,214,160,240,169,84,171,194,53,155,195,133,55,
-  36,160,97,181,3,133,26,126,180,56,30,142,238,133,154,129,81,11,147,41,212,227,193,103,101,192,106,232,118,97,219,66,
-  170,160,228,57,119,208,194,67,43,112,20,41,199,111,213,170,187,26,39,79,142,239,126,251,59,188,195,205,166,252,242,239,
-  253,139,65,165,99,233,118,187,56,231,136,227,152,169,169,105,70,71,198,169,86,43,180,90,109,182,54,55,201,210,28,165,
-  36,198,88,38,38,38,25,31,31,71,8,193,216,232,56,165,82,137,145,209,58,66,8,210,44,195,152,140,118,103,155,61,
-  123,223,74,175,189,77,210,117,28,62,186,64,165,86,193,201,140,92,167,148,234,227,156,58,245,16,143,62,246,85,124,79,
-  210,75,122,56,91,88,108,216,111,138,98,86,8,164,149,248,161,95,24,145,57,135,181,14,207,8,154,107,155,244,194,62,
-  211,115,62,115,51,187,144,82,226,43,69,16,248,148,43,101,164,82,224,64,105,200,123,25,81,92,194,23,10,229,2,164,
-  243,9,188,24,145,129,240,4,183,175,95,38,105,236,184,0,108,144,36,246,16,223,16,1,15,49,36,160,55,20,238,78,
-  50,123,192,75,224,111,195,136,15,115,33,44,90,88,20,176,96,96,174,11,99,41,120,10,80,96,21,180,125,216,12,5,
-  105,28,69,229,164,159,204,239,204,206,28,204,254,171,159,155,147,11,123,228,76,125,130,189,229,50,89,175,203,187,75,49,
-  31,255,216,199,217,222,217,65,74,133,49,150,110,183,83,184,4,58,75,158,231,248,254,60,167,30,58,69,165,92,45,44,
-  44,40,230,112,188,193,132,178,214,26,157,21,196,36,164,194,247,124,114,33,209,54,225,220,185,11,28,63,190,159,131,139,
-  123,153,158,142,192,43,38,108,180,201,10,159,103,107,185,255,161,147,148,75,1,137,205,209,90,23,91,75,99,64,12,200,
-  71,41,180,115,168,200,43,172,93,179,194,55,8,41,200,76,142,31,68,196,165,128,208,247,233,247,250,196,97,132,147,10,
-  223,47,114,226,115,173,9,130,8,63,10,81,190,135,23,23,199,249,189,180,79,175,101,72,147,46,43,55,110,112,231,153,
-  103,89,253,253,63,104,237,190,179,114,221,192,182,78,83,253,171,195,101,56,36,160,55,114,229,51,11,226,131,80,217,130,
-  61,62,220,39,225,254,46,28,219,169,148,23,178,67,123,199,163,251,78,148,107,71,78,4,37,63,166,84,46,145,246,58,
-  180,87,238,216,213,149,101,221,104,181,92,98,80,87,111,222,82,135,222,247,62,239,248,219,222,33,150,87,183,105,111,236,
-  208,189,122,19,155,167,228,121,70,28,149,193,53,6,149,5,24,171,139,74,8,65,224,71,92,191,126,157,213,149,85,14,
-  29,60,196,209,163,71,41,87,202,164,105,97,155,161,117,142,231,121,248,129,196,247,138,35,115,132,67,121,1,121,98,232,
-  39,9,207,60,119,150,253,123,23,8,60,176,178,136,242,81,158,194,232,156,76,119,57,118,124,15,19,83,35,92,188,118,
-  3,103,45,131,248,46,148,87,84,65,74,41,100,16,98,125,15,145,101,28,83,158,59,172,84,190,183,84,182,143,26,237,
-  223,240,148,10,203,17,74,74,60,169,16,82,14,12,235,65,121,1,129,167,240,124,69,169,82,65,202,0,47,240,137,3,
-  159,180,159,240,167,159,254,18,219,43,55,104,127,245,139,122,241,241,39,239,236,109,182,159,5,254,204,193,21,32,29,97,
-  120,90,53,36,160,55,68,185,227,8,162,144,221,129,79,158,229,76,2,199,65,25,152,84,112,111,0,111,111,194,195,75,
-  243,147,71,43,63,243,51,19,199,62,248,99,222,222,147,135,9,203,21,154,205,14,237,141,6,59,155,29,182,219,93,58,
-  51,123,81,38,15,246,197,37,158,126,242,41,174,108,231,196,205,62,222,197,139,120,94,21,163,193,58,133,242,3,74,149,
-  18,83,227,19,220,188,121,3,161,36,32,145,72,148,80,69,206,86,210,197,26,135,53,134,75,151,47,210,238,180,216,189,
-  123,55,51,51,51,175,24,201,131,64,9,137,177,57,18,133,117,96,140,35,240,67,242,76,115,229,210,45,110,222,92,230,
-  208,225,5,64,224,164,196,57,81,168,218,173,101,116,162,194,193,67,123,57,127,249,26,129,167,200,180,198,9,65,113,16,
-  47,113,210,99,26,120,176,215,79,222,150,235,205,195,206,173,214,178,180,17,109,111,143,236,175,215,247,255,47,229,242,88,
-  224,251,228,14,82,163,137,131,10,202,247,17,74,81,169,214,112,56,2,79,17,74,69,88,170,224,132,38,105,174,177,249,
-  242,6,249,205,149,124,242,210,153,237,61,27,203,87,74,198,60,101,225,177,28,94,168,192,74,2,230,129,225,202,28,18,
-  208,27,130,127,146,132,99,15,156,100,233,63,123,23,55,63,243,103,92,6,21,192,116,14,143,4,240,254,51,146,71,58,
-  31,120,239,222,247,127,244,127,8,14,221,123,47,141,173,45,46,157,187,204,213,203,55,105,108,247,72,181,33,213,144,234,
-  62,89,102,168,84,66,46,95,188,204,167,62,249,71,244,186,9,187,23,231,241,164,34,112,32,68,76,238,43,82,154,8,
-  153,48,59,51,70,240,66,128,243,4,185,51,4,25,72,2,82,211,39,239,25,98,47,194,218,20,227,52,102,217,176,181,
-  189,77,179,217,100,110,110,142,122,189,142,239,251,72,63,196,106,31,163,53,224,240,132,197,104,141,211,150,220,120,156,63,
-  127,135,253,7,23,17,50,47,230,145,133,4,5,78,88,188,48,228,240,225,227,216,79,125,1,35,61,82,37,177,18,170,
-  6,142,89,97,223,150,233,230,3,253,254,157,133,60,187,162,224,98,238,220,149,20,26,26,246,239,233,245,126,236,17,193,
-  67,87,54,55,162,242,174,24,105,37,189,230,54,196,49,165,145,81,68,183,139,236,103,208,107,131,238,89,215,221,209,213,
-  205,181,254,194,214,102,123,180,211,109,84,90,173,59,10,46,89,56,173,225,180,129,107,93,104,229,96,134,42,248,33,1,
-  253,224,117,138,95,243,235,29,202,247,249,92,24,112,95,241,63,117,3,15,89,248,169,143,73,222,51,246,119,126,105,242,
-  239,255,227,223,164,219,94,231,11,159,253,20,55,110,238,208,110,129,192,129,44,178,176,76,174,89,95,95,35,138,74,108,
-  110,44,243,248,227,143,209,235,167,56,41,217,105,117,200,115,69,224,28,34,239,80,246,60,2,127,140,52,239,179,231,224,
-  49,68,240,53,210,212,224,2,129,81,6,231,122,88,13,89,174,49,174,77,20,248,104,13,89,154,211,15,19,148,148,180,
-  90,45,198,199,199,25,31,31,167,62,50,74,28,151,17,66,160,117,142,186,187,205,234,91,148,15,75,55,111,209,108,118,
-  168,215,67,44,22,196,55,196,165,8,193,201,55,221,67,232,41,166,149,112,187,16,118,183,148,233,35,90,111,28,205,147,
-  235,37,99,46,230,112,33,131,139,22,150,20,108,196,144,165,112,201,215,58,255,112,171,125,167,215,237,207,245,183,215,199,
-  76,24,134,185,16,194,58,135,148,210,73,132,8,172,37,74,211,60,72,211,166,103,244,154,7,235,2,86,44,172,89,184,
-  237,224,102,14,203,211,176,179,1,217,39,128,95,228,91,199,25,134,24,18,208,255,175,33,74,175,207,182,92,4,62,171,
-  183,111,243,123,79,61,207,63,128,192,135,125,62,188,235,179,240,246,234,223,251,149,201,127,240,27,191,202,141,171,215,248,
-  202,87,94,164,223,55,40,95,96,85,134,115,18,97,171,24,151,147,230,109,234,213,10,198,88,46,156,63,67,171,221,64,
-  170,8,139,230,250,205,37,238,187,255,17,212,104,153,40,48,4,34,197,237,180,8,101,64,109,126,150,127,244,143,254,59,
-  158,121,254,12,159,250,211,79,225,2,1,58,71,80,38,10,21,34,75,200,50,131,211,125,4,105,97,16,102,52,173,86,
-  139,157,157,29,58,157,14,225,218,26,181,106,157,90,181,74,185,92,70,122,10,43,0,89,184,43,110,108,111,177,190,182,
-  201,216,216,158,226,116,203,13,196,163,88,208,154,197,253,251,248,201,35,135,242,159,184,177,212,155,108,119,151,125,99,206,
-  225,220,121,13,47,167,112,217,192,114,79,136,102,197,247,147,11,158,111,30,74,250,108,88,123,117,196,185,158,112,238,217,
-  178,205,166,43,121,54,37,122,189,210,160,133,228,238,222,23,92,209,208,207,5,108,3,171,6,182,51,104,4,208,201,161,
-  215,133,254,4,216,125,224,250,3,226,17,195,222,207,144,128,126,160,8,40,42,189,190,31,32,235,4,255,242,119,249,123,
-  183,151,69,6,19,62,220,127,11,30,156,248,200,223,156,254,165,223,252,135,60,245,245,103,121,233,165,203,116,123,25,16,
-  162,240,16,50,68,231,25,142,46,97,232,97,173,67,9,201,157,59,183,232,118,59,100,38,67,170,0,33,12,155,55,175,
-  163,154,219,16,180,192,53,168,69,41,39,142,204,51,191,231,0,99,115,135,136,199,39,105,172,174,242,147,63,185,204,51,
-  103,95,194,151,96,16,56,151,83,169,84,49,214,145,155,188,232,7,89,203,206,206,78,49,115,99,12,198,24,194,32,228,
-  70,118,3,107,45,227,227,227,76,79,79,179,123,247,110,194,40,34,205,50,250,73,206,202,234,38,135,143,238,25,76,88,
-  187,66,115,38,36,96,217,53,62,198,125,202,211,83,141,237,29,7,143,167,240,105,13,23,34,216,172,67,247,113,200,43,
-  74,33,222,249,30,236,252,110,158,250,250,99,212,94,62,223,115,112,195,193,173,42,132,14,162,154,84,190,21,66,8,163,
-  95,225,15,1,244,193,88,72,51,72,98,208,135,193,252,11,112,15,243,141,184,35,201,55,38,159,135,228,51,36,160,31,
-  44,200,215,227,242,235,179,178,118,147,223,252,151,255,154,143,64,148,192,225,28,30,217,60,116,226,224,207,253,198,175,169,
-  39,190,246,36,79,62,113,13,99,115,60,15,132,208,88,171,112,70,32,148,68,121,61,44,22,129,160,219,233,210,106,183,
-  104,236,52,0,131,167,124,156,235,48,26,231,132,233,69,222,60,57,202,190,153,128,241,218,24,206,131,157,214,13,46,175,
-  53,104,232,29,30,126,211,65,254,218,143,190,135,231,94,58,143,47,125,172,211,72,207,32,125,159,192,243,169,40,0,73,
-  224,135,180,90,173,87,44,82,181,214,68,81,52,240,232,129,181,181,53,54,54,54,72,210,148,137,201,73,162,40,6,33,
-  88,91,109,96,173,3,103,80,210,199,57,112,206,98,141,198,143,61,250,187,38,252,68,200,173,192,217,23,36,60,51,15,
-  107,255,70,74,247,246,19,111,102,227,194,75,200,44,163,49,62,65,82,171,211,154,156,98,230,229,243,220,4,115,13,204,
-  135,32,107,67,251,183,172,161,193,55,220,32,7,213,15,191,9,116,129,167,129,69,224,71,128,165,193,247,97,94,251,144,
-  128,126,240,241,75,31,121,29,87,217,99,102,117,157,95,185,179,194,29,24,173,194,61,107,130,19,39,63,250,235,99,171,
-  235,91,156,63,123,155,94,218,163,20,71,248,170,168,180,172,203,208,166,141,18,10,167,61,228,192,21,48,73,50,218,221,
-  30,105,102,200,116,70,73,194,104,181,198,63,252,200,125,188,247,68,149,216,211,108,172,246,249,202,217,62,59,193,4,170,
-  94,193,90,203,243,47,60,198,98,188,195,91,222,188,143,145,145,17,250,205,62,82,21,199,234,89,174,169,214,106,72,9,
-  158,244,81,202,43,60,155,141,161,84,46,83,169,86,201,178,194,203,89,12,254,85,171,85,60,63,32,207,53,206,245,9,
-  188,136,173,237,54,105,146,17,132,114,64,62,174,16,171,90,240,163,0,87,45,75,131,107,201,162,55,211,254,223,193,157,
-  149,146,131,135,142,32,174,188,12,89,134,52,6,97,12,106,144,29,47,191,169,202,113,20,105,33,235,223,230,18,255,253,
-  193,115,135,194,138,33,1,253,112,226,247,254,245,235,122,121,90,220,161,131,16,22,82,184,71,189,255,253,187,71,238,63,
-  36,31,251,15,143,210,235,121,120,94,136,39,61,28,26,156,5,103,17,214,67,103,96,156,198,247,36,78,56,180,161,232,
-  215,72,69,20,251,216,118,198,252,212,40,15,28,153,227,230,245,13,158,191,154,144,149,246,50,119,244,65,14,223,115,148,
-  201,93,19,40,4,43,43,151,184,126,229,22,15,190,243,4,123,15,236,225,236,51,103,65,130,176,30,54,203,6,166,96,
-  150,192,131,206,118,131,44,203,16,66,20,67,130,90,227,28,248,97,80,36,150,122,133,29,71,165,82,193,247,124,156,133,
-  208,143,104,182,123,36,105,70,169,84,193,9,133,47,60,172,21,24,157,162,100,64,85,6,22,231,114,1,89,2,246,143,
-  40,28,29,255,122,145,4,246,231,178,45,26,138,75,135,4,244,67,137,207,190,206,215,59,160,4,245,16,142,174,195,137,
-  221,63,255,145,145,243,23,174,176,182,182,142,241,71,16,66,33,149,162,82,138,241,148,44,242,175,140,64,27,75,175,155,
-  146,154,46,73,218,167,221,237,98,17,56,1,163,99,227,32,2,116,154,16,171,132,229,118,74,26,31,225,3,127,243,151,
-  153,152,170,145,247,91,172,173,109,114,230,250,50,141,164,204,198,250,50,129,159,115,242,205,39,120,230,241,39,9,85,5,
-  157,75,172,72,104,54,27,4,65,72,71,119,208,218,226,73,133,239,251,84,42,85,148,146,148,203,17,189,94,15,207,243,
-  8,195,152,153,153,57,42,229,10,121,166,65,129,182,119,27,207,170,16,183,74,139,115,2,41,68,161,118,247,21,42,146,
-  78,64,238,64,43,112,31,2,82,99,153,121,246,73,110,165,9,211,98,72,31,67,2,26,226,219,226,208,235,40,239,5,
-  144,129,216,134,154,133,197,246,145,195,123,153,159,147,207,127,246,243,100,198,225,185,14,190,95,71,201,136,114,80,101,106,
-  87,141,133,221,147,84,106,33,89,63,163,185,222,226,233,211,231,232,119,83,180,211,168,80,129,113,84,188,93,152,216,177,
-  222,91,166,35,160,62,86,97,178,60,143,114,25,79,61,253,12,91,43,13,178,84,210,74,250,84,203,147,92,93,185,68,
-  111,189,203,123,31,184,151,127,229,123,224,4,78,40,180,215,67,153,14,42,23,244,122,61,130,32,68,72,69,181,90,29,
-  28,187,27,164,204,240,60,143,192,47,51,55,187,155,122,173,74,150,247,7,114,10,31,129,193,23,6,231,2,140,5,165,
-  114,44,2,105,65,24,131,72,19,166,186,253,60,42,52,108,205,25,48,255,19,69,143,40,185,126,153,7,128,138,148,124,
-  42,8,96,72,68,67,2,26,226,91,209,150,30,145,213,175,137,132,4,133,133,170,145,66,9,235,194,185,201,73,121,249,
-  226,101,218,237,30,113,20,160,164,135,179,41,190,215,231,192,161,69,142,31,219,207,200,120,25,235,108,145,242,112,104,158,
-  202,68,137,63,250,227,47,83,142,74,96,53,213,74,181,176,39,45,89,214,150,187,108,54,20,123,38,234,92,249,234,139,
-  180,31,155,165,39,186,132,186,77,144,135,104,171,241,71,3,150,238,40,206,45,189,192,209,153,58,227,19,85,150,119,114,
-  202,153,198,225,145,43,3,54,37,8,2,106,181,26,97,24,99,140,193,247,139,192,192,66,51,22,16,199,49,99,99,99,
-  120,158,79,174,19,242,92,35,16,228,66,17,87,71,40,197,37,148,76,144,194,34,40,60,125,208,22,157,58,242,141,173,
-  84,194,70,10,59,189,66,209,143,30,92,159,10,69,21,53,255,212,19,44,191,239,175,146,6,193,240,164,106,72,64,67,
-  220,197,71,103,142,243,179,27,151,25,207,122,175,105,128,45,7,234,8,36,14,175,211,211,87,94,190,140,242,60,156,53,
-  88,28,81,232,113,207,241,67,156,122,232,40,82,229,8,50,24,52,114,51,221,231,240,161,93,28,59,184,200,234,202,50,
-  165,56,160,94,171,162,148,32,247,12,73,174,185,178,180,197,131,239,62,73,125,116,25,151,165,140,150,43,180,123,29,124,
-  153,178,103,102,140,23,47,95,66,101,227,184,102,29,229,239,112,96,62,230,246,198,38,130,26,46,47,99,92,15,200,145,
-  210,209,233,116,169,84,106,232,220,144,231,57,165,82,209,24,15,195,136,133,133,57,170,213,50,89,150,98,29,88,235,8,
-  67,137,231,251,76,78,77,17,4,62,66,244,193,89,156,182,197,73,158,12,57,119,254,69,154,171,171,122,15,244,203,144,
-  254,54,216,63,248,118,100,125,229,18,251,148,226,64,171,201,189,195,101,55,36,160,33,6,8,74,188,48,58,207,149,181,
-  75,223,119,163,51,5,126,6,152,118,206,51,16,172,151,170,126,203,130,176,22,169,20,2,69,28,150,89,92,88,196,147,
-  138,60,239,35,144,40,21,35,164,35,211,41,130,156,131,251,23,248,202,87,193,87,18,223,243,208,38,35,55,61,146,212,
-  240,153,39,110,241,115,63,254,22,222,245,224,28,143,95,110,211,82,146,204,102,216,238,37,222,250,222,15,178,123,254,24,
-  51,174,71,232,174,225,162,113,142,239,61,193,19,79,223,194,85,115,112,10,165,21,8,131,82,138,56,138,209,185,70,8,
-  73,28,199,4,65,64,28,151,168,86,107,212,106,181,65,83,218,97,140,197,243,124,162,40,70,231,25,83,211,35,8,101,
-  192,88,76,63,3,17,129,11,56,123,238,34,127,252,241,63,230,100,179,149,0,125,3,250,167,129,3,128,255,31,53,203,
-  28,189,151,207,51,201,127,156,121,54,196,144,128,222,176,240,157,229,98,105,148,207,188,198,215,63,2,226,158,194,178,198,
-  107,132,161,159,106,13,121,70,218,207,81,194,35,80,62,206,88,176,14,233,192,100,6,252,194,172,171,72,123,176,108,53,
-  54,177,54,71,56,65,154,100,180,123,13,182,183,54,8,163,18,207,157,89,225,242,218,14,71,78,236,99,185,213,97,114,
-  143,100,241,222,119,176,122,166,194,242,165,47,242,230,135,238,33,223,184,205,234,166,225,143,255,244,28,159,127,236,121,202,
-  126,133,220,119,8,145,160,80,4,65,9,223,247,9,163,176,120,207,190,55,248,30,16,69,37,198,199,39,8,163,0,173,
-  53,214,25,148,44,30,215,58,103,98,178,198,225,67,243,96,179,226,125,8,159,44,151,156,62,125,142,47,125,245,113,242,
-  181,6,211,189,254,182,133,13,160,87,5,222,253,29,8,70,80,184,63,150,135,203,110,72,64,67,20,208,206,176,55,30,
-  99,79,101,130,165,206,230,247,253,122,67,49,16,103,64,110,6,177,111,173,5,147,145,166,9,158,12,105,119,119,184,185,
-  124,131,197,197,147,56,99,16,202,2,41,166,216,231,208,234,102,188,248,210,121,12,154,94,191,152,201,105,181,154,228,185,
-  197,154,156,181,141,156,63,250,183,79,242,235,127,171,205,59,102,203,4,113,139,252,230,13,246,250,9,155,145,229,147,159,
-  252,18,95,127,106,155,11,75,61,158,56,127,150,70,99,135,48,10,144,89,140,144,2,233,65,24,132,32,10,151,194,32,
-  14,176,214,81,42,69,212,235,35,76,78,78,83,173,86,48,38,35,203,82,130,32,66,160,240,124,31,107,53,7,22,23,
-  152,26,175,14,124,131,34,214,55,119,120,241,197,23,120,252,107,95,39,40,133,252,149,83,15,57,62,247,39,77,7,155,
-  10,146,79,83,196,75,203,87,185,102,18,24,3,194,225,242,27,18,208,27,29,119,239,212,247,141,237,125,77,4,212,150,
-  66,72,207,19,46,203,197,213,141,77,185,92,190,78,144,118,209,14,202,229,49,114,157,240,252,139,167,57,126,104,31,147,
-  99,37,12,125,4,6,97,4,206,40,190,254,245,115,92,190,118,131,219,43,183,105,52,91,108,109,55,232,180,91,8,2,
-  156,53,136,32,231,183,63,251,20,126,205,240,142,251,247,145,52,250,60,250,244,10,103,111,238,32,130,58,126,181,66,101,
-  98,23,227,251,29,51,59,107,52,90,25,125,155,225,153,140,216,31,108,245,242,140,177,209,113,2,63,196,31,196,221,140,
-  140,140,50,54,54,70,20,21,113,58,214,25,60,95,146,101,41,190,23,99,173,102,100,180,206,187,222,241,118,60,20,91,
-  141,38,75,183,87,57,247,242,117,158,121,246,52,229,146,207,135,62,252,55,184,244,135,31,179,94,150,165,2,210,14,152,
-  127,69,49,3,244,221,16,1,31,102,56,205,60,36,160,55,56,4,144,59,195,92,121,244,53,85,65,117,235,156,201,114,
-  235,131,243,251,61,179,182,182,78,217,246,201,140,99,107,171,205,158,189,123,184,112,225,26,159,253,220,151,248,233,159,248,
-  81,170,181,10,189,78,151,141,141,109,158,127,238,28,159,249,179,175,113,107,229,14,107,27,183,89,91,109,97,140,69,72,
-  137,213,2,132,69,90,65,187,175,248,39,255,215,83,252,206,39,47,209,111,182,89,152,223,195,254,163,199,40,141,132,220,
-  92,90,162,183,233,216,53,61,195,177,197,7,89,187,149,176,153,172,128,167,208,46,67,106,139,26,184,31,214,106,117,162,
-  40,38,12,66,198,199,199,41,151,43,120,202,199,216,188,200,228,114,142,48,12,177,6,42,149,42,239,127,255,251,201,211,
-  148,39,191,118,158,229,149,117,94,190,188,196,205,213,6,251,14,237,227,167,254,243,247,49,187,48,198,153,141,85,83,118,
-  100,14,242,17,112,255,61,133,116,226,187,85,64,171,131,94,208,80,185,62,36,160,33,28,56,241,218,171,160,65,126,151,
-  213,155,183,76,143,24,191,18,211,75,183,72,251,45,148,39,152,156,28,231,209,199,30,231,197,179,103,88,152,159,195,88,
-  199,141,155,183,89,89,91,163,213,110,115,231,206,29,26,141,6,157,78,7,59,176,50,13,35,133,144,30,253,110,15,207,
-  179,24,171,216,218,106,33,149,32,26,169,241,210,153,23,185,179,124,7,173,29,199,143,159,96,114,98,158,218,68,153,217,
-  221,19,108,159,93,69,249,22,141,69,225,35,132,71,28,151,136,162,136,145,122,141,106,165,78,41,174,32,133,135,0,66,
-  63,160,223,55,132,97,136,195,17,197,30,71,14,29,100,109,121,153,23,159,221,102,117,117,149,102,227,38,211,149,113,254,
-  202,187,223,197,125,111,59,202,254,185,17,154,253,148,222,157,213,108,10,183,165,97,167,14,230,191,230,123,107,48,219,193,
-  86,109,133,225,148,243,144,128,134,64,91,195,76,121,148,71,38,247,115,102,227,234,171,222,193,239,162,75,113,218,35,192,
-  74,208,213,118,91,175,244,175,194,238,41,106,35,37,180,150,108,110,110,209,237,182,153,155,153,97,103,167,193,213,107,87,
-  11,77,86,146,113,103,101,149,126,210,39,75,83,180,214,133,68,98,16,103,227,7,1,8,247,74,198,151,64,32,85,113,
-  124,255,252,115,207,17,4,1,74,42,74,213,152,245,245,117,54,183,182,152,155,157,225,200,145,35,220,190,125,155,86,171,
-  53,168,164,44,190,231,177,111,223,62,74,113,76,20,132,248,42,0,109,169,148,35,74,213,18,142,194,204,222,58,135,242,
-  37,83,19,147,52,26,219,92,188,116,145,94,179,195,228,216,24,111,122,211,73,14,29,63,192,209,147,71,168,151,75,144,
-  229,228,105,134,235,119,50,96,83,194,78,0,38,227,123,27,236,20,131,30,208,20,176,54,92,176,67,2,26,2,180,115,
-  220,63,50,207,135,118,150,17,121,255,187,222,201,45,80,5,215,131,36,130,230,92,24,245,140,203,217,106,111,178,211,131,
-  188,229,33,21,212,70,98,110,175,88,202,113,17,63,188,177,177,78,179,221,38,73,179,194,28,62,207,49,198,224,156,195,
-  243,60,162,40,34,240,125,210,44,41,102,114,156,0,44,14,144,74,18,122,17,224,176,214,96,172,161,84,142,73,146,62,
-  253,126,194,216,232,4,15,191,245,17,190,252,229,175,208,75,250,68,129,79,173,82,163,215,233,161,211,156,190,239,83,171,
-  86,193,194,86,150,97,221,24,86,138,66,32,170,36,129,10,216,220,220,66,231,154,90,173,198,248,200,24,7,247,204,114,
-  228,196,62,118,31,95,68,8,135,236,39,133,177,124,150,35,114,99,92,97,149,145,255,83,176,191,13,196,223,207,53,167,
-  80,185,247,134,203,111,72,64,111,248,126,144,179,180,252,144,237,209,5,126,118,253,210,119,37,32,1,164,224,214,161,99,
-  97,61,180,174,147,27,77,214,234,99,243,12,151,6,8,37,72,117,200,214,214,22,58,47,140,224,173,45,146,33,140,27,
-  164,82,72,31,165,160,84,42,17,197,49,74,41,90,173,29,250,73,31,107,45,82,202,226,53,20,61,154,48,12,105,53,
-  119,144,82,146,235,156,102,163,133,153,53,248,190,143,231,121,204,207,47,176,127,255,126,110,221,188,197,161,195,135,11,31,
-  232,48,26,228,128,5,148,75,49,149,114,25,37,20,81,169,140,23,12,166,118,132,32,215,57,253,94,239,149,252,46,229,
-  251,220,243,192,125,28,56,92,39,205,83,252,220,71,56,139,139,28,237,126,15,63,147,133,64,30,92,62,184,46,253,239,
-  243,186,95,26,46,189,33,1,13,81,64,89,67,119,100,134,79,55,151,185,152,118,94,245,148,198,0,19,192,143,66,106,
-  160,61,233,92,111,97,124,146,43,171,55,9,125,129,13,44,214,64,183,219,5,1,149,82,25,231,138,237,158,213,182,72,
-  14,117,16,199,17,97,24,146,231,57,253,126,31,173,53,89,158,14,162,142,93,33,6,149,30,142,194,72,172,215,235,97,
-  29,8,91,184,19,250,158,143,148,146,36,73,104,54,155,128,96,223,226,62,78,61,120,10,223,247,209,86,147,231,154,36,
-  77,17,206,20,166,241,82,18,148,98,130,48,192,243,10,2,202,117,142,195,225,251,62,130,66,49,47,156,164,159,228,128,
-  195,211,57,82,75,50,15,124,17,177,121,233,26,193,173,149,190,131,158,6,243,87,129,105,190,255,44,174,187,150,28,191,
-  51,232,9,13,49,36,160,55,46,1,57,199,154,10,185,49,115,148,201,27,207,188,106,63,227,110,254,87,12,166,11,201,
-  136,54,253,227,243,115,110,105,107,83,88,157,0,69,76,177,67,22,238,126,73,70,24,68,248,94,128,117,22,207,83,8,
-  100,97,113,234,28,217,192,62,35,205,138,78,138,214,69,190,123,81,156,200,65,85,82,228,122,149,226,26,221,110,151,90,
-  173,198,236,204,44,35,35,163,120,158,95,228,171,87,171,56,99,144,82,208,107,119,48,206,21,141,114,229,21,57,91,65,
-  132,19,18,173,13,46,24,100,121,13,72,64,73,137,118,20,118,28,56,242,164,199,250,237,219,184,227,51,8,153,162,189,
-  132,160,84,231,198,229,43,172,254,246,239,247,38,175,93,61,99,224,106,94,84,129,188,214,52,10,31,248,101,224,183,248,
-  222,142,241,135,24,18,208,15,45,164,213,148,227,17,62,84,153,228,129,206,198,119,125,254,121,208,109,216,246,250,189,141,
-  55,151,131,188,245,35,111,15,190,252,165,47,225,97,145,24,156,28,156,147,185,98,186,56,8,130,34,59,29,135,20,96,
-  156,165,215,110,99,181,65,15,178,213,7,188,131,148,2,73,17,117,46,165,71,28,197,120,94,192,248,248,36,211,187,118,
-  177,184,103,145,106,173,74,28,71,228,70,147,38,9,206,90,130,48,64,122,62,141,78,151,181,149,85,70,171,53,198,198,
-  199,169,212,234,120,190,87,152,207,75,85,228,120,41,129,16,2,147,229,131,0,67,135,144,5,225,41,223,103,121,109,21,
-  157,38,248,165,0,43,224,153,207,127,145,139,191,250,209,230,220,153,243,79,1,159,13,224,89,160,125,138,215,55,229,252,
-  163,192,28,240,145,225,18,28,18,208,27,186,23,52,168,110,62,62,190,135,127,223,217,224,213,82,198,51,224,199,32,159,
-  133,213,28,174,30,184,117,103,123,249,173,239,216,181,118,239,253,92,60,127,6,169,187,8,43,144,74,34,165,122,133,124,
-  172,213,3,55,68,65,158,166,224,28,218,232,34,81,75,74,140,45,26,195,175,40,207,157,96,126,126,158,125,251,15,48,
-  57,53,205,232,232,24,113,24,163,60,143,164,159,144,101,26,135,165,86,175,129,20,116,27,219,52,207,158,177,245,151,47,
-  232,122,187,205,246,169,183,121,106,106,90,226,64,56,1,6,226,56,36,14,35,180,213,131,30,147,64,121,170,56,113,19,
-  18,103,45,206,15,176,113,136,240,28,55,207,94,224,201,223,255,152,238,254,223,255,110,101,177,217,121,86,194,159,120,240,
-  165,18,220,250,29,200,99,160,198,107,183,55,137,40,172,87,135,24,18,208,112,43,102,13,155,165,81,110,76,29,224,193,
-  245,43,40,190,253,124,75,6,204,20,237,160,181,12,206,238,186,113,237,202,200,129,197,169,83,15,63,40,199,106,37,46,
-  157,62,77,171,213,66,10,65,185,84,166,90,175,131,128,78,167,69,174,51,148,49,248,214,129,47,65,72,242,60,43,42,
-  16,107,200,179,12,68,153,61,187,15,240,240,155,223,204,222,61,11,88,9,61,155,163,60,15,172,165,151,116,169,142,84,
-  9,2,159,78,179,73,114,237,101,166,175,93,237,239,191,116,121,115,87,171,181,58,225,156,150,48,243,137,222,214,252,86,
-  104,101,28,9,130,48,64,57,137,22,142,204,104,140,209,24,35,193,249,4,94,72,106,83,148,7,81,57,70,167,9,165,
-  235,55,220,215,255,155,207,247,150,63,241,137,173,250,230,246,245,89,120,30,120,76,195,243,22,86,62,10,217,39,135,75,
-  102,136,33,1,253,249,193,1,129,53,28,157,56,192,223,238,53,57,248,42,91,177,12,120,17,90,192,185,32,201,158,59,
-  113,230,194,190,47,76,239,157,61,114,252,30,118,239,154,225,133,211,47,176,116,115,169,200,80,239,52,177,198,224,156,69,
-  231,57,218,128,239,135,4,202,199,26,73,166,29,58,207,41,197,101,14,239,187,135,99,71,143,113,248,240,65,194,48,164,
-  211,238,224,210,28,101,12,81,217,71,196,33,213,168,130,202,53,205,103,158,118,19,47,189,212,222,187,124,123,121,38,77,
-  46,120,112,94,195,245,4,144,112,106,223,203,23,223,219,169,141,237,73,246,29,19,65,77,225,252,16,89,13,113,78,160,
-  6,65,131,214,9,116,214,39,72,122,148,117,134,60,123,75,215,158,125,170,25,94,187,182,154,102,217,149,121,56,235,224,
-  124,14,23,114,184,49,2,173,223,2,51,36,159,33,134,4,244,250,118,92,238,59,61,208,119,134,127,55,177,200,111,188,
-  10,1,5,69,21,148,95,133,27,26,158,88,88,186,113,240,216,153,211,245,211,39,223,84,246,75,1,15,63,242,86,22,
-  118,47,176,178,186,70,191,215,67,8,65,191,223,163,223,235,97,133,71,169,82,67,73,1,90,83,158,171,48,63,191,192,
-  145,35,71,153,158,222,5,194,144,244,59,244,186,93,132,148,152,80,225,171,128,40,12,72,218,93,242,243,215,221,244,11,
-  207,108,159,188,117,243,70,93,235,139,14,94,178,240,146,129,107,1,108,38,224,57,88,58,218,106,53,103,190,252,232,91,
-  239,60,245,244,108,30,70,126,88,42,17,150,203,104,157,129,49,88,231,112,206,162,180,182,113,158,217,82,146,228,81,187,
-  181,86,178,246,162,131,11,182,248,186,146,193,198,94,232,124,5,242,39,249,54,182,27,67,12,49,36,160,239,187,216,121,
-  213,7,79,116,183,190,235,15,89,0,82,216,185,3,207,9,152,185,247,185,167,107,237,170,255,192,141,125,135,35,95,120,
-  220,115,248,24,39,142,221,75,158,229,104,173,49,185,38,244,3,180,40,82,48,42,81,137,122,181,74,185,84,38,42,197,
-  244,147,132,84,247,49,182,56,202,15,189,128,220,106,188,56,198,96,216,57,119,134,125,47,157,110,237,191,181,116,173,154,
-  36,207,105,120,206,193,121,3,75,26,54,28,244,207,130,253,47,129,23,32,233,67,163,174,243,211,99,237,198,140,107,23,
-  39,229,118,160,130,184,27,6,248,205,217,90,6,50,1,43,6,174,8,184,133,16,59,35,74,245,207,104,109,223,9,156,
-  7,110,48,148,81,12,49,36,160,191,80,24,227,136,59,13,150,10,130,121,213,50,202,130,25,131,91,91,240,133,216,104,
-  255,109,95,121,204,69,205,238,125,183,79,190,165,98,29,120,198,17,120,1,97,84,122,101,32,209,15,36,206,228,131,19,
-  41,73,187,219,161,213,239,224,135,33,42,240,240,157,79,53,174,128,146,100,121,74,239,218,117,70,95,120,186,127,224,252,
-  217,165,201,110,247,37,11,79,25,120,38,134,43,171,208,168,64,58,3,238,14,112,109,240,119,37,208,156,135,179,55,225,
-  90,9,226,210,64,132,110,41,28,29,131,111,67,36,61,48,9,244,35,33,186,7,14,29,200,211,195,251,221,212,196,8,
-  75,95,250,26,246,250,173,225,194,24,98,72,64,127,225,228,99,29,251,234,17,215,30,121,136,23,146,14,82,124,103,133,
-  152,39,37,75,221,46,201,115,207,166,191,226,220,149,77,248,211,64,91,115,234,217,231,146,169,102,251,77,23,223,252,224,
-  164,155,157,23,65,232,147,27,141,118,14,43,101,17,113,227,10,162,19,18,148,23,225,5,138,32,80,68,113,132,148,18,
-  50,141,185,190,68,233,233,39,251,251,47,156,185,189,171,213,56,39,29,207,26,120,110,16,129,188,118,24,250,159,6,142,
-  81,72,27,238,18,138,1,38,129,189,144,158,133,116,22,26,255,219,128,120,74,158,98,97,116,140,155,141,6,169,214,175,
-  156,254,5,20,121,92,231,129,119,252,200,91,168,253,212,143,99,146,62,38,246,169,47,175,13,9,104,136,33,1,253,101,
-  32,183,150,15,28,159,231,161,125,179,69,64,240,171,118,146,4,120,30,239,217,217,225,127,190,114,57,249,187,112,169,15,
-  105,136,219,58,116,249,226,234,200,157,219,167,206,47,238,95,216,62,113,50,142,23,22,240,3,133,242,61,60,21,160,42,
-  101,132,44,68,167,65,224,19,122,30,158,16,36,157,54,237,75,231,92,240,228,83,189,189,55,174,223,158,236,118,206,75,
-  120,206,192,139,249,128,120,22,161,87,3,187,243,77,149,216,119,130,164,104,152,127,124,64,64,191,246,158,119,114,100,113,
-  47,255,227,255,249,251,223,242,188,24,248,111,7,63,75,249,62,86,128,233,37,72,5,54,207,135,219,174,33,134,4,244,
-  151,5,37,4,89,154,146,234,239,238,92,83,141,34,170,74,241,135,192,12,36,31,129,171,13,104,229,176,58,213,235,222,
-  24,63,247,210,253,203,183,150,14,95,157,157,223,149,238,222,29,245,162,72,154,56,22,34,12,113,206,81,169,85,104,182,
-  90,168,141,117,29,111,108,234,218,198,122,251,158,181,181,149,145,52,189,12,60,111,224,197,92,138,75,210,186,85,191,176,
-  63,181,21,10,85,249,119,83,239,127,59,210,24,13,124,198,148,122,245,215,56,203,43,83,145,67,12,49,36,160,191,92,
-  184,239,247,201,119,165,19,64,21,244,255,11,107,143,192,19,10,150,4,188,176,187,213,188,103,161,213,60,144,93,186,48,
-  154,64,156,249,126,77,123,94,221,89,43,124,104,187,60,111,199,90,247,98,104,43,88,6,174,89,196,229,28,119,57,129,
-  245,178,117,189,17,176,222,224,215,189,158,106,196,216,194,132,108,136,33,134,4,244,195,86,57,1,91,192,117,176,191,14,
-  173,231,161,155,192,178,134,211,22,166,34,107,43,1,132,54,77,71,101,154,78,57,80,18,54,44,108,73,72,53,244,50,
-  216,150,176,237,227,218,14,250,115,96,253,111,170,78,134,110,130,67,12,9,104,136,87,45,136,238,110,112,170,96,34,104,
-  93,131,206,12,220,186,13,106,18,68,25,162,118,209,118,17,9,244,125,72,19,48,62,216,147,160,159,6,171,129,121,94,
-  187,196,97,136,33,254,178,33,220,176,180,30,98,136,33,254,19,65,14,47,193,16,67,12,241,159,10,255,31,228,61,54,
-  41,71,79,28,213,0,0,0,0,73,69,78,68,174,66,96,130,
-};
-
-};
diff --git a/target-loki/resource/resource.hpp b/target-loki/resource/resource.hpp
deleted file mode 100644
index a665316a..00000000
--- a/target-loki/resource/resource.hpp
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace resource {
-  extern const uint8_t loki[121905];
-};
diff --git a/target-loki/settings/settings.cpp b/target-loki/settings/settings.cpp
deleted file mode 100644
index 6a067be1..00000000
--- a/target-loki/settings/settings.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "../loki.hpp"
-Settings* settings = nullptr;
-
-Settings::Settings() {
-  settings = this;
-
-  video.append(video.driver = ruby::video.optimalDriver(), "Driver");
-  video.append(video.synchronize = false, "Synchronize");
-  append(video, "Video");
-
-  audio.append(audio.driver = ruby::audio.optimalDriver(), "Driver");
-  audio.append(audio.synchronize = true, "Synchronize");
-  audio.append(audio.mute = false, "Mute");
-  append(audio, "Audio");
-
-  input.append(input.driver = ruby::input.optimalDriver(), "Driver");
-  append(input, "Input");
-
-  terminal.append(terminal.backgroundColor = 0x481818, "BackgroundColor");
-  terminal.append(terminal.foregroundColor = 0xffffff, "ForegroundColor");
-  append(terminal, "Terminal");
-
-  geometry.append(geometry.presentation = "", "Presentation");
-  geometry.append(geometry.terminal = "", "Terminal");
-  append(geometry, "Geometry");
-
-  load();
-}
-
-void Settings::load() {
-  Configuration::Document::load(program->path("settings.bml"));
-  Configuration::Document::save(program->path("settings.bml"));
-}
-
-void Settings::unload() {
-  //remember window geometry for next run
-  geometry.presentation = presentation->geometry().text();
-  geometry.terminal = ::terminal->geometry().text();
-
-  Configuration::Document::save(program->path("settings.bml"));
-}
-
-void Settings::command(string s, lstring args) {
-  unsigned argc = args.size();
-  s.ltrim<1>("settings.");
-
-  if(s == "video.driver" && argc == 1) { video.driver = args[0]; return; }
-  if(s == "video.synchronize" && argc == 1) { video.synchronize = args[0] != "false"; ruby::video.set(ruby::Video::Synchronize, video.synchronize); return; }
-  if(s == "audio.driver" && argc == 1) { audio.driver = args[0]; return; }
-  if(s == "audio.synchronize" && argc == 1) { audio.synchronize = args[0] != "false"; ruby::audio.set(ruby::Audio::Synchronize, audio.synchronize); return; }
-  if(s == "audio.mute" && argc == 1) { audio.mute = args[0] != "false"; return; }
-  if(s == "input.driver" && argc == 1) { input.driver = args[0]; return; }
-  if(s == "terminal.background-color" && argc == 1) { terminal.backgroundColor = hex(args[0]); ::terminal->setColors(); return; }
-  if(s == "terminal.foreground-color" && argc == 1) { terminal.foregroundColor = hex(args[0]); ::terminal->setColors(); return; }
-
-  echo("Error: unrecognized setting: ", s, "\n");
-}
diff --git a/target-loki/settings/settings.hpp b/target-loki/settings/settings.hpp
deleted file mode 100644
index 68bd5de1..00000000
--- a/target-loki/settings/settings.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-struct Settings : Configuration::Document {
-  struct Video : Configuration::Node {
-    string driver;
-    bool synchronize;
-  } video;
-
-  struct Audio : Configuration::Node {
-    string driver;
-    bool synchronize;
-    bool mute;
-  } audio;
-
-  struct Input : Configuration::Node {
-    string driver;
-  } input;
-
-  struct Terminal : Configuration::Node {
-    unsigned backgroundColor;
-    unsigned foregroundColor;
-  } terminal;
-
-  struct Geometry : Configuration::Node {
-    string presentation;
-    string terminal;
-  } geometry;
-
-  Settings();
-  void load();
-  void unload();
-
-  void command(string s, lstring args);
-};
-
-extern Settings* settings;
diff --git a/target-loki/terminal/terminal.cpp b/target-loki/terminal/terminal.cpp
deleted file mode 100644
index 18fd0213..00000000
--- a/target-loki/terminal/terminal.cpp
+++ /dev/null
@@ -1,384 +0,0 @@
-#include "../loki.hpp"
-Terminal* terminal = nullptr;
-
-Terminal::Terminal() {
-  terminal = this;
-  if(settings->geometry.terminal.empty()) {
-    unsigned y = 64 + presentation->geometry().height + presentation->frameMargin().height;
-    settings->geometry.terminal = {"64,", y, ",800,480"};
-  }
-
-  setTitle({"loki v", Emulator::Version});
-  setGeometry(settings->geometry.terminal);
-
-  console.setFont(Font::monospace(8));
-  console.setPrompt("$ ");
-  setColors();
-
-  layout.append(console, {~0, ~0});
-  append(layout);
-
-  onClose = &Application::quit;
-  console.onActivate = {&Terminal::command, this};
-}
-
-void Terminal::load() {
-  if(file::exists(program->path("aliases.cfg"))) {
-    string filedata = file::read(program->path("aliases.cfg"));
-    lstring lines = filedata.split("\n");
-    for(auto& line : lines) {
-      lstring part = line.split<1>(" => ");
-      if(part.size() != 2) continue;
-      aliases.append({part[0], part[1]});
-    }
-  }
-
-  if(file::exists(program->path("hotkeys.cfg"))) {
-    string filedata = file::read(program->path("hotkeys.cfg"));
-    lstring lines = filedata.split("\n");
-    for(auto& line : lines) {
-      lstring part = line.split<1>(" => ");
-      if(part.size() != 2) continue;
-      hotkeys.append({part[0], part[1]});
-    }
-  }
-}
-
-void Terminal::unload() {
-  file fp;
-  if(fp.open(program->path("aliases.cfg"), file::mode::write)) {
-    for(auto& alias : aliases) fp.print(alias.name, " => ", alias.mapping, "\n");
-    fp.close();
-  }
-  if(fp.open(program->path("hotkeys.cfg"), file::mode::write)) {
-    for(auto& hotkey : hotkeys) fp.print(hotkey.name, " => ", hotkey.mapping, "\n");
-    fp.close();
-  }
-}
-
-void Terminal::command(string t) {
-  for(auto& alias : aliases) {
-    lstring tokens;
-    if(tokenize(tokens, t, alias.name) == false) continue;
-    string output = alias.mapping;
-    for(unsigned n = 0; n < tokens.size(); n++) {
-      output.replace(string{"{", 1 + n, "}"}, tokens[n]);
-    }
-    t = output;
-    break;
-  }
-
-  auto source = Debugger::Source::CPU;
-  if(t.beginsWith("cpu/"  )) { source = Debugger::Source::CPU;   t.ltrim<1>("cpu/"  ); }
-  if(t.beginsWith("smp/"  )) { source = Debugger::Source::SMP;   t.ltrim<1>("smp/"  ); }
-  if(t.beginsWith("ppu/"  )) { source = Debugger::Source::PPU;   t.ltrim<1>("ppu/"  ); }
-  if(t.beginsWith("dsp/"  )) { source = Debugger::Source::DSP;   t.ltrim<1>("dsp/"  ); }
-  if(t.beginsWith("apu/"  )) { source = Debugger::Source::APU;   t.ltrim<1>("apu/"  ); }
-  if(t.beginsWith("wram/" )) { source = Debugger::Source::WRAM;  t.ltrim<1>("wram/" ); }
-  if(t.beginsWith("vram/" )) { source = Debugger::Source::VRAM;  t.ltrim<1>("vram/" ); }
-  if(t.beginsWith("oam/"  )) { source = Debugger::Source::OAM;   t.ltrim<1>("oam/"  ); }
-  if(t.beginsWith("cgram/")) { source = Debugger::Source::CGRAM; t.ltrim<1>("cgram/"); }
-
-  if(source == Debugger::Source::CPU) t.replace("$", hex(SFC::cpu.regs.pc));
-  if(source == Debugger::Source::SMP) t.replace("$", hex(SFC::smp.regs.pc));
-
-  lstring part = t.strip().split<1>(" "), args;
-  string s = part(0);
-  string p = part(1);
-  if(p) args = p.qsplit(" ").strip();
-  unsigned argc = args.size();
-
-  if(s.empty()) return;
-
-  if(s.beginsWith("settings.")) return settings->command(s, args);
-
-  if(s == "aliases") {
-    echoAliases();
-    return;
-  }
-
-  if(s == "aliases.append") {
-    lstring part = p.qsplit<1>("=>").strip();
-    if(part.size() == 2) aliases.append({part[0], part[1]});
-    echoAliases();
-    return;
-  }
-
-  if(s == "aliases.remove" && argc == 1) {
-    unsigned n = decimal(args[0]);
-    if(n < aliases.size()) aliases.remove(n);
-    echoAliases();
-    return;
-  }
-
-  if(s == "aliases.reset") {
-    aliases.reset();
-    echo("All aliases removed\n");
-    return;
-  }
-
-  if(s == "break") {
-    debugger->stop();
-    return;
-  }
-
-  if(s == "breakpoints") {
-    debugger->echoBreakpoints();
-    return;
-  }
-
-  if(s == "breakpoints.append" && argc >= 2 && argc <= 3) {
-    Debugger::Breakpoint bp;
-    bp.source = source;
-    if(args[0] == "read"   ) bp.mode = Debugger::Breakpoint::Mode::Read;
-    if(args[0] == "write"  ) bp.mode = Debugger::Breakpoint::Mode::Write;
-    if(args[0] == "execute") bp.mode = Debugger::Breakpoint::Mode::Execute;
-    bp.addr = hex(args[1]);
-    if(argc >= 3) bp.data = (uint8_t)hex(args[2]);
-    debugger->breakpoints.append(bp);
-    debugger->echoBreakpoints();
-    return;
-  }
-
-  if(s == "breakpoints.remove" && argc == 1) {
-    unsigned n = decimal(args[0]);
-    if(n < debugger->breakpoints.size()) {
-      debugger->breakpoints.remove(n);
-    }
-    debugger->echoBreakpoints();
-    return;
-  }
-
-  if(s == "breakpoints.reset") {
-    debugger->breakpoints.reset();
-    echo("All breakpoints removed\n");
-    return;
-  }
-
-  if(s == "clear") {
-    reset();
-    return;
-  }
-
-  if(s == "counter") {
-    if(source == Debugger::Source::CPU) echo("CPU instructions executed: ", debugger->cpuInstructionCounter, "\n");
-    if(source == Debugger::Source::SMP) echo("SMP instructions executed: ", debugger->smpInstructionCounter, "\n");
-    return;
-  }
-
-  if(s == "counter.reset") {
-    if(source == Debugger::Source::CPU) { echo("CPU instruction counter reset\n"); debugger->cpuInstructionCounter = 0; }
-    if(source == Debugger::Source::SMP) { echo("SMP instruction counter reset\n"); debugger->smpInstructionCounter = 0; }
-    return;
-  }
-
-  if(s == "disassemble" && argc >= 1 && argc <= 2) {
-    debugger->echoDisassemble(source, hex(args[0]), argc == 2 ? decimal(args[1]) : 16);
-    return;
-  }
-
-  if(s == "export" && argc <= 1) {
-    string filename = {debugger->sourceName(source), "-", string::datetime().transform(" :", "--"), ".ram"};
-    if(argc >= 1) filename = args[0];
-    string pathname = {interface->pathname, "loki/", filename};
-    debugger->memoryExport(source, pathname);
-    return;
-  }
-
-  if(s == "hex" && argc >= 1 && argc <= 2) {
-    debugger->echoHex(source, hex(args[0]), argc == 2 ? decimal(args[1]) : 256);
-    return;
-  }
-
-  if(s == "hotkeys") {
-    echoHotkeys();
-    return;
-  }
-
-  if(s == "hotkeys.append") {
-    lstring part = p.qsplit<1>("=>").strip();
-    if(part.size() == 2) hotkeys.append({part[0], part[1]});
-    echoHotkeys();
-    return;
-  }
-
-  if(s == "hotkeys.remove" && argc == 1) {
-    unsigned n = decimal(args[0]);
-    if(n < hotkeys.size()) hotkeys.remove(n);
-    echoHotkeys();
-    return;
-  }
-
-  if(s == "hotkeys.reset") {
-    hotkeys.reset();
-    echo("All hotkeys removed\n");
-    return;
-  }
-
-  if(s == "power") {
-    emulator->power();
-    echo("System has been power cycled\n");
-    return;
-  }
-
-  if(s == "quit") {
-    Application::quit();
-    return;
-  }
-
-  if(s == "read" && argc == 1) {
-    unsigned addr = hex(args[0]);
-    uint8_t data = debugger->memoryRead(source, addr);
-    echo(debugger->sourceName(source), "/", hex<6>(addr), " = ", hex<2>(data), "\n");
-    return;
-  }
-
-  if(s == "reset") {
-    emulator->reset();
-    echo("System has been reset\n");
-    return;
-  }
-
-  if(s == "run" && argc == 0) {
-    debugger->run();
-    return;
-  }
-
-  if(s == "run.for" && argc == 1) {
-    debugger->run();
-    if(source == Debugger::Source::CPU) debugger->cpuRunFor = (unsigned)decimal(args[0]);
-    if(source == Debugger::Source::SMP) debugger->smpRunFor = (unsigned)decimal(args[0]);
-    return;
-  }
-
-  if(s == "run.to" && argc == 1) {
-    debugger->run();
-    if(source == Debugger::Source::CPU) debugger->cpuRunTo = (unsigned)hex(args[0]);
-    if(source == Debugger::Source::SMP) debugger->smpRunTo = (unsigned)hex(args[0]);
-    return;
-  }
-
-  if(s == "state.load" && argc == 1) {
-    string pathname = {interface->pathname, "loki/state-", args[0], ".bst"};
-    debugger->stateLoad(pathname);
-    return;
-  }
-
-  if(s == "state.save" && argc == 1) {
-    string pathname = {interface->pathname, "loki/state-", args[0], ".bst"};
-    debugger->stateSave(pathname);
-    return;
-  }
-
-  if(s == "step" && argc == 0) {
-    debugger->run();
-    if(source == Debugger::Source::CPU) debugger->cpuStepFor = 1u;
-    if(source == Debugger::Source::SMP) debugger->smpStepFor = 1u;
-    return;
-  }
-
-  if(s == "step.for" && argc == 1) {
-    debugger->run();
-    if(source == Debugger::Source::CPU) debugger->cpuStepFor = (unsigned)decimal(args[0]);
-    if(source == Debugger::Source::SMP) debugger->smpStepFor = (unsigned)decimal(args[0]);
-    return;
-  }
-
-  if(s == "step.to" && argc == 1) {
-    debugger->run();
-    if(source == Debugger::Source::CPU) debugger->cpuStepTo = (unsigned)hex(args[0]);
-    if(source == Debugger::Source::SMP) debugger->smpStepTo = (unsigned)hex(args[0]);
-    return;
-  }
-
-  if(s == "tracer.enable" && argc <= 1) {
-    string filename = {debugger->sourceName(source), "-trace-", string::datetime().transform(" :", "--"), ".log"};
-    if(argc >= 1) filename = args[0];
-    string pathname = {interface->pathname, "loki/", filename};
-    debugger->tracerEnable(source, pathname);
-    return;
-  }
-
-  if(s == "tracer.disable") {
-    debugger->tracerDisable(source);
-    return;
-  }
-
-  if(s == "tracer.mask.enable") {
-    debugger->tracerMaskEnable(source);
-    return;
-  }
-
-  if(s == "tracer.mask.disable") {
-    debugger->tracerMaskDisable(source);
-    return;
-  }
-
-  if(s == "usage.reset") {
-    if(source == Debugger::Source::CPU) memset(debugger->cpuUsage, 0x00, 0x1000000);
-    if(source == Debugger::Source::APU) memset(debugger->apuUsage, 0x00, 0x10000);
-    return;
-  }
-
-  if(s == "write" && argc == 2) {
-    unsigned addr = hex(args[0]);
-    uint8 data = hex(args[1]);
-    debugger->memoryWrite(source, addr, data);
-    echo(debugger->sourceName(source), "/", hex<6>(addr), " = ", hex<2>(data), "\n");
-    return;
-  }
-
-  echo("Error: unrecognized command: ", s, "\n");
-}
-
-void Terminal::echoAliases() {
-  if(aliases.size() == 0) return echo("No aliases defined\n");
-  echo("#    alias\n");
-  echo("---  -----\n");
-  for(unsigned n = 0; n < aliases.size(); n++) {
-    echo(format<-3>(n), "  ", aliases[n].name, " => ", aliases[n].mapping, "\n");
-  }
-}
-
-void Terminal::echoHotkeys() {
-  if(hotkeys.size() == 0) return echo("No hotkeys defined\n");
-  echo("#    hotkey\n");
-  echo("---  ------\n");
-  for(unsigned n = 0; n < hotkeys.size(); n++) {
-    echo(format<-3>(n), "  ", hotkeys[n].name, " => ", hotkeys[n].mapping, "\n");
-  }
-}
-
-void Terminal::inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
-  if(focused() == false) return;
-  if(device.isKeyboard() == false) return;    //only capture keyboard events
-  if(oldValue != 0 || newValue != 1) return;  //only capture key down events
-  string name = device.group[group].input[input].name;
-
-  for(auto& hotkey : hotkeys) {
-    if(name != hotkey.name) continue;
-    command(hotkey.mapping);
-  }
-}
-
-void Terminal::reset() {
-  console.reset();
-}
-
-void Terminal::print(const string& text) {
-  console.print(text);
-}
-
-void Terminal::setColors() {
-  console.setBackgroundColor({
-    (uint8)(settings->terminal.backgroundColor >> 16),
-    (uint8)(settings->terminal.backgroundColor >>  8),
-    (uint8)(settings->terminal.backgroundColor >>  0)
-  });
-
-  console.setForegroundColor({
-    (uint8)(settings->terminal.foregroundColor >> 16),
-    (uint8)(settings->terminal.foregroundColor >>  8),
-    (uint8)(settings->terminal.foregroundColor >>  0)
-  });
-}
diff --git a/target-loki/terminal/terminal.hpp b/target-loki/terminal/terminal.hpp
deleted file mode 100644
index 93f05460..00000000
--- a/target-loki/terminal/terminal.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-struct Terminal : Window {
-  struct Alias {
-    string name;
-    string mapping;
-  };
-
-  struct Hotkey {
-    string name;
-    string mapping;
-  };
-
-  Terminal();
-  void load();
-  void unload();
-
-  void command(string s);
-  void echoAliases();
-  void echoHotkeys();
-  void inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
-  void reset();
-  void print(const string& text);
-  void setColors();
-
-  VerticalLayout layout;
-  Console console;
-
-  vector<Alias> aliases;
-  vector<Hotkey> hotkeys;
-};
-
-extern Terminal* terminal;