From 6ea4bc031fe794bc953ffb5cb159b97980843878 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 26 Dec 2010 23:24:34 +1100 Subject: [PATCH] Update to v073 release. byuu says: This release marks a major step forward, offering full low-level emulation of all four DSP coprocessors based on the NEC uPD77C25 processor core. Many people were responsible for this milestone: Dr. Decapitator for the actual decapping and extraction; Lord Nightmare for the cartridges and some special analysis tools; myself, Jonas Quinn and Cydrak for the uPD77C25 emulation; and all of the donors who raised the necessary $1,000 for the necessary hardware and equipment needed to pull this all off. To say thanks to the donors, I am releasing the uPD77C25 emulation core to the public domain, so that everyone can benefit from it. All four DSP emulations will be improved by this by way of having realistic timing; the DSP-4 will benefit further as the high-level emulation was incomplete and somewhat buggy; and the DSP-3 will benefit the most as the high-levle emulation there was not complete enough to be playable. As a result, most notably, this means bsnes v073 is the first emulator to fully be able to play SD Gundam GX (J)! As bsnes' primary goal is accuracy, the LLE DSP support renders the old HLE DSP support obsolete. Ergo, I have removed the 166KB of HLE source code, and replaced it with the uPD77C25 core, which comprises a mere 20KB of source code. As this LLE module supports save states, this also means that for the first time, DSP-3 and DSP-4 games have save state support. On the other hand, this also means that to run any DSP game, you will need the appropriate program ROM. As these are copyrighted, I cannot distribute them nor tell you where to get them. All I can do is provide you with the necessary filenames and hashes. Changelog (since v072 release): * added NEC uPD77C25 emulation core * added low-level emulation of the DSP-1, DSP-1B, DSP-2, DSP-3, DSP-4 coprocessors * removed high-level emulation of the DSP-n coprocessors * added blargg's libco::ppc.c module, which is far more portable, even running on the PS3 * added software filter support via binary plugins * added debugger (currently Linux-only); but it is as yet unstable * added pause shortcut * updated mightymo's cheat code database --- bsnes/Makefile | 2 +- bsnes/data/cheats.xml | 1467 ++++++++++------- bsnes/libco/libco.c | 4 +- bsnes/libco/libco.h | 2 +- bsnes/libco/ppc-elf.c | 325 ---- bsnes/libco/ppc.c | 407 +++++ bsnes/libco/ppc.s | 478 ------ bsnes/libco/ppc64.s | 513 ------ bsnes/snes/alt/cpu/debugger/debugger.cpp | 36 +- bsnes/snes/dsp/debugger/debugger.cpp | 14 +- bsnes/snes/snes.hpp | 6 +- .../GLSL/Curvature.shader | 6 +- {pixelshaders => shaders}/GLSL/HDR-TV.shader | 0 {pixelshaders => shaders}/GLSL/HQ2x.shader | 0 .../GLSL/Pixellate.shader | 0 {pixelshaders => shaders}/GLSL/Scale2x.shader | 0 {pixelshaders => shaders}/HLSL/Sepia.shader | 0 snespurify/cc-gtk.sh | 4 + snespurify/cc-windows.bat | 7 + snespurify/nall/Makefile | 109 ++ snespurify/nall/algorithm.hpp | 17 + snespurify/nall/any.hpp | 74 + snespurify/nall/array.hpp | 141 ++ snespurify/nall/base64.hpp | 90 + snespurify/nall/bit.hpp | 51 + snespurify/nall/concept.hpp | 34 + snespurify/nall/config.hpp | 123 ++ snespurify/nall/crc32.hpp | 66 + snespurify/nall/detect.hpp | 30 + snespurify/nall/dictionary.hpp | 75 + snespurify/nall/directory.hpp | 151 ++ snespurify/nall/dl.hpp | 96 ++ snespurify/nall/endian.hpp | 38 + snespurify/nall/file.hpp | 261 +++ snespurify/nall/filemap.hpp | 200 +++ snespurify/nall/foreach.hpp | 12 + snespurify/nall/function.hpp | 60 + snespurify/nall/input.hpp | 386 +++++ snespurify/nall/lzss.hpp | 81 + snespurify/nall/moduloarray.hpp | 40 + snespurify/nall/platform.hpp | 122 ++ snespurify/nall/priorityqueue.hpp | 109 ++ snespurify/nall/property.hpp | 91 + snespurify/nall/random.hpp | 20 + snespurify/nall/serial.hpp | 85 + snespurify/nall/serializer.hpp | 146 ++ snespurify/nall/sha256.hpp | 143 ++ snespurify/nall/snes/info.hpp | 869 ++++++++++ snespurify/nall/sort.hpp | 62 + snespurify/nall/static.hpp | 20 + snespurify/nall/stdint.hpp | 44 + snespurify/nall/string.hpp | 32 + snespurify/nall/string/base.hpp | 159 ++ snespurify/nall/string/bsv.hpp | 75 + snespurify/nall/string/cast.hpp | 32 + snespurify/nall/string/compare.hpp | 110 ++ snespurify/nall/string/convert.hpp | 153 ++ snespurify/nall/string/core.hpp | 139 ++ snespurify/nall/string/filename.hpp | 63 + snespurify/nall/string/math.hpp | 164 ++ snespurify/nall/string/platform.hpp | 41 + snespurify/nall/string/replace.hpp | 103 ++ snespurify/nall/string/split.hpp | 58 + snespurify/nall/string/strl.hpp | 52 + snespurify/nall/string/strpos.hpp | 41 + snespurify/nall/string/trim.hpp | 38 + snespurify/nall/string/utility.hpp | 157 ++ snespurify/nall/string/variadic.hpp | 12 + snespurify/nall/string/wrapper.hpp | 33 + snespurify/nall/string/xml.hpp | 266 +++ snespurify/nall/ups.hpp | 223 +++ snespurify/nall/utf8.hpp | 86 + snespurify/nall/utility.hpp | 39 + snespurify/nall/varint.hpp | 92 ++ snespurify/nall/vector.hpp | 281 ++++ snespurify/phoenix/cc-gtk.sh | 4 + snespurify/phoenix/cc-qt.sh | 6 + snespurify/phoenix/cc-windows.bat | 6 + snespurify/phoenix/gtk/button.cpp | 13 + snespurify/phoenix/gtk/canvas.cpp | 59 + snespurify/phoenix/gtk/checkbox.cpp | 23 + snespurify/phoenix/gtk/combobox.cpp | 48 + snespurify/phoenix/gtk/editbox.cpp | 49 + snespurify/phoenix/gtk/font.cpp | 18 + snespurify/phoenix/gtk/gtk.cpp | 189 +++ snespurify/phoenix/gtk/gtk.hpp | 265 +++ snespurify/phoenix/gtk/horizontalslider.cpp | 25 + snespurify/phoenix/gtk/label.cpp | 13 + snespurify/phoenix/gtk/listbox.cpp | 195 +++ snespurify/phoenix/gtk/menu.cpp | 129 ++ snespurify/phoenix/gtk/messagewindow.cpp | 65 + snespurify/phoenix/gtk/object.cpp | 57 + snespurify/phoenix/gtk/progressbar.cpp | 12 + snespurify/phoenix/gtk/radiobox.cpp | 36 + snespurify/phoenix/gtk/textbox.cpp | 33 + snespurify/phoenix/gtk/verticalslider.cpp | 25 + snespurify/phoenix/gtk/viewport.cpp | 20 + snespurify/phoenix/gtk/widget.cpp | 47 + snespurify/phoenix/gtk/window.cpp | 99 ++ snespurify/phoenix/nall/Makefile | 109 ++ snespurify/phoenix/nall/algorithm.hpp | 17 + snespurify/phoenix/nall/any.hpp | 74 + snespurify/phoenix/nall/array.hpp | 141 ++ snespurify/phoenix/nall/base64.hpp | 90 + snespurify/phoenix/nall/bit.hpp | 51 + snespurify/phoenix/nall/concept.hpp | 34 + snespurify/phoenix/nall/config.hpp | 123 ++ snespurify/phoenix/nall/crc32.hpp | 66 + snespurify/phoenix/nall/detect.hpp | 30 + snespurify/phoenix/nall/dictionary.hpp | 75 + snespurify/phoenix/nall/directory.hpp | 151 ++ snespurify/phoenix/nall/dl.hpp | 96 ++ snespurify/phoenix/nall/endian.hpp | 38 + snespurify/phoenix/nall/file.hpp | 261 +++ snespurify/phoenix/nall/filemap.hpp | 200 +++ snespurify/phoenix/nall/foreach.hpp | 12 + snespurify/phoenix/nall/function.hpp | 60 + snespurify/phoenix/nall/input.hpp | 386 +++++ snespurify/phoenix/nall/lzss.hpp | 81 + snespurify/phoenix/nall/moduloarray.hpp | 40 + snespurify/phoenix/nall/platform.hpp | 122 ++ snespurify/phoenix/nall/priorityqueue.hpp | 109 ++ snespurify/phoenix/nall/property.hpp | 91 + snespurify/phoenix/nall/random.hpp | 20 + snespurify/phoenix/nall/serial.hpp | 85 + snespurify/phoenix/nall/serializer.hpp | 146 ++ snespurify/phoenix/nall/sha256.hpp | 143 ++ snespurify/phoenix/nall/snes/info.hpp | 864 ++++++++++ snespurify/phoenix/nall/sort.hpp | 62 + snespurify/phoenix/nall/static.hpp | 20 + snespurify/phoenix/nall/stdint.hpp | 44 + snespurify/phoenix/nall/string.hpp | 32 + snespurify/phoenix/nall/string/base.hpp | 159 ++ snespurify/phoenix/nall/string/bsv.hpp | 75 + snespurify/phoenix/nall/string/cast.hpp | 32 + snespurify/phoenix/nall/string/compare.hpp | 110 ++ snespurify/phoenix/nall/string/convert.hpp | 153 ++ snespurify/phoenix/nall/string/core.hpp | 139 ++ snespurify/phoenix/nall/string/filename.hpp | 63 + snespurify/phoenix/nall/string/math.hpp | 164 ++ snespurify/phoenix/nall/string/platform.hpp | 41 + snespurify/phoenix/nall/string/replace.hpp | 103 ++ snespurify/phoenix/nall/string/split.hpp | 58 + snespurify/phoenix/nall/string/strl.hpp | 52 + snespurify/phoenix/nall/string/strpos.hpp | 41 + snespurify/phoenix/nall/string/trim.hpp | 38 + snespurify/phoenix/nall/string/utility.hpp | 157 ++ snespurify/phoenix/nall/string/variadic.hpp | 12 + snespurify/phoenix/nall/string/wrapper.hpp | 33 + snespurify/phoenix/nall/string/xml.hpp | 266 +++ snespurify/phoenix/nall/ups.hpp | 223 +++ snespurify/phoenix/nall/utf8.hpp | 86 + snespurify/phoenix/nall/utility.hpp | 39 + snespurify/phoenix/nall/varint.hpp | 92 ++ snespurify/phoenix/nall/vector.hpp | 281 ++++ snespurify/phoenix/phoenix.cpp | 17 + snespurify/phoenix/phoenix.hpp | 15 + snespurify/phoenix/qt/button.cpp | 13 + snespurify/phoenix/qt/canvas.cpp | 39 + snespurify/phoenix/qt/checkbox.cpp | 21 + snespurify/phoenix/qt/combobox.cpp | 38 + snespurify/phoenix/qt/editbox.cpp | 29 + snespurify/phoenix/qt/font.cpp | 14 + snespurify/phoenix/qt/horizontalslider.cpp | 22 + snespurify/phoenix/qt/label.cpp | 16 + snespurify/phoenix/qt/listbox.cpp | 102 ++ snespurify/phoenix/qt/menu.cpp | 169 ++ snespurify/phoenix/qt/messagewindow.cpp | 45 + snespurify/phoenix/qt/object.cpp | 7 + snespurify/phoenix/qt/progressbar.cpp | 16 + snespurify/phoenix/qt/qt.cpp | 128 ++ snespurify/phoenix/qt/qt.hpp | 337 ++++ snespurify/phoenix/qt/qt.moc | 930 +++++++++++ snespurify/phoenix/qt/qt.moc.hpp | 311 ++++ snespurify/phoenix/qt/radiobox.cpp | 37 + snespurify/phoenix/qt/textbox.cpp | 26 + snespurify/phoenix/qt/verticalslider.cpp | 24 + snespurify/phoenix/qt/viewport.cpp | 16 + snespurify/phoenix/qt/widget.cpp | 35 + snespurify/phoenix/qt/window.cpp | 77 + snespurify/phoenix/sync.sh | 8 + snespurify/phoenix/windows/button.cpp | 10 + snespurify/phoenix/windows/canvas.cpp | 60 + snespurify/phoenix/windows/checkbox.cpp | 18 + snespurify/phoenix/windows/combobox.cpp | 46 + snespurify/phoenix/windows/editbox.cpp | 53 + snespurify/phoenix/windows/font.cpp | 26 + .../phoenix/windows/horizontalslider.cpp | 25 + snespurify/phoenix/windows/label.cpp | 51 + snespurify/phoenix/windows/listbox.cpp | 114 ++ snespurify/phoenix/windows/menu.cpp | 144 ++ snespurify/phoenix/windows/messagewindow.cpp | 41 + snespurify/phoenix/windows/object.cpp | 87 + snespurify/phoenix/windows/phoenix.Manifest | 9 + snespurify/phoenix/windows/phoenix.rc | 1 + snespurify/phoenix/windows/progressbar.cpp | 18 + snespurify/phoenix/windows/radiobox.cpp | 42 + snespurify/phoenix/windows/textbox.cpp | 28 + snespurify/phoenix/windows/verticalslider.cpp | 25 + snespurify/phoenix/windows/viewport.cpp | 17 + snespurify/phoenix/windows/widget.cpp | 40 + snespurify/phoenix/windows/window.cpp | 96 ++ snespurify/phoenix/windows/windows.cpp | 468 ++++++ snespurify/phoenix/windows/windows.hpp | 288 ++++ snespurify/snespurify.cpp | 400 +++++ snespurify/sync.sh | 10 + 206 files changed, 19998 insertions(+), 1903 deletions(-) delete mode 100755 bsnes/libco/ppc-elf.c create mode 100755 bsnes/libco/ppc.c delete mode 100755 bsnes/libco/ppc.s delete mode 100755 bsnes/libco/ppc64.s rename {pixelshaders => shaders}/GLSL/Curvature.shader (61%) rename {pixelshaders => shaders}/GLSL/HDR-TV.shader (100%) rename {pixelshaders => shaders}/GLSL/HQ2x.shader (100%) rename {pixelshaders => shaders}/GLSL/Pixellate.shader (100%) rename {pixelshaders => shaders}/GLSL/Scale2x.shader (100%) rename {pixelshaders => shaders}/HLSL/Sepia.shader (100%) create mode 100755 snespurify/cc-gtk.sh create mode 100755 snespurify/cc-windows.bat create mode 100755 snespurify/nall/Makefile create mode 100755 snespurify/nall/algorithm.hpp create mode 100755 snespurify/nall/any.hpp create mode 100755 snespurify/nall/array.hpp create mode 100755 snespurify/nall/base64.hpp create mode 100755 snespurify/nall/bit.hpp create mode 100755 snespurify/nall/concept.hpp create mode 100755 snespurify/nall/config.hpp create mode 100755 snespurify/nall/crc32.hpp create mode 100755 snespurify/nall/detect.hpp create mode 100755 snespurify/nall/dictionary.hpp create mode 100755 snespurify/nall/directory.hpp create mode 100755 snespurify/nall/dl.hpp create mode 100755 snespurify/nall/endian.hpp create mode 100755 snespurify/nall/file.hpp create mode 100755 snespurify/nall/filemap.hpp create mode 100755 snespurify/nall/foreach.hpp create mode 100755 snespurify/nall/function.hpp create mode 100755 snespurify/nall/input.hpp create mode 100755 snespurify/nall/lzss.hpp create mode 100755 snespurify/nall/moduloarray.hpp create mode 100755 snespurify/nall/platform.hpp create mode 100755 snespurify/nall/priorityqueue.hpp create mode 100755 snespurify/nall/property.hpp create mode 100755 snespurify/nall/random.hpp create mode 100755 snespurify/nall/serial.hpp create mode 100755 snespurify/nall/serializer.hpp create mode 100755 snespurify/nall/sha256.hpp create mode 100755 snespurify/nall/snes/info.hpp create mode 100755 snespurify/nall/sort.hpp create mode 100755 snespurify/nall/static.hpp create mode 100755 snespurify/nall/stdint.hpp create mode 100755 snespurify/nall/string.hpp create mode 100755 snespurify/nall/string/base.hpp create mode 100755 snespurify/nall/string/bsv.hpp create mode 100755 snespurify/nall/string/cast.hpp create mode 100755 snespurify/nall/string/compare.hpp create mode 100755 snespurify/nall/string/convert.hpp create mode 100755 snespurify/nall/string/core.hpp create mode 100755 snespurify/nall/string/filename.hpp create mode 100755 snespurify/nall/string/math.hpp create mode 100755 snespurify/nall/string/platform.hpp create mode 100755 snespurify/nall/string/replace.hpp create mode 100755 snespurify/nall/string/split.hpp create mode 100755 snespurify/nall/string/strl.hpp create mode 100755 snespurify/nall/string/strpos.hpp create mode 100755 snespurify/nall/string/trim.hpp create mode 100755 snespurify/nall/string/utility.hpp create mode 100755 snespurify/nall/string/variadic.hpp create mode 100755 snespurify/nall/string/wrapper.hpp create mode 100755 snespurify/nall/string/xml.hpp create mode 100755 snespurify/nall/ups.hpp create mode 100755 snespurify/nall/utf8.hpp create mode 100755 snespurify/nall/utility.hpp create mode 100755 snespurify/nall/varint.hpp create mode 100755 snespurify/nall/vector.hpp create mode 100755 snespurify/phoenix/cc-gtk.sh create mode 100755 snespurify/phoenix/cc-qt.sh create mode 100755 snespurify/phoenix/cc-windows.bat create mode 100755 snespurify/phoenix/gtk/button.cpp create mode 100755 snespurify/phoenix/gtk/canvas.cpp create mode 100755 snespurify/phoenix/gtk/checkbox.cpp create mode 100755 snespurify/phoenix/gtk/combobox.cpp create mode 100755 snespurify/phoenix/gtk/editbox.cpp create mode 100755 snespurify/phoenix/gtk/font.cpp create mode 100755 snespurify/phoenix/gtk/gtk.cpp create mode 100755 snespurify/phoenix/gtk/gtk.hpp create mode 100755 snespurify/phoenix/gtk/horizontalslider.cpp create mode 100755 snespurify/phoenix/gtk/label.cpp create mode 100755 snespurify/phoenix/gtk/listbox.cpp create mode 100755 snespurify/phoenix/gtk/menu.cpp create mode 100755 snespurify/phoenix/gtk/messagewindow.cpp create mode 100755 snespurify/phoenix/gtk/object.cpp create mode 100755 snespurify/phoenix/gtk/progressbar.cpp create mode 100755 snespurify/phoenix/gtk/radiobox.cpp create mode 100755 snespurify/phoenix/gtk/textbox.cpp create mode 100755 snespurify/phoenix/gtk/verticalslider.cpp create mode 100755 snespurify/phoenix/gtk/viewport.cpp create mode 100755 snespurify/phoenix/gtk/widget.cpp create mode 100755 snespurify/phoenix/gtk/window.cpp create mode 100755 snespurify/phoenix/nall/Makefile create mode 100755 snespurify/phoenix/nall/algorithm.hpp create mode 100755 snespurify/phoenix/nall/any.hpp create mode 100755 snespurify/phoenix/nall/array.hpp create mode 100755 snespurify/phoenix/nall/base64.hpp create mode 100755 snespurify/phoenix/nall/bit.hpp create mode 100755 snespurify/phoenix/nall/concept.hpp create mode 100755 snespurify/phoenix/nall/config.hpp create mode 100755 snespurify/phoenix/nall/crc32.hpp create mode 100755 snespurify/phoenix/nall/detect.hpp create mode 100755 snespurify/phoenix/nall/dictionary.hpp create mode 100755 snespurify/phoenix/nall/directory.hpp create mode 100755 snespurify/phoenix/nall/dl.hpp create mode 100755 snespurify/phoenix/nall/endian.hpp create mode 100755 snespurify/phoenix/nall/file.hpp create mode 100755 snespurify/phoenix/nall/filemap.hpp create mode 100755 snespurify/phoenix/nall/foreach.hpp create mode 100755 snespurify/phoenix/nall/function.hpp create mode 100755 snespurify/phoenix/nall/input.hpp create mode 100755 snespurify/phoenix/nall/lzss.hpp create mode 100755 snespurify/phoenix/nall/moduloarray.hpp create mode 100755 snespurify/phoenix/nall/platform.hpp create mode 100755 snespurify/phoenix/nall/priorityqueue.hpp create mode 100755 snespurify/phoenix/nall/property.hpp create mode 100755 snespurify/phoenix/nall/random.hpp create mode 100755 snespurify/phoenix/nall/serial.hpp create mode 100755 snespurify/phoenix/nall/serializer.hpp create mode 100755 snespurify/phoenix/nall/sha256.hpp create mode 100755 snespurify/phoenix/nall/snes/info.hpp create mode 100755 snespurify/phoenix/nall/sort.hpp create mode 100755 snespurify/phoenix/nall/static.hpp create mode 100755 snespurify/phoenix/nall/stdint.hpp create mode 100755 snespurify/phoenix/nall/string.hpp create mode 100755 snespurify/phoenix/nall/string/base.hpp create mode 100755 snespurify/phoenix/nall/string/bsv.hpp create mode 100755 snespurify/phoenix/nall/string/cast.hpp create mode 100755 snespurify/phoenix/nall/string/compare.hpp create mode 100755 snespurify/phoenix/nall/string/convert.hpp create mode 100755 snespurify/phoenix/nall/string/core.hpp create mode 100755 snespurify/phoenix/nall/string/filename.hpp create mode 100755 snespurify/phoenix/nall/string/math.hpp create mode 100755 snespurify/phoenix/nall/string/platform.hpp create mode 100755 snespurify/phoenix/nall/string/replace.hpp create mode 100755 snespurify/phoenix/nall/string/split.hpp create mode 100755 snespurify/phoenix/nall/string/strl.hpp create mode 100755 snespurify/phoenix/nall/string/strpos.hpp create mode 100755 snespurify/phoenix/nall/string/trim.hpp create mode 100755 snespurify/phoenix/nall/string/utility.hpp create mode 100755 snespurify/phoenix/nall/string/variadic.hpp create mode 100755 snespurify/phoenix/nall/string/wrapper.hpp create mode 100755 snespurify/phoenix/nall/string/xml.hpp create mode 100755 snespurify/phoenix/nall/ups.hpp create mode 100755 snespurify/phoenix/nall/utf8.hpp create mode 100755 snespurify/phoenix/nall/utility.hpp create mode 100755 snespurify/phoenix/nall/varint.hpp create mode 100755 snespurify/phoenix/nall/vector.hpp create mode 100755 snespurify/phoenix/phoenix.cpp create mode 100755 snespurify/phoenix/phoenix.hpp create mode 100755 snespurify/phoenix/qt/button.cpp create mode 100755 snespurify/phoenix/qt/canvas.cpp create mode 100755 snespurify/phoenix/qt/checkbox.cpp create mode 100755 snespurify/phoenix/qt/combobox.cpp create mode 100755 snespurify/phoenix/qt/editbox.cpp create mode 100755 snespurify/phoenix/qt/font.cpp create mode 100755 snespurify/phoenix/qt/horizontalslider.cpp create mode 100755 snespurify/phoenix/qt/label.cpp create mode 100755 snespurify/phoenix/qt/listbox.cpp create mode 100755 snespurify/phoenix/qt/menu.cpp create mode 100755 snespurify/phoenix/qt/messagewindow.cpp create mode 100755 snespurify/phoenix/qt/object.cpp create mode 100755 snespurify/phoenix/qt/progressbar.cpp create mode 100755 snespurify/phoenix/qt/qt.cpp create mode 100755 snespurify/phoenix/qt/qt.hpp create mode 100755 snespurify/phoenix/qt/qt.moc create mode 100755 snespurify/phoenix/qt/qt.moc.hpp create mode 100755 snespurify/phoenix/qt/radiobox.cpp create mode 100755 snespurify/phoenix/qt/textbox.cpp create mode 100755 snespurify/phoenix/qt/verticalslider.cpp create mode 100755 snespurify/phoenix/qt/viewport.cpp create mode 100755 snespurify/phoenix/qt/widget.cpp create mode 100755 snespurify/phoenix/qt/window.cpp create mode 100755 snespurify/phoenix/sync.sh create mode 100755 snespurify/phoenix/windows/button.cpp create mode 100755 snespurify/phoenix/windows/canvas.cpp create mode 100755 snespurify/phoenix/windows/checkbox.cpp create mode 100755 snespurify/phoenix/windows/combobox.cpp create mode 100755 snespurify/phoenix/windows/editbox.cpp create mode 100755 snespurify/phoenix/windows/font.cpp create mode 100755 snespurify/phoenix/windows/horizontalslider.cpp create mode 100755 snespurify/phoenix/windows/label.cpp create mode 100755 snespurify/phoenix/windows/listbox.cpp create mode 100755 snespurify/phoenix/windows/menu.cpp create mode 100755 snespurify/phoenix/windows/messagewindow.cpp create mode 100755 snespurify/phoenix/windows/object.cpp create mode 100755 snespurify/phoenix/windows/phoenix.Manifest create mode 100755 snespurify/phoenix/windows/phoenix.rc create mode 100755 snespurify/phoenix/windows/progressbar.cpp create mode 100755 snespurify/phoenix/windows/radiobox.cpp create mode 100755 snespurify/phoenix/windows/textbox.cpp create mode 100755 snespurify/phoenix/windows/verticalslider.cpp create mode 100755 snespurify/phoenix/windows/viewport.cpp create mode 100755 snespurify/phoenix/windows/widget.cpp create mode 100755 snespurify/phoenix/windows/window.cpp create mode 100755 snespurify/phoenix/windows/windows.cpp create mode 100755 snespurify/phoenix/windows/windows.hpp create mode 100755 snespurify/snespurify.cpp create mode 100755 snespurify/sync.sh diff --git a/bsnes/Makefile b/bsnes/Makefile index 06a58342..a820d46f 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -1,6 +1,6 @@ include nall/Makefile snes := snes -profile := compatibility +profile := accuracy ui := ui-phoenix # compiler diff --git a/bsnes/data/cheats.xml b/bsnes/data/cheats.xml index 4cd1423e..3cd2ef6a 100755 --- a/bsnes/data/cheats.xml +++ b/bsnes/data/cheats.xml @@ -3360,16 +3360,6 @@ Arcana (USA) - - Start with 60,000 gold pieces - 1D69-AFD0 - 3C69-AF00 - - - Start with 250 gold pieces - EC69-AFD0 - DD69-AF00 - Infinite money for weapons C225-0F02 @@ -3387,13 +3377,34 @@ C22F-A7DE - Level and statistics for all characters in group are increased after each battle you win + Level and statistics for all characters in group are increased after each battle B387-DFF2 Magic points don't decrease 828B-AF2E + + Start with 60,000 gold pieces + 1D69-AFD0 + 3C69-AF00 + + + Start with 250 gold pieces + EC69-AFD0 + DD69-AF00 + + + + Archer Maclean's Super Dropzone (Europe) + + Infinite lives + 7E002504 + + + Infinite bombs + 7E002604 + Ardy Lightfoot (USA) @@ -4290,10 +4301,6 @@ Battle Blaze (USA) - - Infinite continues - C285-D4AB - Infinite health (disable at end of round) - P1 7E06DCC0 @@ -4306,6 +4313,14 @@ No health - P1 7E06DC00 + + No health - P2 + 7E070400 + + + Infinite continues + C285-D4AB + Battle Clash (USA) @@ -4595,6 +4610,83 @@ 8982-CFD1 + + Battletoads-Double Dragon (USA) + + Invincibility (blinking) - P1 + 7E11127F + + + Infinite health - P1 + 7E003A2F + + + Infinite lives - P1 + 7E002605 + + + Infinite lives + 40B8-04AF + + + Enemies have less health + DE6E-1466 + + + Abobo has less health + 4EB7-1DD6 + + + Big Blag has less health + 4EB3-C4DB + + + Roper has less health + D7BA-3FA8 + + + Robo-Manus has less health + D7C5-3F66 + + + Start with 2 lives + DF60-D76D + + + Start with 10 lives + DB60-D76D + + + Start on level 2 with 11 lives + DD65-DD0D + CB66-D46D + DF66-D4AD + + + Start on level 3 with 11 lives + DD65-DD0D + CB66-D46D + D466-D4AD + + + Start on level 4 with 11 lives + DD65-DD0D + CB66-D46D + D766-D4AD + + + Start on level 5 with 11 lives + DD65-DD0D + CB66-D46D + D066-D4AD + + + Start on level 6 with 11 lives + DD65-DD0D + CB66-D46D + D966-D4AD + + Bazooka Blitzkrieg (USA) @@ -4996,6 +5088,21 @@ C262-0D62 + + BioMetal (USA) + + Infinite lives + C26E-6D02 + + + Infinite lives (alt) + 7E029D0A + + + Infinite charge + C262-0D62 + + Bishin Densetsu Zoku (Japan) @@ -7756,17 +7863,21 @@ Castlevania - Dracula X (USA) - Invincibility (turn on after title screen, during game play) - 10D2-FE86 + Invincibility + 2D69-CD9D Invincibility after one hit C969-CD2D - Infinite energy + Infinite health C96D-17FF + + Infinite lives + C9AF-47A7 + All hearts worth 99 2D25-1FD3 @@ -7840,10 +7951,6 @@ Start with 99 lives BB85-1D6F - - Infinite lives - C9AF-47A7 - Start with 25 hearts after you die 49A4-44D7 @@ -8990,26 +9097,6 @@ Infinite bombs (top-view levels) 22B8-0766 - - Start with 5 bombs on each life (side-view levels) - D9BB-AFA1 - D9CE-6D0D - - - Start with 9 bombs on each life (side-view levels) - DBBB-AFA1 - DBCE-6D0D - - - Start with 5 bombs on each life (top-view levels) - D963-6708 - D9CE-6D0F - - - Start with 9 bombs on each life (top-view levels) - DB63-6708 - DBCE-6D0F - Enable 30 and 99 lives in option menu (99 actually gives 35,081 lives) D987-AFA4 @@ -9062,6 +9149,26 @@ Always have Laser for gun 2 (disable during bonus stages) 7E1F8605 + + Start with 5 bombs on each life (side-view levels) + D9BB-AFA1 + D9CE-6D0D + + + Start with 9 bombs on each life (side-view levels) + DBBB-AFA1 + DBCE-6D0D + + + Start with 5 bombs on each life (top-view levels) + D963-6708 + D9CE-6D0F + + + Start with 9 bombs on each life (top-view levels) + DB63-6708 + DBCE-6D0F + Start on stage 2 7E008602 @@ -9128,6 +9235,10 @@ Cool World (USA) + + Invincibility (blinking) + 7E02BFFF + Infinite lives C2B6-D4DC @@ -9136,6 +9247,10 @@ Infinite continues C2B2-DF67 + + Infinite Nickels + 7E00ED63 + Start with 1 life DFBE-DD07 @@ -11065,10 +11180,6 @@ Infinite lives C2C1-4A2C - - Start with 255 lives - EE68-C33D - Easy level exit (press start and select) DDB0-34A4 @@ -11095,56 +11206,67 @@ 166A-3ECD - High-jump Donkey Kong + Multi-jump + 6D89-3A8A + 4080-13E3 + 6D89-1A73 + 2D89-13E3 + + + Multi-jump - Expresso (the Ostrich) + EE81-CEE3 + + + High-jump - Donkey Kong A086-13E3 - Super-jump Donkey Kong + Super-jump - Donkey Kong 2D86-13E3 - Mega-jump Donkey Kong + Mega-jump - Donkey Kong 3D86-13E3 - Moon-jump Donkey Kong + Moon-jump - Donkey Kong EE86-13E3 - High-jump Diddy Kong + High-jump - Diddy Kong 808B-1AE3 - Super-jump Diddy Kong + Super-jump - Diddy Kong AD8B-1AE3 - Mega-jump Diddy Kong + Mega-jump - Diddy Kong 2D8B-1AE3 - Moon-jump Diddy Kong + Moon-jump - Diddy Kong EE8B-1AE3 - High-jump Animals + High-jump - Animals A087-C3E3 - Super-jump Animals + Super-jump - Animals 2D87-C3E3 - Mega-jump Animals + Mega-jump - Animals 3087-C3E3 - Moon-jump Animals + Moon-jump - Animals EE87-C3E3 - Expresso Multi-jump (jump in mid-air) - EE81-CEE3 + Start with 255 lives + EE68-C33D @@ -11163,10 +11285,6 @@ Infinite lives C2C5-4A2C - - 255 lives - EE63-333D - Easy level exit (press start and select) DDB9-3764 @@ -11188,56 +11306,67 @@ 7E057901 - High-jump for Donkey Kong + Multi-jump + 6D85-327A + 4081-1E53 + 6D85-1A83 + 2D85-1E53 + + + Multi-jump - Expresso (the Ostrich) + EE8B-CA53 + + + High-jump - Donkey Kong A08C-1E53 - Super-jump for Donkey Kong + Super-jump - Donkey Kong 2D8C-1E53 - Mega-jump for Donkey Kong + Mega-jump - Donkey Kong 308C-1E53 - Moon-jump for Donkey Kong + Moon-jump - Donkey Kong EE8C-1E53 - High-jump for Diddy Kong + High-jump - Diddy Kong 8088-1253 - Super-jump for Diddy Kong + Super-jump - Diddy Kong AD88-1253 - Mega-jump for Diddy Kong + Mega-jump - Diddy Kong 2D88-1253 - Moon-jump for Diddy Kong + Moon-jump - Diddy Kong EE88-1253 - High-jump for Animals + High-jump - Animals A089-CE53 - Super-jump for Animals + Super-jump - Animals 2D89-CE53 - Mega-jump for Animals + Mega-jump - Animals 3089-CE53 - Moon-jump for Animals + Moon-jump - Animals EE89-CE53 - Multi-jump in mid-air - EE8B-CA53 + Start with 255 lives + EE63-333D @@ -11266,45 +11395,6 @@ C2C9-4E2C C2C1-4A9C - - Start With 8 lives - D568-C34D - D568-C33D - - - Start With 11 lives - DC68-C34D - DC68-C33D - - - Start With 16 lives - DE68-C34D - DE68-C33D - - - Start With 26 lives - FB68-C34D - FB68-C33D - - - Start With 51 lives - 7468-C34D - 7468-C33D - - - Start With 76 lives - 0868-C34D - 0868-C33D - - - Start With 100 lives - 1768-C34D - 1768-C33D - - - Start with 255 lives - EE68-C34D - 10 Bananas needed for extra life DBC1-3D6D @@ -11381,101 +11471,152 @@ 166A-3ECD - High-jump for Donkey Kong + Multi-jump + 6D84-33EA + 4084-1273 + 6D84-1353 + 2D87-1273 + + + Multi-jump - Expresso (the Ostrich) + EE80-C373 + + + High-jump - Donkey Kong A081-1273 - Super-jump for Donkey Kong + Super-jump - Donkey Kong 2D81-1273 - Mega-jump for Donkey Kong + Mega-jump - Donkey Kong 3D81-1273 - Moon-jump for Donkey Kong + Moon-jump - Donkey Kong EE81-1273 - High-jump for Diddy Kong + High-jump - Diddy Kong 8081-1E73 - Super-jump for Diddy Kong + Super-jump - Diddy Kong AD81-1E73 - Mega-jump for Diddy Kong + Mega-jump - Diddy Kong 2D81-1E73 - Moon-jump for Diddy Kong + Moon-jump - Diddy Kong EE81-1E73 - High-jump for Animals + High-jump - Animals A08F-C273 - Super-jump for Animals + Super-jump - Animals 2D8F-C273 - Mega-jump for Animals + Mega-jump - Animals 3D8F-C273 - Moon-jump for Animals + Moon-jump - Animals EE8F-C273 - High-jump for Donkey Kong + High-jump - Donkey Kong A081-1273 A086-13E3 - Super-jump for Donkey Kong + Super-jump - Donkey Kong 2D81-1273 2D86-13E3 - Mega-Jump for Donkey Kong + Mega-Jump - Donkey Kong 3D81-1273 3D86-13E3 - High-jump for Diddy Kong + High-jump - Diddy Kong 8081-1E73 808B-1AE3 - Super-jump for Diddy Kong + Super-jump - Diddy Kong AD81-1E73 AD8B-1AE3 - Mega-Jump for Diddy Kong + Mega-Jump - Diddy Kong 2D81-1E73 2D8B-1AE3 - High-jump for all Animals + High-jump - all Animals A08F-C273 A087-C3E3 - Super-jump for all Animals + Super-jump - all Animals 2D8F-C273 2D87-C3E3 - Mega-jump for all Animals + Mega-jump - all Animals 3D8F-C273 3087-C3E3 - Expresso Multi-jump (jump in mid-air) - EE80-C373 + Start With 8 lives + D568-C34D + D568-C33D + + + Start With 11 lives + DC68-C34D + DC68-C33D + + + Start With 16 lives + DE68-C34D + DE68-C33D + + + Start With 26 lives + FB68-C34D + FB68-C33D + + + Start With 51 lives + 7468-C34D + 7468-C33D + + + Start With 76 lives + 0868-C34D + 0868-C33D + + + Start With 100 lives + 1768-C34D + 1768-C33D + + + Start with 255 lives + EE68-C34D + + + Infinite time in bonus stages + 7E137F99 + 7E13F399 @@ -11491,6 +11632,104 @@ C2A1-CE5B C2A5-C37B + + Get 2 lives per 100 bananas collected + D4A1-437C + D4A5-4AEC + + + Get 5 lives per 100 bananas collected + D9A1-437C + D9A5-4AEC + + + Get 255 lives per 100 bananas collected + EEA1-437C + + + When your last Kong is hit the other returns + EEC2-1A1D + EECC-CA4D + + + Easy level exit (press start and select) + DD6C-C7D4 + DD62-C4A4 + + + Kong family coins don't get used up + C2B9-13B7 + C2B1-13F7 + + + Kremcoins don't get used up + C2B9-1297 + C2B1-1A27 + + + Mega-jump - Diddy + EDD0-735A + + + Super-jump - Diddy + E7D0-735A + + + Jump higher - Diddy + E1D0-735A + + + Jump lower - Diddy + EBD0-735A + + + Jump much lower - Diddy + ECD0-735A + + + Mega-jump - Dixie + EDD7-5AEA + + + Super-jump - Dixie + E7D7-5AEA + + + Jump higher - Dixie + E5D7-5AEA + + + Jump lower - Dixie + EBD7-5AEA + + + Jump much lower - Dixie + ECD7-5AEA + + + Mega-jump - Rambi with Diddy riding + EFDD-535A + + + Super-jump - Rambi with Diddy riding + E7DD-535A + + + Rambi jumps higher with Diddy riding + E5DD-535A + + + Rambi doesn't jump as high with Diddy riding + ECDD-535A + + + Start with more kong family coins + 626D-4EBD + + + Start with more Kremcoins + 626D-432D + Start with 3 lives D465-3D67 @@ -11520,104 +11759,6 @@ Start with 255 lives EE65-3D67 - - Get 2 lives per 100 bananas collected - D4A1-437C - D4A5-4AEC - - - Get 5 lives per 100 bananas collected - D9A1-437C - D9A5-4AEC - - - Get 255 lives per 100 bananas collected - EEA1-437C - - - When your last Kong is hit the other returns - EEC2-1A1D - EECC-CA4D - - - Easy level exit (press start and select) - DD6C-C7D4 - DD62-C4A4 - - - Start with more kong family coins - 626D-4EBD - - - Kong family coins don't get used up - C2B9-13B7 - C2B1-13F7 - - - Start a new game with more Kremcoins - 626D-432D - - - Kremcoins don't get used up - C2B9-1297 - C2B1-1A27 - - - Mega-jump for Diddy - EDD0-735A - - - Super-jump for Diddy - E7D0-735A - - - Diddy jumps higher - E1D0-735A - - - Diddy doesn't jump as high - EBD0-735A - - - Diddy jumps much lower - ECD0-735A - - - Mega-jump for Dixie - EDD7-5AEA - - - Super-jump for Dixie - E7D7-5AEA - - - Dixie jumps higher - E5D7-5AEA - - - Dixie doesn't jump as high - EBD7-5AEA - - - Dixie jumps much lower - ECD7-5AEA - - - Mega-jump for Rambi with Diddy riding - EFDD-535A - - - Super-jump for Rambi with Diddy riding - E7DD-535A - - - Rambi jumps higher with Diddy riding - E5DD-535A - - - Rambi doesn't jump as high with Diddy riding - ECDD-535A - Donkey Kong Country 2 - Diddy's Kong Quest (USA) (En,Fr) @@ -11627,11 +11768,120 @@ 6DAF-12EB 6D8D-C33E + + Invincibility + 4028-434D + Infinite lives C2A1-CE5B C2A5-C37B + + Get 2 lives per 100 bananas collected + D4A1-437C + D4A5-4AEC + + + Get 5 lives per 100 bananas collected + D9A1-437C + D9A5-4AEC + + + Get 255 lives per 100 bananas collected + EEA1-437C + + + When your last Kong is hit the other returns + EEC2-1A1D + EECC-CA4D + + + Easy level exit (press start and select) + DD6C-C7D4 + DD62-C4A4 + + + Kong family coins don't get used up + C2B9-13B7 + C2B1-13F7 + + + Kremcoins don't get used up + C2B9-1297 + C2B1-1A27 + + + Multi-jump + 4DAF-121F + 54AF-12CF + A0AF-123F + 1DAF-134F + + + Mega-jump - Diddy + EDD0-735A + + + Super-jump - Diddy + E7D0-735A + + + Jump higher - Diddy + E1D0-735A + + + Jump lower - Diddy + EBD0-735A + + + Jump much lower - Diddy + ECD0-735A + + + Mega-jump - Dixie + EDD7-5AEA + + + Super-jump - Dixie + E7D7-5AEA + + + Jump higher - Dixie + E5D7-5AEA + + + Jump lower - Dixie + EBD7-5AEA + + + Jump much lower - Dixie + ECD7-5AEA + + + Mega-jump - Rambi with Diddy riding + EFDD-535A + + + Super-jump - Rambi with Diddy riding + E7DD-535A + + + Rambi jumps higher with Diddy riding + E5DD-535A + + + Rambi doesn't jump as high with Diddy riding + ECDD-535A + + + Start with more kong family coins + 626D-4EBD + + + Start with more Kremcoins + 626D-432D + Start with 3 lives D465-3D67 @@ -11661,104 +11911,6 @@ Start with 255 lives EE65-3D67 - - Get 2 lives per 100 bananas collected - D4A1-437C - D4A5-4AEC - - - Get 5 lives per 100 bananas collected - D9A1-437C - D9A5-4AEC - - - Get 255 lives per 100 bananas collected - EEA1-437C - - - When your last Kong is hit the other returns - EEC2-1A1D - EECC-CA4D - - - Easy level exit (press start and select) - DD6C-C7D4 - DD62-C4A4 - - - Start with more kong family coins - 626D-4EBD - - - Kong family coins don't get used up - C2B9-13B7 - C2B1-13F7 - - - Start a new game with more Kremcoins - 626D-432D - - - Kremcoins don't get used up - C2B9-1297 - C2B1-1A27 - - - Mega-jump for Diddy - EDD0-735A - - - Super-jump for Diddy - E7D0-735A - - - Diddy jumps higher - E1D0-735A - - - Diddy doesn't jump as high - EBD0-735A - - - Diddy jumps much lower - ECD0-735A - - - Mega-jump for Dixie - EDD7-5AEA - - - Super-jump for Dixie - E7D7-5AEA - - - Dixie jumps higher - E5D7-5AEA - - - Dixie doesn't jump as high - EBD7-5AEA - - - Dixie jumps much lower - ECD7-5AEA - - - Mega-jump for Rambi with Diddy riding - EFDD-535A - - - Super-jump for Rambi with Diddy riding - E7DD-535A - - - Rambi jumps higher with Diddy riding - E5DD-535A - - - Rambi doesn't jump as high with Diddy riding - ECDD-535A - Donkey Kong Country 3 - Dixie Kong's Double Trouble! (USA) (En,Fr) @@ -11766,6 +11918,12 @@ Invincibility B768-C34D + + Invincibility (alt) + CBA0-CECF + DBA0-CE3F + DDA9-CA4F + Infinite lives CB6E-73CD @@ -11779,6 +11937,13 @@ 6D6C-7EFF 6D62-722F + + Multi-jump + 4D2B-3E1F + 5F2B-3ECF + BD2B-3E3F + 1D2C-3A4F + Doom (USA) @@ -17945,6 +18110,28 @@ BA2B-3FC4 + + Gekitotsu Dangan Jidousha Kessen - Battle Mobile (Japan) + + Invincibility + B989-04A9 + 898B-AFD5 + 8988-AFA5 + + + Infinite health + 898B-AFD5 + 8988-AFA5 + + + Infinite lives + C282-AF65 + + + Infinite Shields + 8BBF-A7A5 + + George Foreman's KO Boxing (USA) (Rev 1) @@ -19519,45 +19706,63 @@ Indiana Jones' Greatest Adventures (USA) - Infinite energy - CB24-4D64 + Invincibility + 62A7-1764 + 2D80-CDDF - Start with very little energy - DFC1-3707 + Infinite health + 7E012006 - Start with about 1/2 energy - D7C1-3707 + Infinite lives + C2C2-37A4 - Start with more energy (ignore energy meter) - DEC1-3701 - - - Hearts don't restore energy - C2B6-440F - - - Infinite grenades + Infinite Bombs C2B6-370F - Start with 5 grenades + Infinite Grenades + C2B6-370F + + + Infinite continues + 3CAB-CE82 + F0BC-173B + + + Hearts don't restore health + C2B6-440F + + + Can't collect Grenades + C2B9-37DF + + + Start with very little health + DFC1-3707 + + + Start with about 1/2 health + D7C1-3707 + + + Start with more health (ignore health meter) + DEC1-3701 + + + Start with 5 Grenades D9C7-CF0F - Start with 9 grenades + Start with 9 Grenades DBC7-CF0F - Start with 15 grenades + Start with 15 Grenades DEC7-CF0F - - Can't collect any more grenades - C2B9-37DF - Start with 2 lives DFCC-1E83 @@ -19574,11 +19779,6 @@ Start with no continues D1C8-13E3 - - Infinite continues - 3CAB-CE82 - F0BC-173B - Inindo - Way of the Ninja (USA) @@ -24015,13 +24215,6 @@ Have bottle 4 with infinite Good Bees 7EF35F08 - - Walk through walls - 07CB9AEA - 07CB9BEA - 07C1FAEA - 07C1FBEA - Legend of the Mystical Ninja, The (USA) @@ -30111,22 +30304,58 @@ Infinite Beat Whistles (alt) 7E0BA384 + + Infinite weapon energy + FEC2-E405 + Infinite Bolts 7E0BA6E7 7E0BA703 - Infinite weapon energy - FEC2-E405 + Infinite Danger Wrap + 7E0B91FF + + + Infinite Feeze Cracker + 7E0B85FF + + + Infinite Junk Shield + 7E0B89FF + + + Infinite Noise Crush + 7E0B8FFF + + + Infinite Scorch Wheel + 7E0B8BFF + + + Infinite Slash Claw + 7E0B8DFF Infinite slide 7E0C61FF - Moon-jump - 7E0C1AF4 + Infinite Thunder Bolt + 7E0B87FF + + + Infinite Rush Coil + 7E0B9BFF + + + Infinite Rush Search + 7E0B97FF + + + Infinite Wild Coil + 7E0B93FF Have exit @@ -30145,44 +30374,18 @@ 7E0B95FF - Infinite Feeze Cracker - 7E0B85FF + Multi-jump + 09FB-54A0 + 6DFB-5700 + CBFC-5D60 + D1FC-5DA0 + DDFC-5FD0 + 69FC-5F00 + D4FC-5F60 - Infinite Junk Shield - 7E0B89FF - - - Infinite Scorch Wheel - 7E0B8BFF - - - Infinite Slash Claw - 7E0B8DFF - - - Infinite Thunder Bolt - 7E0B87FF - - - Infinite Noise Crush - 7E0B8FFF - - - Infinite Danger Wrap - 7E0B91FF - - - Infinite Wild Coil - 7E0B93FF - - - Infinite Rush Coil - 7E0B9BFF - - - Infinite Rush Search - 7E0B97FF + Moon-jump + 7E0C1AF4 @@ -30204,6 +30407,10 @@ Infinite lives 7E1F8009 + + Infinite weapons once obtained + DDB3-4FA9 + One hit kills (most enemies) 6DB5-CD97 @@ -30237,8 +30444,10 @@ 7E1F86FF - Infinite weapon usage once obtained - DDB3-4FA9 + Multi-jump + 4065-17A9 + EA66-1409 + B966-1469 Bogus jump (may go back to normal jumps) @@ -30248,6 +30457,14 @@ Super-jump (may go back to normal jumps) D58A-1FBC + + Start with less health + D6BE-47AF + + + Start with more health + 4DBE-47AF + Start with 10 lives DBBE-446F @@ -30264,14 +30481,6 @@ Start with 1 life DDBE-446F - - Start with less health - D6BE-47AF - - - Start with more health - 4DBE-47AF - Mega Man X (USA) @@ -30292,28 +30501,12 @@ 7E1F8009 - Start with 10 lives - DBBE-446F + Start with all weapons and all enemies defeated (except Sigma) + 23BD-3F07 - Start with 7 lives - D1BE-446F - - - Start with 5 lives - D0BE-446F - - - Start with 1 life - DDBE-446F - - - Start with less health - D6BE-47AF - - - Start with more health - 4DBE-47AF + Infinite weapons once obtained + C9B3-4769 One hit kills (most enemies) @@ -30340,12 +30533,10 @@ 7E1F86FF - Start with all weapons and all enemies defeated (except Sigma) - 23BD-3F07 - - - Infinite weapons once you have them - C9B3-4769 + Multi-jump + 4065-1F09 + E465-17A9 + B966-1DD9 Bogus jump (may go back to normal jumps) @@ -30363,6 +30554,30 @@ Weapon charges to 1st power level faster DDB1-4F61 + + Start with less health + D6BE-47AF + + + Start with more health + 4DBE-47AF + + + Start with 10 lives + DBBE-446F + + + Start with 7 lives + D1BE-446F + + + Start with 5 lives + D0BE-446F + + + Start with 1 life + DDBE-446F + Mega Man X2 (USA) @@ -30390,10 +30605,6 @@ Infinite lives 7E1FB309 - - One hit kills - 6D2D-AF14 - Have Shouryuken (F, D, DF + Fire) (must have max health and health containers) 7E1FB180 @@ -30462,17 +30673,23 @@ Have sub-tank 4 full 7E1FB9FF + + One hit kills + 6D2D-AF14 + + + Multi-jump + 40BE-DD44 + 26BE-D4C4 + 8BBE-D434 + Mega Man X3 (USA) - Invincibility (enable) + Invincibility 7E0A0808 - - Invincibility (disable) - 7E0A0800 - Infinite health C2AD-6FF7 @@ -30525,6 +30742,12 @@ Skip bosses at start of game and proceed to stage select B933-DF6C + + Multi-jump + 4064-042F + D367-0F9F + C567-0FBF + Super-jump D586-6F26 @@ -31822,6 +32045,23 @@ 0823-3944 + + Mortal Kombat - Shinken Kourin Densetsu (Japan) + + Infinite health + CF31-4F64 + 6231-44D4 + EB31-4404 + D031-4464 + + + One hit kill - P1 + DD3C-44D4 + 623C-4464 + E83C-44A4 + D03C-47D4 + + Mortal Kombat 3 (USA) @@ -32018,10 +32258,28 @@ Invincibility - P1 C2B1-14F7 + + Invincibility - P1 (alt) + 849C6880 + 849C6909 + 849C7B00 + 85AA5260 + 85AB1C60 + 85AC2A60 + Invincibility - P2/CPU C2B5-14F7 + + Invincibility - P2/CPU (alt) + 849C6880 + 849C6906 + 849C7600 + 85AA5260 + 85AB1C60 + 85AC2A60 + Infinite health CB89-4FAA @@ -33896,10 +34154,22 @@ (NG) Invincibility 6DB5-3797 + + (NG) Invincibility after first hit + 7E009301 + (NG) Infinite health C9C3-3F2D + + (NG) Infinite Ninja Power + 7E006463 + + + (NG) Infinite lives + C9CF-CFBF + (NG) Infinite time DD81-1F97 @@ -33933,17 +34203,30 @@ D6C5-4DF4 - (NG) Infinite lives - C9CF-CFBF + (NG) Auto kill enemies that are close by + 849F9280 + 849F9F80 (NGII) Invincibility 1DB4-CD4D + + (NGII) Invincibility (alt) + 7E006825 + (NGII) Infinite health C9BB-441F + + (NGII) Infinite Ninja Power + 7E00AE63 + + + (NGII) Infinite lives + C961-3D37 + (NGII) Infinite time C96F-1FC7 @@ -33957,21 +34240,21 @@ D6B3-144F - (NGII) Start every life with two shadow ninjas + (NGII) Start every life with two Shadow Ninjas DEB9-14C4 - (NGII) Throwing stars doesn't use energy (ignore counter) + (NGII) Always have Fireball + 7E007D02 + + + (NGII) Throwing stars don't use Ninja Power (ignore counter) DFC4-1F47 (NGII) Start with 1 life DDB3-C4CF - - (NGII) Infinite lives - C961-3D37 - (NGIII) Invincibility DDAB-3FE7 @@ -33980,16 +34263,28 @@ (NGIII) Infinite health C92A-CFED + + (NGIII) Infinite lives + CB69-CD57 + + + (NGIII) Infinite lives (alt) + 7E00C403 + (NGIII) Infinite time C927-17EF - (NGIII) Almost infinite ninja power + (NGIII) Infinite Ninja Power + 7E00CD63 + + + (NGIII) Almost infinite Ninja Power C9A7-C757 - (NGIII) Start with 99 weapon points (ignore counter) + (NGIII) Start with 99 Ninja Power (ignore counter) 176C-CDE7 @@ -34004,10 +34299,6 @@ (NGIII) Start with 9 lives D66B-CF87 - - (NGIII) Infinite lives - CB69-CD57 - (NGIII) Start with very little health DF68-CD77 @@ -34907,7 +35198,7 @@ 7E050603 - Have All 3 Cartridges + Have all 3 Cartridges 7E050707 @@ -35270,7 +35561,7 @@ 7E174F25 - Chezni starts with 255 max. HP + Chezni starts with 255 max HP EEEB-6D1F @@ -35422,7 +35713,7 @@ FBEA-6F4D - Chezni starts with Rft shield + Chezni starts with Rft shield 59EA-6F4D @@ -36338,6 +36629,11 @@ Power Piggs of the Dark Age (USA) + + Invincibility + 1DB7-C40F + 6DB1-CF0D + Partial invincibility 7E06A43F @@ -36354,6 +36650,11 @@ Infinite Doughnuts 7E150509 + + Multi-jump + 4066-CFA7 + D5EA-44DC + Start on level Beautiful Downtown Pigg 7E187400 @@ -45179,15 +45480,24 @@ Super Castlevania IV (USA) Invincibility + 2D2B-6764 + 2DCD-D764 + + + Invincibility (blinking) 608E-D7FB C92C-6DD4 - Invincibility (blinking) + Invincibility (blinking) (alt) 7E00BC0A Infinite health + C22F-07D7 + + + Infinite health (alt) 7E13F410 @@ -45206,6 +45516,10 @@ Infinite time (alt) 7E13F099 + + Infinite lives + C96C-DFD6 + Infinite shots for most weapons A689-0FD7 @@ -45346,6 +45660,13 @@ 7E127B89 + + Super Donkey Kong 2 - Dixie & Diddy (Japan) + + Invincibility + 4022-431D + + Super Double Dragon (USA) @@ -46023,7 +46344,7 @@ (SMB) Multi-jump - 23C2BB02 + 2D8E-D7D2 (SMB) Run without holding the dash button @@ -46167,22 +46488,12 @@ (SMW) Always have Yoshi 7E0DC101 - - (SMW) Yoshi is invincible - 89E4-AFD9 - 89C6-D4DB - (SMW) Infinite flying time for Yoshi C2EC-0700 - (SMW) Collecting a Yoshi Coin gives you Yoshi's Wings - B9E5-A4AD - F8E5-A7DD - - - (SMW) Jump in mid air and float down (disable in water and to get on Yoshi) + (SMW) Multi-jump and float down (disable in water and to get on Yoshi) 7E147101 @@ -46284,6 +46595,24 @@ 7EFA0D63 7EFA0C63 + + Infinite Coins + 7FF8AFE7 + 7FF8B003 + + + Infinite Frog Coins + 7FF8B3E7 + 7FF8B403 + + + 255 EXP per battle (disable before going to the world map or using the menu button) + 7EFA02FF + + + Always Mario's turn + 7E070200 + Infinite tries in action sequences A98E-7707 @@ -46654,7 +46983,7 @@ D2E5-A7AD - Jump in mid air and float down (disable in water and to get on Yoshi) + Multi-jump and float down (disable in water and to get on Yoshi) 7E147101 @@ -47198,6 +47527,11 @@ Have all Boots and regular Bombs 7E09A5FF + + Automatically collect secret and/or special items in current room (Missiles, Energy Tanks, ect) + 402B-3DB7 + 402E-3FF7 + Maximum 999 Missiles 7E09C8E7 @@ -47725,13 +48059,52 @@ Super R-Type (USA) - Invincibility + Start with Invincibility (blinking) + 82C2-DF61 + + + Start with Invincibility (blinking) (alt) 7E15BD00 Infinite lives C2C7-6D0F + + Spiral motion gun takes less time to power up + 6D80-6DD1 + DD80-6D01 + + + Spiral motion gun takes much less time to power up + DD80-6DA1 + + + Spiral motion gun can't get over-charged + 6D84-6F01 + + + All FORCE satellites have 1 unit of power (but can't exceed 1 unit) + DD68-6DDB + CB6C-67AB + 7D68-6D6B + + + All FORCE satellites have 2 units of power (but can't exceed 2 units) + DF68-6DDB + CB6C-67AB + 7D68-6D6B + + + All FORCE satellites have 3 units of power + D468-6DDB + CB6C-67AB + 7D68-6D6B + + + Once FORCE has been obtained, keep it forever + C267-A4D9 + Start with 1 life instead of 3 DF66-0F00 @@ -47804,41 +48177,6 @@ CB6C-A7D9 DD6C-A769 - - Spiral motion gun takes less time to power up - 6D80-6DD1 - DD80-6D01 - - - Spiral motion gun takes much less time to power up - DD80-6DA1 - - - Spiral motion gun can't get over-charged - 6D84-6F01 - - - All FORCE satellites have 1 unit of power (but can't exceed 1 unit) - DD68-6DDB - CB6C-67AB - 7D68-6D6B - - - All FORCE satellites have 2 units of power (but can't exceed 2 units) - DF68-6DDB - CB6C-67AB - 7D68-6D6B - - - All FORCE satellites have 3 units of power - D468-6DDB - CB6C-67AB - 7D68-6D6B - - - Once FORCE has been obtained, keep it forever - C267-A4D9 - Super Scope 6 (USA) @@ -50166,6 +50504,18 @@ TKO Super Championship Boxing (USA) + + Infinite punch meters - both players + 4088-AF00 + + + Infinite punch meter - P1 + 40BF-04DD + + + Infinite punch meter - P2 + 40B0-076D + 9 minutes per round DB60-A7D4 @@ -50188,18 +50538,6 @@ D7B9-04DD D765-A764 - - Infinite punch meters - both players - 4088-AF00 - - - Infinite punch meter - P1 - 40BF-04DD - - - Infinite punch meter - P2 - 40B0-076D - Taz-Mania (USA) (Rev 1) @@ -51600,6 +51938,11 @@ Time Trax (USA) + + Invincibility + 84E948FF + 8187FFFF + Infinite health C2E8-1F60 @@ -51741,6 +52084,22 @@ Tiny Toon Adventures - Buster Busts Loose! (USA) + + Infinite health (when hit, a fake empty heart appears) + 3CE9-448A + + + Infinite dash meter + C927-47AD + + + Infinite lives (except football level) + DD34-37D7 + + + Infinite lives (football level) + DDC0-3F07 + Start with 1 life DF6F-14DF @@ -51789,14 +52148,6 @@ Continue with 99 lives BBC0-CFAF - - Infinite lives - except football level - DD34-37D7 - - - Infinite lives - football level - DDC0-3F07 - Start with no continues on normal level - handicap DD6D-47D0 @@ -51837,18 +52188,10 @@ Start with 5 hearts on Children or Normal difficulty levels, 3 on Challenge D9CD-4FAF - - Infinite health (when hit, a fake empty heart appears) - 3CE9-448A - Five heart maximum on challenge level D184-C4A1 - - Dash meter doesn't go down - C927-47AD - 1-up gives 2-up (not on mystery weight challenge level, or by collecting stars) D46B-CFAF @@ -54692,10 +55035,6 @@ WeaponLord (USA) - - Play as Zarak in story mode - D13C-561B - Infinite health - P1 5C1A-7B3B @@ -54704,6 +55043,14 @@ Infinite health - both players C21A-7BCB + + Infinite time + C204-56C5 + + + Play as Zarak in story mode + D13C-561B + Wheel of Fortune (USA) @@ -54881,6 +55228,18 @@ Wizard of Oz, The (USA) + + Invincibility + 2D8D-07A5 + + + Infinite health + 823C-AD66 + + + Infinite lives + 223E-DDAF + Infinite health - Dorthy 7E01E500 diff --git a/bsnes/libco/libco.c b/bsnes/libco/libco.c index dd020fe3..55676263 100755 --- a/bsnes/libco/libco.c +++ b/bsnes/libco/libco.c @@ -8,8 +8,8 @@ #include "x86.c" #elif defined(__GNUC__) && defined(__amd64__) #include "amd64.c" -#elif defined(__GNUC__) && defined(__powerpc__) && defined(__ELF__) - #include "ppc-elf.c" +#elif defined(__GNUC__) && defined(_ARCH_PPC) + #include "ppc.c" #elif defined(__GNUC__) #include "sjlj.c" #elif defined(_MSC_VER) && defined(_M_IX86) diff --git a/bsnes/libco/libco.h b/bsnes/libco/libco.h index b1b49a29..deb954fb 100755 --- a/bsnes/libco/libco.h +++ b/bsnes/libco/libco.h @@ -1,6 +1,6 @@ /* libco - version: 0.15 (2009-10-12) + version: 0.16 (2010-12-24) license: public domain */ diff --git a/bsnes/libco/ppc-elf.c b/bsnes/libco/ppc-elf.c deleted file mode 100755 index 5740f77f..00000000 --- a/bsnes/libco/ppc-elf.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * libco.ppc-elf - * author: Kernigh - * license: public domain - * - * PowerPC 32-bit ELF implementation of libco (for compile with GCC), - * ported from PowerPC Mac OS X implementation (ppc.s) by Vas Crabb. - * This ELF version works for OpenBSD, and might also work for FreeBSD, - * NetBSD and Linux. - * - * Note 1: This implementation does not handle the AltiVec/VMX - * registers, because the ELF ABI does not mention them, - * and my OpenBSD system is not using them. - * - * Note 2: If you want position-independent code, then you must - * define __PIC__. gcc -fpic or -fPIC defines __PIC__, but - * gcc -fpie or -fPIE might not. If you want to use -fpie - * or -fPIE, then you might need a manual definition: - * gcc -fpie -D__PIC__=1 - * gcc -fPIE -D__PIC__=2 - * - * The ELF ABI is "System V Application Binary Interface, PowerPC - * Processor Supplement", which you can get from - * - * (PDF file, hosted by Linux Foundation). - * - * ELF and Mac OS X use similar conventions to allocate the registers, - * and to pass arguments and return values through registers. The main - * differences are that ELF has a slightly different stack format, that - * symbols are different (and without an extra underscore at the start), - * and that the assembly syntax is different. - * - * A function may destroy the values of volatile registers, but must - * preserve the values of nonvolatile registers. So the co_switch() - * function only saves the nonvolatile registers. - * - * [nonvolatile registers in ELF] - * %r1, %r14..%r31 - * %f14..%f31 - * %cr2..%cr4 in cr - * - * [volatile registers in ELF] - * %r0, %r3..%r10 - * %f0..%f13 - * %cr0, %cr1, %cr5..%cr7 in cr - * ctr, lr, xer - * - * lr (link register) is volatile, but it contains the return address, - * so co_switch must save lr. - * - * %r13 is the small data pointer. This is constant across threads, so - * co_switch() does not touch %r13. - * - * %r2 is a reserved register, so co_switch() does not touch %r2. Some - * systems might borrow an idea from the PowerPC Embedded ABI, and might - * use %r2 as a small read-only data pointer, which is constant across - * threads. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void * cothread_t; - -/* - * co_active_context is either in a global offset table (if we are - * compiling -fPIC or -fPIE) or has an absolute position. - */ -static void *co_main_stack_pointer; -static cothread_t co_active_context = &co_main_stack_pointer; - -extern cothread_t co_active() { - return co_active_context; -} - -/* - * Embedded assembly. - * - * We are not using the percent-sign substitution feature, - * so we must write "%r1", not "%%r1". - * - * We always write 'bl malloc@plt', not 'bl malloc'. The '@plt' - * is necessary in position-indepent code and seems to have no - * significant effect in fixed-position code. - * - * We never use the 'lmw' or 'stmw' instructions. The ELF ABI - * mentions that these instructions "are usually slower than - * a sequence of other instructions that have the same effect." - * We instead use sequences of 'lwz' or 'stz' instructions. - */ -__asm__("\n" -"### embedded assembly \n" -".section \".text\" \n" -" .balign 4 \n" -" \n" -/* - * void co_switch(co_thread to %r3) - * - * Allocate our stack frame of 240 bytes: - * Old New Value - * 4(%r1) 244(%r1) return address, used by us - * 0(%r1) 240(%r1) frame pointer - * 232(%r1) %f31 - * 224(%r1) %f30 - * ... - * 96(%r1) %f14 - * 92(%r1) %r31 - * 88(%r1) %r30 - * ... - * 24(%r1) %r14 - * 20(%r1) condition register - * 8(%r1) padding of 12 bytes - * 4(%r1) return address, never used - * 0(%r1) frame pointer - * - * Save our registers in our stack frame. - * Save our stack pointer in 0(%r4). - * Switch to the stack of the other thread. - * Restore registers and return. - */ -" .globl co_switch \n" -" .type co_switch, @function \n" -"co_switch: \n" -" mflr %r0 # %r0 = return address \n" -" mfcr %r9 # %r9 = condition register \n" -" stwu %r1, -240(%r1) # allocate stack frame \n" -" \n" -" stw %r0, 244(%r1) # save return address \n" -" stfd %f31, 232(%r1) # save floating-point regs \n" -" stfd %f30, 224(%r1) \n" -" stfd %f29, 216(%r1) \n" -" stfd %f28, 208(%r1) \n" -" stfd %f27, 200(%r1) \n" -" stfd %f26, 192(%r1) \n" -" stfd %f25, 184(%r1) \n" -" stfd %f24, 176(%r1) \n" -" stfd %f23, 168(%r1) \n" -" stfd %f22, 160(%r1) \n" -" stfd %f21, 152(%r1) \n" -" stfd %f20, 144(%r1) \n" -" stfd %f19, 136(%r1) \n" -" stfd %f18, 128(%r1) \n" -" stfd %f17, 120(%r1) \n" -" stfd %f16, 112(%r1) \n" -" stfd %f16, 104(%r1) \n" -" stfd %f14, 96(%r1) \n" -" stw %r31, 92(%r1) # save general-purpose regs \n" -" stw %r30, 88(%r1) \n" -" stw %r29, 84(%r1) \n" -" stw %r28, 80(%r1) \n" -" stw %r27, 76(%r1) \n" -" stw %r26, 72(%r1) \n" -" stw %r25, 68(%r1) \n" -" stw %r24, 64(%r1) \n" -" stw %r23, 60(%r1) \n" -" stw %r22, 56(%r1) \n" -" stw %r21, 52(%r1) \n" -" stw %r20, 48(%r1) \n" -" stw %r19, 44(%r1) \n" -" stw %r18, 40(%r1) \n" -" stw %r17, 36(%r1) \n" -" stw %r16, 32(%r1) \n" -" stw %r15, 28(%r1) \n" -" stw %r14, 24(%r1) \n" -" stw %r9, 20(%r1) # save condition reg \n" -" \n" -" # save current context, set new context \n" -" # %r4 = co_active_context \n" -" # co_active_context = %r3 \n" -#if __PIC__ == 2 -" # position-independent code, large model (-fPIC) \n" -" bl _GLOBAL_OFFSET_TABLE_@local-4 \n" -" mflr %r8 # %r8 = address of got \n" -" addis %r7, %r8, co_active_context@got@ha \n" -" lwz %r6, co_active_context@got@l(%r7) \n" -" lwz %r4, 0(%r6) \n" -" stw %r3, 0(%r6) \n" -#elif __PIC__ == 1 -" # position-independent code, small model (-fpic) \n" -" bl _GLOBAL_OFFSET_TABLE_@local-4 \n" -" mflr %r8 # %r8 = address of got \n" -" lwz %r7, co_active_context@got(%r8) \n" -" lwz %r4, 0(%r7) \n" -" stw %r3, 0(%r7) \n" -#else -" # fixed-position code \n" -" lis %r8, co_active_context@ha \n" -" lwz %r4, co_active_context@l(%r8) \n" -" stw %r3, co_active_context@l(%r8) \n" -#endif -" \n" -" # save current stack pointer \n" -" stw %r1, 0(%r4) \n" -" # get new stack pointer \n" -" lwz %r1, 0(%r3) \n" -" \n" -" lwz %r0, 244(%r1) # get return address \n" -" lfd %f31, 232(%r1) # restore floating-point regs \n" -" lfd %f30, 224(%r1) \n" -" lfd %f29, 216(%r1) \n" -" lfd %f28, 208(%r1) \n" -" lfd %f27, 200(%r1) \n" -" lfd %f26, 192(%r1) \n" -" lfd %f25, 184(%r1) \n" -" lfd %f24, 176(%r1) \n" -" lfd %f23, 168(%r1) \n" -" lfd %f22, 160(%r1) \n" -" lfd %f21, 152(%r1) \n" -" lfd %f20, 144(%r1) \n" -" lfd %f19, 136(%r1) \n" -" lfd %f18, 128(%r1) \n" -" lfd %f17, 120(%r1) \n" -" lfd %f16, 112(%r1) \n" -" lfd %f16, 104(%r1) \n" -" lfd %f14, 96(%r1) \n" -" lwz %r31, 92(%r1) # restore general-purpose regs \n" -" lwz %r30, 88(%r1) \n" -" lwz %r29, 84(%r1) \n" -" lwz %r28, 80(%r1) \n" -" lwz %r27, 76(%r1) \n" -" lwz %r26, 72(%r1) \n" -" lwz %r25, 68(%r1) \n" -" lwz %r24, 64(%r1) \n" -" lwz %r23, 60(%r1) \n" -" lwz %r22, 56(%r1) \n" -" lwz %r21, 52(%r1) \n" -" lwz %r20, 48(%r1) \n" -" lwz %r19, 44(%r1) \n" -" lwz %r18, 40(%r1) \n" -" lwz %r17, 36(%r1) \n" -" lwz %r16, 32(%r1) \n" -" lwz %r15, 28(%r1) \n" -" lwz %r14, 24(%r1) \n" -" lwz %r9, 20(%r1) # get condition reg \n" -" \n" -" addi %r1, %r1, 240 # free stack frame \n" -" mtlr %r0 # restore return address \n" -" mtcr %r9 # restore condition register \n" -" blr # return \n" -" .size co_switch, . - co_switch \n" -" \n" -/* - * cothread_t %r3 co_create(unsigned int stack_size %r3, - * void (*coentry %r4)()) - * - * Allocate a new stack, such that when you co_switch to that - * stack, then co_switch returns to coentry. - */ -" .globl co_create \n" -" .type co_create, @function \n" -"co_create: \n" -" mflr %r0 # %r0 = return address \n" -" stwu %r1, -16(%r1) # allocate my stack frame \n" -" stw %r0, 20(%r1) # save return address \n" -" stw %r31, 12(%r1) # save %r31 \n" -" stw %r30, 8(%r1) # save %r30 \n" -" \n" -" mr %r30, %r3 # %r30 = stack_size \n" -" mr %r31, %r4 # %r31 = coentry \n" -" \n" -" # Call malloc(stack_size %r3) to allocate stack; \n" -" # malloc() probably uses good alignment. \n" -" # \n" -" bl malloc@plt # returns %r3 = low end \n" -" cmpwi %r3, 0 # if returned NULL, \n" -" beq- 1f # then abort \n" -" \n" -" # we return %r3 = low end of stack \n" -" add %r4, %r3, %r30 # %r4 = high end of stack \n" -" \n" -" # uncomment if malloc() uses wrong alignment \n" -" #rlwinm %r4,%r4,0,0,27 # force 16-byte alignment \n" -" \n" - /* - * Allocate two stack frames: - * 16 bytes for stack frame with return address - * 240 bytes for co_switch stack frame - * - * Old New Value - * -8(%r4) 248(%r5) padding of 8 bytes - * -12(%r4) 244(%r5) return address = coentry - * -16(%r4) 240(%r5) frame pointer = NULL - * 232(%r5) %f31 = 0 - * ... - * 20(%r5) condition register = 0 - * 0(%r5) frame pointer - */ -" li %r9, (240-20)/4+1 \n" -" addi %r5, %r4, -16 # allocate first stack frame \n" -" li %r0, 0 \n" -" stwu %r5, -240(%r5) # allocate second stack frame \n" -" li %r8, 20 \n" -" mtctr %r9 # loop %r9 times \n" -"2: # loop to store zero to 20(%r5) through 240(%r5) \n" -" stwx %r0, %r5, %r8 \n" -" addi %r8, %r8, 4 # index += 4 \n" -" bdnz+ 2b # ctr -= 1, branch if nonzero \n" -" \n" -" stw %r31, 244(%r5) # return address = coentry \n" -" stw %r5, 0(%r3) # save stack pointer \n" -" \n" -" lwz %r0, 20(%r1) # get return address \n" -" lwz %r31, 12(%r1) # restore %r31 \n" -" lwz %r30, 8(%r1) # restore %r30 \n" -" mtlr %r0 # restore return address \n" -" addi %r1, %r1, 16 # free stack frame \n" -" blr # return \n" -" \n" -"1: b abort@plt # branch 1f to abort \n" -" .size co_create, . - co_create \n" -" \n" -/* - * void co_delete(cothread_t) => void free(void *) - */ -" .globl co_delete \n" -" .type co_delete, @function \n" -"co_delete: \n" -" b free@plt \n" -" \n" -); - -#ifdef __cplusplus -} -#endif diff --git a/bsnes/libco/ppc.c b/bsnes/libco/ppc.c new file mode 100755 index 00000000..a6028fdb --- /dev/null +++ b/bsnes/libco/ppc.c @@ -0,0 +1,407 @@ +/* + libco.ppc (2010-10-17) + author: blargg + license: public domain +*/ + +/* PowerPC 32/64 using embedded or external asm, with optional +floating-point and AltiVec save/restore */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#define LIBCO_MPROTECT (__unix__ && !LIBCO_PPC_ASM) + +#if LIBCO_MPROTECT + #include + #include +#endif + +/* State format (offsets in 32-bit words) + ++0 Pointer to swap code + Rest of function descriptor for entry function ++8 PC ++10 SP + Special regs + GPRs + FPRs + VRs + stack +*/ + +enum { state_size = 1024 }; +enum { above_stack = 2048 }; +enum { stack_align = 256 }; + +static thread_local cothread_t co_active_handle = 0; + +/**** Determine environment ****/ + +#define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__) + +/* Whether function calls are indirect through a descriptor, +or are directly to function */ +#ifndef LIBCO_PPCDESC + #if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || LIBCO_PPC64) + #define LIBCO_PPCDESC 1 + #endif +#endif + +#ifdef LIBCO_PPC_ASM + + #ifdef __cplusplus + extern "C" + #endif + + /* Swap code is in ppc.S */ + void co_swap_asm( cothread_t, cothread_t ); + #define CO_SWAP_ASM( x, y ) co_swap_asm( x, y ) + +#else + +/* Swap code is here in array. Please leave dieassembly comments, +as they make it easy to see what it does, and reorder instructions +if one wants to see whether that improves performance. */ +static const uint32_t libco_ppc_code [] = { +#if LIBCO_PPC64 + 0x7d000026, /* mfcr r8 */ + 0xf8240028, /* std r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0xf9c40048, /* std r14,72(r4) */ + 0xf9e40050, /* std r15,80(r4) */ + 0xfa040058, /* std r16,88(r4) */ + 0xfa240060, /* std r17,96(r4) */ + 0xfa440068, /* std r18,104(r4) */ + 0xfa640070, /* std r19,112(r4) */ + 0xfa840078, /* std r20,120(r4) */ + 0xfaa40080, /* std r21,128(r4) */ + 0xfac40088, /* std r22,136(r4) */ + 0xfae40090, /* std r23,144(r4) */ + 0xfb040098, /* std r24,152(r4) */ + 0xfb2400a0, /* std r25,160(r4) */ + 0xfb4400a8, /* std r26,168(r4) */ + 0xfb6400b0, /* std r27,176(r4) */ + 0xfb8400b8, /* std r28,184(r4) */ + 0xfba400c0, /* std r29,192(r4) */ + 0xfbc400c8, /* std r30,200(r4) */ + 0xfbe400d0, /* std r31,208(r4) */ + 0xf9240020, /* std r9,32(r4) */ + 0xe8e30020, /* ld r7,32(r3) */ + 0xe8230028, /* ld r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0xe9c30048, /* ld r14,72(r3) */ + 0xe9e30050, /* ld r15,80(r3) */ + 0xea030058, /* ld r16,88(r3) */ + 0xea230060, /* ld r17,96(r3) */ + 0xea430068, /* ld r18,104(r3) */ + 0xea630070, /* ld r19,112(r3) */ + 0xea830078, /* ld r20,120(r3) */ + 0xeaa30080, /* ld r21,128(r3) */ + 0xeac30088, /* ld r22,136(r3) */ + 0xeae30090, /* ld r23,144(r3) */ + 0xeb030098, /* ld r24,152(r3) */ + 0xeb2300a0, /* ld r25,160(r3) */ + 0xeb4300a8, /* ld r26,168(r3) */ + 0xeb6300b0, /* ld r27,176(r3) */ + 0xeb8300b8, /* ld r28,184(r3) */ + 0xeba300c0, /* ld r29,192(r3) */ + 0xebc300c8, /* ld r30,200(r3) */ + 0xebe300d0, /* ld r31,208(r3) */ + 0x7ccff120, /* mtcr r6 */ +#else + 0x7d000026, /* mfcr r8 */ + 0x90240028, /* stw r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0x91a4003c, /* stw r13,60(r4) */ + 0x91c40040, /* stw r14,64(r4) */ + 0x91e40044, /* stw r15,68(r4) */ + 0x92040048, /* stw r16,72(r4) */ + 0x9224004c, /* stw r17,76(r4) */ + 0x92440050, /* stw r18,80(r4) */ + 0x92640054, /* stw r19,84(r4) */ + 0x92840058, /* stw r20,88(r4) */ + 0x92a4005c, /* stw r21,92(r4) */ + 0x92c40060, /* stw r22,96(r4) */ + 0x92e40064, /* stw r23,100(r4) */ + 0x93040068, /* stw r24,104(r4) */ + 0x9324006c, /* stw r25,108(r4) */ + 0x93440070, /* stw r26,112(r4) */ + 0x93640074, /* stw r27,116(r4) */ + 0x93840078, /* stw r28,120(r4) */ + 0x93a4007c, /* stw r29,124(r4) */ + 0x93c40080, /* stw r30,128(r4) */ + 0x93e40084, /* stw r31,132(r4) */ + 0x91240020, /* stw r9,32(r4) */ + 0x80e30020, /* lwz r7,32(r3) */ + 0x80230028, /* lwz r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0x81a3003c, /* lwz r13,60(r3) */ + 0x81c30040, /* lwz r14,64(r3) */ + 0x81e30044, /* lwz r15,68(r3) */ + 0x82030048, /* lwz r16,72(r3) */ + 0x8223004c, /* lwz r17,76(r3) */ + 0x82430050, /* lwz r18,80(r3) */ + 0x82630054, /* lwz r19,84(r3) */ + 0x82830058, /* lwz r20,88(r3) */ + 0x82a3005c, /* lwz r21,92(r3) */ + 0x82c30060, /* lwz r22,96(r3) */ + 0x82e30064, /* lwz r23,100(r3) */ + 0x83030068, /* lwz r24,104(r3) */ + 0x8323006c, /* lwz r25,108(r3) */ + 0x83430070, /* lwz r26,112(r3) */ + 0x83630074, /* lwz r27,116(r3) */ + 0x83830078, /* lwz r28,120(r3) */ + 0x83a3007c, /* lwz r29,124(r3) */ + 0x83c30080, /* lwz r30,128(r3) */ + 0x83e30084, /* lwz r31,132(r3) */ + 0x7ccff120, /* mtcr r6 */ +#endif + +#ifndef LIBCO_PPC_NOFP + 0xd9c400e0, /* stfd f14,224(r4) */ + 0xd9e400e8, /* stfd f15,232(r4) */ + 0xda0400f0, /* stfd f16,240(r4) */ + 0xda2400f8, /* stfd f17,248(r4) */ + 0xda440100, /* stfd f18,256(r4) */ + 0xda640108, /* stfd f19,264(r4) */ + 0xda840110, /* stfd f20,272(r4) */ + 0xdaa40118, /* stfd f21,280(r4) */ + 0xdac40120, /* stfd f22,288(r4) */ + 0xdae40128, /* stfd f23,296(r4) */ + 0xdb040130, /* stfd f24,304(r4) */ + 0xdb240138, /* stfd f25,312(r4) */ + 0xdb440140, /* stfd f26,320(r4) */ + 0xdb640148, /* stfd f27,328(r4) */ + 0xdb840150, /* stfd f28,336(r4) */ + 0xdba40158, /* stfd f29,344(r4) */ + 0xdbc40160, /* stfd f30,352(r4) */ + 0xdbe40168, /* stfd f31,360(r4) */ + 0xc9c300e0, /* lfd f14,224(r3) */ + 0xc9e300e8, /* lfd f15,232(r3) */ + 0xca0300f0, /* lfd f16,240(r3) */ + 0xca2300f8, /* lfd f17,248(r3) */ + 0xca430100, /* lfd f18,256(r3) */ + 0xca630108, /* lfd f19,264(r3) */ + 0xca830110, /* lfd f20,272(r3) */ + 0xcaa30118, /* lfd f21,280(r3) */ + 0xcac30120, /* lfd f22,288(r3) */ + 0xcae30128, /* lfd f23,296(r3) */ + 0xcb030130, /* lfd f24,304(r3) */ + 0xcb230138, /* lfd f25,312(r3) */ + 0xcb430140, /* lfd f26,320(r3) */ + 0xcb630148, /* lfd f27,328(r3) */ + 0xcb830150, /* lfd f28,336(r3) */ + 0xcba30158, /* lfd f29,344(r3) */ + 0xcbc30160, /* lfd f30,352(r3) */ + 0xcbe30168, /* lfd f31,360(r3) */ +#endif + +#ifdef __ALTIVEC__ + 0x7ca042a6, /* mfvrsave r5 */ + 0x39040180, /* addi r8,r4,384 */ + 0x39240190, /* addi r9,r4,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x90a40034, /* stw r5,52(r4) */ + 0x4182005c, /* beq- 2 */ + 0x7e8041ce, /* stvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea049ce, /* stvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec041ce, /* stvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee049ce, /* stvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0041ce, /* stvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2049ce, /* stvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4041ce, /* stvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6049ce, /* stvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8041ce, /* stvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa049ce, /* stvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc041ce, /* stvx v30,r0,r8 */ + 0x7fe049ce, /* stvx v31,r0,r9 */ + 0x80a30034,/*2:lwz r5,52(r3) */ + 0x39030180, /* addi r8,r3,384 */ + 0x39230190, /* addi r9,r3,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x7ca043a6, /* mtvrsave r5 */ + 0x4d820420, /* beqctr */ + 0x7e8040ce, /* lvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea048ce, /* lvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec040ce, /* lvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee048ce, /* lvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0040ce, /* lvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2048ce, /* lvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4040ce, /* lvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6048ce, /* lvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8040ce, /* lvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa048ce, /* lvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc040ce, /* lvx v30,r0,r8 */ + 0x7fe048ce, /* lvx v31,r0,r9 */ +#endif + + 0x4e800420, /* bctr */ +}; + + #if LIBCO_PPCDESC + /* Function call goes through indirect descriptor */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) x)( x, y ) + #else + /* Function call goes directly to code */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) libco_ppc_code)( x, y ) + #endif + +#endif + +static uint32_t* co_create_( unsigned size, uintptr_t entry ) +{ + uint32_t* t = (uint32_t*) malloc( size ); + + (void) entry; + + #if LIBCO_PPCDESC + if ( t ) + { + /* Copy entry's descriptor */ + memcpy( t, (void*) entry, sizeof (void*) * 3 ); + + /* Set function pointer to swap routine */ + #ifdef LIBCO_PPC_ASM + *(const void**) t = *(void**) &co_swap_asm; + #else + *(const void**) t = libco_ppc_code; + #endif + } + #endif + + return t; +} + +cothread_t co_create( unsigned int size, void (*entry_)( void ) ) +{ + uintptr_t entry = (uintptr_t) entry_; + uint32_t* t = NULL; + + /* Be sure main thread was successfully allocated */ + if ( co_active() ) + { + size += state_size + above_stack + stack_align; + t = co_create_( size, entry ); + } + + if ( t ) + { + uintptr_t sp; + int shift; + + /* Save current registers into new thread, so that any special ones will + have proper values when thread is begun */ + CO_SWAP_ASM( t, t ); + + #if LIBCO_PPCDESC + /* Get real address */ + entry = (uintptr_t) *(void**) entry; + #endif + + /* Put stack near end of block, and align */ + sp = (uintptr_t) t + size - above_stack; + sp -= sp % stack_align; + + /* On PPC32, we save and restore GPRs as 32 bits. For PPC64, we + save and restore them as 64 bits, regardless of the size the ABI + uses. So, we manually write pointers at the proper size. We always + save and restore at the same address, and since PPC is big-endian, + we must put the low byte first on PPC32. */ + + /* If uintptr_t is 32 bits, >>32 is undefined behavior, so we do two shifts + and don't have to care how many bits uintptr_t is. */ + #if LIBCO_PPC64 + shift = 16; + #else + shift = 0; + #endif + + /* Set up so entry will be called on next swap */ + t [8] = (uint32_t) (entry >> shift >> shift); + t [9] = (uint32_t) entry; + + t [10] = (uint32_t) (sp >> shift >> shift); + t [11] = (uint32_t) sp; + } + + return t; +} + +void co_delete( cothread_t t ) +{ + free( t ); +} + +static void co_init_( void ) +{ + #if LIBCO_MPROTECT + /* TODO: pre- and post-pad PPC code so that this doesn't make other + data executable and writable */ + long page_size = sysconf( _SC_PAGESIZE ); + if ( page_size > 0 ) + { + uintptr_t align = page_size; + uintptr_t begin = (uintptr_t) libco_ppc_code; + uintptr_t end = begin + sizeof libco_ppc_code; + + /* Align beginning and end */ + end += align - 1; + end -= end % align; + begin -= begin % align; + + mprotect( (void*) begin, end - begin, PROT_READ | PROT_WRITE | PROT_EXEC ); + } + #endif + + co_active_handle = co_create_( state_size, (uintptr_t) &co_switch ); +} + +cothread_t co_active() +{ + if ( !co_active_handle ) + co_init_(); + + return co_active_handle; +} + +void co_switch( cothread_t t ) +{ + cothread_t old = co_active_handle; + co_active_handle = t; + + CO_SWAP_ASM( t, old ); +} diff --git a/bsnes/libco/ppc.s b/bsnes/libco/ppc.s deleted file mode 100755 index d7f6b758..00000000 --- a/bsnes/libco/ppc.s +++ /dev/null @@ -1,478 +0,0 @@ -;***** -;libco.ppc (2007-11-29) -;author: Vas Crabb -;license: public domain -; -;cross-platform PowerPC implementation of libco -;special thanks to byuu for writing the original version -; -;[ABI compatibility] -;- gcc; mac os x; ppc -; -;[nonvolatile registers] -;- GPR1, GPR13 - GPR31 -;- FPR14 - FPR31 -;- V20 - V31 -;- VRSAVE, CR2 - CR4 -; -;[volatile registers] -;- GPR0, GPR2 - GPR12 -;- FPR0 - FPR13 -;- V0 - V19 -;- LR, CTR, XER, CR0, CR1, CR5 - CR7 -;***** - - -;Declare some target-specific stuff - - .section __TEXT,__text,regular,pure_instructions - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .machine ppc - - -;Constants - - .cstring - .align 2 - -_sysctl_altivec: - .ascii "hw.optional.altivec\0" - - -;Declare space for variables - -.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX -.lcomm _co_primary_buffer,1024,2 ;buffer (will be zeroed by loader) - - .data - .align 2 - -_co_active_context: - .long _co_primary_buffer - - - .text - .align 2 - - -;Declare exported names - -.globl _co_active -.globl _co_create -.globl _co_delete -.globl _co_switch - - -;***** -;extern "C" cothread_t co_active(); -;return = GPR3 -;***** - -_co_active: - mflr r0 ;GPR0 = return address - bcl 20,31,L_co_active$spb -L_co_active$spb: - mflr r2 ;GPR2 set for position-independance - addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 - lwz r3,lo16(_co_active_context-L_co_active$spb)(r3) - mtlr r0 ;LR = return address - blr ;return - - -;***** -;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); -;GPR3 = heapsize -;GPR4 = coentry -;return = GPR3 -;***** - -_co_create: - mflr r0 ;GPR0 = return address - stmw r30,-8(r1) ;save GPR30 and GPR31 - stw r0,8(r1) ;save return address - stwu r1,-(2*4+16+24)(r1) ;allocate 16 bytes for locals/parameters - -;create heap space (stack + register storage) - addi r31,r3,1024-24 ;subtract space for linkage - mr r30,r4 ;GPR30 = coentry - addi r3,r3,1024 ;allocate extra memory for contextual info - bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) - add r4,r3,r31 ;GPR4 points to top-of-stack - rlwinm r5,r4,0,0,27 ;force 16-byte alignment - -;store thread entry point + registers, so that first call to co_switch will execute coentry - stw r30,8(r5) ;store entry point - addi r6,0,2+19+18*2+12*4+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE - addi r0,0,0 - addi r7,0,4 ;start at 4(GPR5) - mtctr r6 -L_co_create$clear_loop: - stwx r0,r5,r7 ;clear a word - addi r7,r7,-4 ;increment pointer - bdnz L_co_create$clear_loop ;loop - stwu r5,-448(r5) ;store top of stack - -;initialize context memory heap and return - stw r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) - lwz r1,0(r1) ;deallocate stack frame - lwz r8,8(r1) ;fetch return address - lmw r30,-8(r1) ;restore GPR30 and GPR31 - mtlr r8 ;return address in LR - blr ;return - - -;***** -;extern "C" void co_delete(cothread_t cothread); -;GPR3 = cothread -;***** - -_co_delete: - b L_free$stub ;free(GPR3) - - -;***** -;extern "C" void co_switch(cothread_t cothread); -;GPR3 = cothread -;***** -; -;Frame looks like: -; -;Old New Value -; 8(r1) 456(r1) Saved LR -; 4(r1) 452(r1) Saved CR -; 0(r1) 448(r1) Old GPR1 -; -4(r1) 444(r1) Saved GPR31 -; -8(r1) 440(r1) Saved GPR30 -;... ... ... -; -72(r1) 376(r1) Saved GPR14 -; -76(r1) 372(r1) Saved GPR13 -; -80(r1) 368(r1) Saved VRSAVE -; -84(r1) 364(r1) +++ -; -88(r1) 360(r1) Saved FPR31 -; -92(r1) 356(r1) +++ -; -96(r1) 352(r1) Saved FPR30 -;... ... ... -;-212(r1) 236(r1) +++ -;-216(r1) 232(r1) Saved FPR15 -;-220(r1) 228(r1) +++ -;-224(r1) 224(r1) Saved FPR14 -;-228(r1) 220(r1) +++ value -;-232(r1) 216(r1) +++ len -;-236(r1) 212(r1) +++ -;-240(r1) 208(r1) Saved VR31 -;-244(r1) 204(r1) +++ -;-248(r1) 200(r1) +++ -;-252(r1) 196(r1) +++ -;-256(r1) 192(r1) Saved VR30 -;... ... ... -;-388(r1) 60(r1) +++ -;-392(r1) 56(r1) +++ -;-396(r1) 52(r1) +++ -;-400(r1) 48(r1) Saved VR21 -;-404(r1) 44(r1) +++ -;-408(r1) 40(r1) +++ Param 5 (GPR7) -;-412(r1) 36(r1) +++ Param 4 (GPR6) -;-416(r1) 32(r1) Saved VR20 Param 3 (GPR5) -;-420(r1) 28(r1) - Param 2 (GPR4) -;-424(r1) 24(r1) - Param 1 (GPR3) -;-428(r1) 20(r1) - Reserved -;-432(r1) 16(r1) - Reserved -;-436(r1) 12(r1) - Reserved -;-440(r1) 8(r1) - New LR -;-444(r1) 4(r1) - New CR -;-448(r1) 0(r1) Saved GPR1 - - -_co_switch: - stmw r13,-76(r1) ;save preserved GPRs - stfd f14,-224(r1) ;save preserved FPRs - stfd f15,-216(r1) - stfd f16,-208(r1) - stfd f17,-200(r1) - stfd f18,-192(r1) - stfd f19,-184(r1) - stfd f20,-176(r1) - stfd f21,-168(r1) - stfd f22,-160(r1) - stfd f23,-152(r1) - stfd f24,-144(r1) - stfd f25,-136(r1) - stfd f26,-128(r1) - stfd f27,-120(r1) - stfd f28,-112(r1) - stfd f29,-104(r1) - stfd f30,-96(r1) - stfd f31,-88(r1) - mflr r0 ;save return address - stw r0,8(r1) - mfcr r2 ;save condition codes - stw r2,4(r1) - stwu r1,-448(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) - - mr r30,r3 ;save new context pointer - bcl 20,31,L_co_switch$spb ;get address of co_active_context -L_co_switch$spb: - mflr r31 - - addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags - lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) - andis. r9,r8,0x8000 ;is it initialised? - bne+ L_co_switch$initialised - - addi r0,0,4 ;len = sizeof(int) - stw r0,216(r1) - addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" - addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) - addi r4,r1,220 ;GPR4 = &value - addi r5,r1,216 ;GPR5 = &len - addi r6,0,0 ;newp = 0 - addi r7,0,0 ;newlen = 0 - bl L_sysctlbyname$stub ;call sysctlbyname - lwz r2,220(r1) ;fetch result - addis r8,0,0x8000 ;set initialised bit - cmpwi cr5,r3,0 ;assume error means not present - cmpwi cr6,r2,0 ;test result - blt- cr5,L_co_switch$store_environ - beq cr6,L_co_switch$store_environ - oris r8,r8,0x4000 ;set the flag to say we have it! -L_co_switch$store_environ: - stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags -L_co_switch$initialised: - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$save_no_vmx - mfspr r11,256 ;save VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - stw r11,368(r1) - beq L_co_switch$save_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,32 ;starting index - beq L_co_switch$save_skip_vr20 - stvx v20,r1,r2 ;save VR20 -L_co_switch$save_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$save_skip_vr21 - stvx v21,r1,r2 ;save VR21 -L_co_switch$save_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$save_skip_vr22 - stvx v22,r1,r2 ;save VR22 -L_co_switch$save_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$save_skip_vr23 - stvx v23,r1,r2 ;save VR23 -L_co_switch$save_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$save_skip_vr24 - stvx v24,r1,r2 ;save VR24 -L_co_switch$save_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$save_skip_vr25 - stvx v25,r1,r2 ;save VR25 -L_co_switch$save_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$save_skip_vr26 - stvx v26,r1,r2 ;save VR26 -L_co_switch$save_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$save_skip_vr27 - stvx v27,r1,r2 ;save VR27 -L_co_switch$save_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$save_skip_vr28 - stvx v28,r1,r2 ;save VR28 -L_co_switch$save_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$save_skip_vr29 - stvx v29,r1,r2 ;save VR29 -L_co_switch$save_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$save_skip_vr30 - stvx v30,r1,r2 ;save VR30 -L_co_switch$save_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$save_skip_vr31 - stvx v31,r1,r2 ;save VR31 -L_co_switch$save_skip_vr31: -L_co_switch$save_no_vmx: - - addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context - lwz r5,lo16(_co_active_context-L_co_switch$spb)(r4) - stw r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context - stw r1,0(r5) ;save current stack pointer - lwz r1,0(r30) ;get new stack pointer - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$restore_no_vmx - lwz r11,368(r1) ;restore VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - mtspr 256,r11 - beq L_co_switch$restore_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,32 ;starting index - beq L_co_switch$restore_skip_vr20 - lvx v20,r1,r2 ;restore VR20 -L_co_switch$restore_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$restore_skip_vr21 - lvx v21,r1,r2 ;restore VR21 -L_co_switch$restore_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$restore_skip_vr22 - lvx v22,r1,r2 ;restore VR22 -L_co_switch$restore_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$restore_skip_vr23 - lvx v23,r1,r2 ;restore VR23 -L_co_switch$restore_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$restore_skip_vr24 - lvx v24,r1,r2 ;restore VR24 -L_co_switch$restore_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$restore_skip_vr25 - lvx v25,r1,r2 ;restore VR25 -L_co_switch$restore_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$restore_skip_vr26 - lvx v26,r1,r2 ;restore VR26 -L_co_switch$restore_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$restore_skip_vr27 - lvx v27,r1,r2 ;restore VR27 -L_co_switch$restore_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$restore_skip_vr28 - lvx v28,r1,r2 ;restore VR28 -L_co_switch$restore_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$restore_skip_vr29 - lvx v29,r1,r2 ;restore VR29 -L_co_switch$restore_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$restore_skip_vr30 - lvx v30,r1,r2 ;restore VR30 -L_co_switch$restore_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$restore_skip_vr31 - lvx v31,r1,r2 ;restore VR31 -L_co_switch$restore_skip_vr31: -L_co_switch$restore_no_vmx: - - lwz r1,0(r1) ;deallocate stack frame - lwz r6,8(r1) ;return address in GPR6 - lwz r7,4(r1) ;condition codes in GPR7 - addi r0,0,0 ;make thread main crash if it returns - lmw r13,-76(r1) ;restore preserved GPRs - lfd f14,-224(r1) ;restore preserved FPRs - lfd f15,-216(r1) - lfd f16,-208(r1) - lfd f17,-200(r1) - lfd f18,-192(r1) - lfd f19,-184(r1) - lfd f20,-176(r1) - lfd f21,-168(r1) - lfd f22,-160(r1) - lfd f23,-152(r1) - lfd f24,-144(r1) - lfd f25,-136(r1) - lfd f26,-128(r1) - lfd f27,-120(r1) - lfd f28,-112(r1) - lfd f29,-104(r1) - lfd f30,-96(r1) - lfd f31,-88(r1) - mtlr r0 - mtctr r6 ;restore return address - mtcrf 32,r7 ;restore preserved condition codes - mtcrf 16,r7 - mtcrf 8,r7 - bctr ;return - - - -;Import external functions - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_malloc$stub: - .indirect_symbol _malloc - mflr r0 - bcl 20,31,L_malloc$spb -L_malloc$spb: - mflr r11 - addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) - mtlr r0 - lwzu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_malloc$lazy_ptr: - .indirect_symbol _malloc - .long dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_free$stub: - .indirect_symbol _free - mflr r0 - bcl 20,31,L_free$spb -L_free$spb: - mflr r11 - addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) - mtlr r0 - lwzu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_free$lazy_ptr: - .indirect_symbol _free - .long dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_sysctlbyname$stub: - .indirect_symbol _sysctlbyname - mflr r0 - bcl 20,31,L_sysctlbyname$spb -L_sysctlbyname$spb: - mflr r11 - addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) - mtlr r0 - lwzu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_sysctlbyname$lazy_ptr: - .indirect_symbol _sysctlbyname - .long dyld_stub_binding_helper - - -;This needs to be here! - - .subsections_via_symbols - diff --git a/bsnes/libco/ppc64.s b/bsnes/libco/ppc64.s deleted file mode 100755 index 2fb048d7..00000000 --- a/bsnes/libco/ppc64.s +++ /dev/null @@ -1,513 +0,0 @@ -;***** -;libco.ppc64 (2007-12-05) -;author: Vas Crabb -;license: public domain -; -;cross-platform 64-bit PowerPC implementation of libco -;special thanks to byuu for writing the original version -; -;[ABI compatibility] -;- gcc; mac os x; ppc64 -; -;[nonvolatile registers] -;- GPR1, GPR13 - GPR31 -;- FPR14 - FPR31 -;- V20 - V31 -;- VRSAVE, CR2 - CR4 -; -;[volatile registers] -;- GPR0, GPR2 - GPR12 -;- FPR0 - FPR13 -;- V0 - V19 -;- LR, CTR, XER, CR0, CR1, CR5 - CR7 -;***** - - -;Declare some target-specific stuff - - .section __TEXT,__text,regular,pure_instructions - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .machine ppc64 - - -;Constants - - .cstring - .align 3 - -_sysctl_altivec: - .ascii "hw.optional.altivec\0" - - -;Declare space for variables - -.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX -.lcomm _co_primary_buffer,1024,3 ;buffer (will be zeroed by loader) - - .data - .align 3 - -_co_active_context: - .quad _co_primary_buffer - - - .text - .align 2 - - -;Declare exported names - -.globl _co_active -.globl _co_create -.globl _co_delete -.globl _co_switch - - -;***** -;extern "C" cothread_t co_active(); -;return = GPR3 -;***** - -_co_active: - mflr r0 ;GPR0 = return address - bcl 20,31,L_co_active$spb -L_co_active$spb: - mflr r2 ;GPR2 set for position-independance - addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 - ld r3,lo16(_co_active_context-L_co_active$spb)(r3) - mtlr r0 ;LR = return address - blr ;return - - -;***** -;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); -;GPR3 = heapsize -;GPR4 = coentry -;return = GPR3 -;***** - -_co_create: - mflr r0 ;GPR0 = return address - std r30,-16(r1) ;save GPR30 and GPR31 - std r31,-8(r1) - std r0,16(r1) ;save return address - stdu r1,-(2*8+16+48)(r1) ;allocate 16 bytes for locals/parameters - -;create heap space (stack + register storage) - addi r31,r3,1024-48 ;subtract space for linkage - mr r30,r4 ;GPR30 = coentry - addi r3,r3,1024 ;allocate extra memory for contextual info - bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) - add r4,r3,r31 ;GPR4 points to top-of-stack - rldicr r5,r4,0,59 ;force 16-byte alignment - -;store thread entry point + registers, so that first call to co_switch will execute coentry - std r30,16(r5) ;store entry point - addi r6,0,2+19+18+12*2+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE - addi r0,0,0 - addi r7,0,8 ;start at 8(GPR5) - mtctr r6 -L_co_create$clear_loop: - stdx r0,r5,r7 ;clear a double - addi r7,r7,-8 ;increment pointer - bdnz L_co_create$clear_loop ;loop - stdu r5,-544(r5) ;store top of stack - -;initialize context memory heap and return - addis r9,0,0x8000 ;GPR13 not set (system TLS) - std r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) - stw r9,8(r3) ;this is a flag word - ld r1,0(r1) ;deallocate stack frame - ld r8,16(r1) ;fetch return address - ld r30,-16(r1) ;restore GPR30 and GPR31 - ld r31,-8(r1) - mtlr r8 ;return address in LR - blr ;return - - -;***** -;extern "C" void co_delete(cothread_t cothread); -;GPR3 = cothread -;***** - -_co_delete: - b L_free$stub ;free(GPR3) - - -;***** -;extern "C" void co_switch(cothread_t cothread); -;GPR3 = cothread -;***** -; -;Frame looks like: -; -;Old New Value -; 16(r1) 560(r1) Saved LR -; 8(r1) 552(r1) Saved CR -; 0(r1) 544(r1) Old GPR1 -; -8(r1) 536(r1) Saved GPR31 -; -16(r1) 528(r1) Saved GPR30 -;... ... ... -;-144(r1) 400(r1) Saved GPR14 -;-152(r1) 392(r1) Saved GPR13 -;-160(r1) 384(r1) Saved FPR31 -;-168(r1) 376(r1) Saved FPR30 -;... ... ... -;-288(r1) 256(r1) Saved FPR15 -;-296(r1) 248(r1) Saved FPR14 -;-304(r1) 240(r1) Saved VRSAVE -;-312(r1) 232(r1) +++ value -;-320(r1) 224(r1) Saved VR31 len -;-328(r1) 216(r1) +++ -;-336(r1) 208(r1) Saved VR30 -;... ... ... -;-456(r1) 88(r1) +++ -;-464(r1) 80(r1) Saved VR22 Param 5 (GPR7) -;-472(r1) 72(r1) +++ Param 4 (GPR6) -;-480(r1) 64(r1) Saved VR21 Param 3 (GPR5) -;-488(r1) 56(r1) +++ Param 2 (GPR4) -;-496(r1) 48(r1) Saved VR20 Param 1 (GPR3) -;-504(r1) 40(r1) - Reserved -;-512(r1) 32(r1) - Reserved -;-520(r1) 24(r1) - Reserved -;-528(r1) 16(r1) - New LR -;-536(r1) 8(r1) - New CR -;-544(r1) 0(r1) Saved GPR1 - - -_co_switch: - std r13,-152(r1) ;save preserved GPRs - std r14,-144(r1) - std r15,-136(r1) - std r16,-128(r1) - std r17,-120(r1) - std r18,-112(r1) - std r19,-104(r1) - std r20,-96(r1) - std r21,-88(r1) - std r22,-80(r1) - std r23,-72(r1) - std r24,-64(r1) - std r25,-56(r1) - std r26,-48(r1) - std r27,-40(r1) - std r28,-32(r1) - std r29,-24(r1) - std r30,-16(r1) - std r31,-8(r1) - mflr r0 ;save return address - std r0,16(r1) - mfcr r2 ;save condition codes - stw r2,8(r1) - stdu r1,-544(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) - stfd f14,248(r1) ;save preserved FPRs - stfd f15,256(r1) - stfd f16,264(r1) - stfd f17,272(r1) - stfd f18,280(r1) - stfd f19,288(r1) - stfd f20,296(r1) - stfd f21,304(r1) - stfd f22,312(r1) - stfd f23,320(r1) - stfd f24,328(r1) - stfd f25,336(r1) - stfd f26,344(r1) - stfd f27,352(r1) - stfd f28,360(r1) - stfd f29,368(r1) - stfd f30,376(r1) - stfd f31,384(r1) - - mr r30,r3 ;save new context pointer - bcl 20,31,L_co_switch$spb ;get address of co_active_context -L_co_switch$spb: - mflr r31 - - addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags - lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) - andis. r9,r8,0x8000 ;is it initialised? - bne+ L_co_switch$initialised - - addi r0,0,4 ;len = sizeof(int) - std r0,224(r1) - addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" - addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) - addi r4,r1,232 ;GPR4 = &value - addi r5,r1,224 ;GPR5 = &len - addi r6,0,0 ;newp = 0 - addi r7,0,0 ;newlen = 0 - bl L_sysctlbyname$stub ;call sysctlbyname - lwz r2,232(r1) ;fetch result - addis r8,0,0x8000 ;set initialised bit - cmpdi cr5,r3,0 ;assume error means not present - cmpwi cr6,r2,0 ;test result - blt- cr5,L_co_switch$store_environ - beq cr6,L_co_switch$store_environ - oris r8,r8,0x4000 ;set the flag to say we have it! -L_co_switch$store_environ: - stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags -L_co_switch$initialised: - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$save_no_vmx - mfspr r11,256 ;save VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - stw r11,240(r1) - beq L_co_switch$save_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,48 ;starting index - beq L_co_switch$save_skip_vr20 - stvx v20,r1,r2 ;save VR20 -L_co_switch$save_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$save_skip_vr21 - stvx v21,r1,r2 ;save VR21 -L_co_switch$save_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$save_skip_vr22 - stvx v22,r1,r2 ;save VR22 -L_co_switch$save_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$save_skip_vr23 - stvx v23,r1,r2 ;save VR23 -L_co_switch$save_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$save_skip_vr24 - stvx v24,r1,r2 ;save VR24 -L_co_switch$save_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$save_skip_vr25 - stvx v25,r1,r2 ;save VR25 -L_co_switch$save_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$save_skip_vr26 - stvx v26,r1,r2 ;save VR26 -L_co_switch$save_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$save_skip_vr27 - stvx v27,r1,r2 ;save VR27 -L_co_switch$save_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$save_skip_vr28 - stvx v28,r1,r2 ;save VR28 -L_co_switch$save_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$save_skip_vr29 - stvx v29,r1,r2 ;save VR29 -L_co_switch$save_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$save_skip_vr30 - stvx v30,r1,r2 ;save VR30 -L_co_switch$save_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$save_skip_vr31 - stvx v31,r1,r2 ;save VR31 -L_co_switch$save_skip_vr31: -L_co_switch$save_no_vmx: - - addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context - ld r5,lo16(_co_active_context-L_co_switch$spb)(r4) - std r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context - std r1,0(r5) ;save current stack pointer - ld r1,0(r30) ;get new stack pointer - lwz r12,8(r30) ;have we already set GPR13 (system TLS)? - andis. r0,r12,0x8000 - beq+ L_co_switch$gpr13_set - std r13,392(r1) - xoris r12,r12,0x8000 - stw r12,8(r30) -L_co_switch$gpr13_set: - - andis. r10,r8,0x4000 ;do we have Altivec/VMX? - beq L_co_switch$restore_no_vmx - lwz r11,240(r1) ;restore VRSAVE - andi. r0,r11,0x0FFF ;short-circuit if it's zero - mtspr 256,r11 - beq L_co_switch$restore_no_vmx - andi. r0,r11,0x0800 ;check bit 20 - addi r2,0,48 ;starting index - beq L_co_switch$restore_skip_vr20 - lvx v20,r1,r2 ;restore VR20 -L_co_switch$restore_skip_vr20: - addi r2,r2,16 ;stride - andi. r0,r11,0x0400 ;check bit 21 - beq L_co_switch$restore_skip_vr21 - lvx v21,r1,r2 ;restore VR21 -L_co_switch$restore_skip_vr21: - addi r2,r2,16 ;stride - andi. r0,r11,0x0200 ;check bit 22 - beq L_co_switch$restore_skip_vr22 - lvx v22,r1,r2 ;restore VR22 -L_co_switch$restore_skip_vr22: - addi r2,r2,16 ;stride - andi. r0,r11,0x0100 ;check bit 23 - beq L_co_switch$restore_skip_vr23 - lvx v23,r1,r2 ;restore VR23 -L_co_switch$restore_skip_vr23: - addi r2,r2,16 ;stride - andi. r0,r11,0x0080 ;check bit 24 - beq L_co_switch$restore_skip_vr24 - lvx v24,r1,r2 ;restore VR24 -L_co_switch$restore_skip_vr24: - addi r2,r2,16 ;stride - andi. r0,r11,0x0040 ;check bit 25 - beq L_co_switch$restore_skip_vr25 - lvx v25,r1,r2 ;restore VR25 -L_co_switch$restore_skip_vr25: - addi r2,r2,16 ;stride - andi. r0,r11,0x0020 ;check bit 26 - beq L_co_switch$restore_skip_vr26 - lvx v26,r1,r2 ;restore VR26 -L_co_switch$restore_skip_vr26: - addi r2,r2,16 ;stride - andi. r0,r11,0x0010 ;check bit 27 - beq L_co_switch$restore_skip_vr27 - lvx v27,r1,r2 ;restore VR27 -L_co_switch$restore_skip_vr27: - addi r2,r2,16 ;stride - andi. r0,r11,0x0008 ;check bit 28 - beq L_co_switch$restore_skip_vr28 - lvx v28,r1,r2 ;restore VR28 -L_co_switch$restore_skip_vr28: - addi r2,r2,16 ;stride - andi. r0,r11,0x0004 ;check bit 29 - beq L_co_switch$restore_skip_vr29 - lvx v29,r1,r2 ;restore VR29 -L_co_switch$restore_skip_vr29: - addi r2,r2,16 ;stride - andi. r0,r11,0x0002 ;check bit 30 - beq L_co_switch$restore_skip_vr30 - lvx v30,r1,r2 ;restore VR30 -L_co_switch$restore_skip_vr30: - addi r2,r2,16 ;stride - andi. r0,r11,0x0001 ;check bit 31 - beq L_co_switch$restore_skip_vr31 - lvx v31,r1,r2 ;restore VR31 -L_co_switch$restore_skip_vr31: -L_co_switch$restore_no_vmx: - - lfd f14,248(r1) ;restore preserved FPRs - lfd f15,256(r1) - lfd f16,264(r1) - lfd f17,272(r1) - lfd f18,280(r1) - lfd f19,288(r1) - lfd f20,296(r1) - lfd f21,304(r1) - lfd f22,312(r1) - lfd f23,320(r1) - lfd f24,328(r1) - lfd f25,336(r1) - lfd f26,344(r1) - lfd f27,352(r1) - lfd f28,360(r1) - lfd f29,368(r1) - lfd f30,376(r1) - lfd f31,384(r1) - addi r0,0,0 ;make thread main crash if it returns - ld r1,0(r1) ;deallocate stack frame - ld r6,16(r1) ;return address in GPR6 - lwz r7,8(r1) ;condition codes in GPR7 - ld r13,-152(r1) ;restore preserved GPRs - ld r14,-144(r1) - ld r15,-136(r1) - ld r16,-128(r1) - ld r17,-120(r1) - ld r18,-112(r1) - ld r19,-104(r1) - ld r20,-96(r1) - ld r21,-88(r1) - ld r22,-80(r1) - ld r23,-72(r1) - ld r24,-64(r1) - ld r25,-56(r1) - ld r26,-48(r1) - ld r27,-40(r1) - ld r28,-32(r1) - ld r29,-24(r1) - ld r30,-16(r1) - ld r31,-8(r1) - mtlr r0 - mtctr r6 ;restore return address - mtcrf 32,r7 ;restore preserved condition codes - mtcrf 16,r7 - mtcrf 8,r7 - bctr ;return - - - -;Import external functions - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_malloc$stub: - .indirect_symbol _malloc - mflr r0 - bcl 20,31,L_malloc$spb -L_malloc$spb: - mflr r11 - addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) - mtlr r0 - ldu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_malloc$lazy_ptr: - .indirect_symbol _malloc - .quad dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_free$stub: - .indirect_symbol _free - mflr r0 - bcl 20,31,L_free$spb -L_free$spb: - mflr r11 - addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) - mtlr r0 - ldu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_free$lazy_ptr: - .indirect_symbol _free - .quad dyld_stub_binding_helper - - - .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 - .align 5 -L_sysctlbyname$stub: - .indirect_symbol _sysctlbyname - mflr r0 - bcl 20,31,L_sysctlbyname$spb -L_sysctlbyname$spb: - mflr r11 - addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) - mtlr r0 - ldu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) - mtctr r12 - bctr - .lazy_symbol_pointer -L_sysctlbyname$lazy_ptr: - .indirect_symbol _sysctlbyname - .quad dyld_stub_binding_helper - - -;This needs to be here! - - .subsections_via_symbols - diff --git a/bsnes/snes/alt/cpu/debugger/debugger.cpp b/bsnes/snes/alt/cpu/debugger/debugger.cpp index f1054ed7..aa93b717 100755 --- a/bsnes/snes/alt/cpu/debugger/debugger.cpp +++ b/bsnes/snes/alt/cpu/debugger/debugger.cpp @@ -56,11 +56,11 @@ bool CPUDebugger::property(unsigned id, string &name, string &value) { } //internal - item("S-CPU MDR", string("0x", strhex<2>(regs.mdr))); + item("S-CPU MDR", string("0x", hex<2>(regs.mdr))); //$2181-2183 item("$2181-$2183", ""); - item("WRAM Address", string("0x", strhex<6>(status.wram_addr))); + item("WRAM Address", string("0x", hex<6>(status.wram_addr))); //$4016 item("$4016", ""); @@ -75,45 +75,45 @@ bool CPUDebugger::property(unsigned id, string &name, string &value) { //$4201 item("$4201", ""); - item("PIO", string("0x", strhex<2>(status.pio))); + item("PIO", string("0x", hex<2>(status.pio))); //$4202 item("$4202", ""); - item("Multiplicand", string("0x", strhex<2>(status.wrmpya))); + item("Multiplicand", string("0x", hex<2>(status.wrmpya))); //$4203 item("$4203", ""); - item("Multiplier", string("0x", strhex<2>(status.wrmpyb))); + item("Multiplier", string("0x", hex<2>(status.wrmpyb))); //$4204-$4205 item("$4204-$4205", ""); - item("Dividend", string("0x", strhex<4>(status.wrdiva))); + item("Dividend", string("0x", hex<4>(status.wrdiva))); //$4206 item("$4206", ""); - item("Divisor", string("0x", strhex<2>(status.wrdivb))); + item("Divisor", string("0x", hex<2>(status.wrdivb))); //$4207-$4208 item("$4207-$4208", ""); - item("H-Time", string("0x", strhex<4>(status.htime))); + item("H-Time", string("0x", hex<4>(status.htime))); //$4209-$420a item("$4209-$420a", ""); - item("V-Time", string("0x", strhex<4>(status.vtime))); + item("V-Time", string("0x", hex<4>(status.vtime))); //$420b unsigned dma_enable = 0; for(unsigned n = 0; n < 8; n++) dma_enable |= channel[n].dma_enabled << n; item("$420b", ""); - item("DMA Enable", string("0x", strhex<2>(dma_enable))); + item("DMA Enable", string("0x", hex<2>(dma_enable))); //$420c unsigned hdma_enable = 0; for(unsigned n = 0; n < 8; n++) hdma_enable |= channel[n].hdma_enabled << n; item("$420c", ""); - item("HDMA Enable", string("0x", strhex<2>(hdma_enable))); + item("HDMA Enable", string("0x", hex<2>(hdma_enable))); //$420d item("$420d", ""); @@ -130,25 +130,25 @@ bool CPUDebugger::property(unsigned id, string &name, string &value) { item("Transfer Mode", (unsigned)channel[i].transfer_mode); //$43x1 - item("B-Bus Address", string("0x", strhex<4>(channel[i].dest_addr))); + item("B-Bus Address", string("0x", hex<4>(channel[i].dest_addr))); //$43x2-$43x3 - item("A-Bus Address", string("0x", strhex<4>(channel[i].source_addr))); + item("A-Bus Address", string("0x", hex<4>(channel[i].source_addr))); //$43x4 - item("A-Bus Bank", string("0x", strhex<2>(channel[i].source_bank))); + item("A-Bus Bank", string("0x", hex<2>(channel[i].source_bank))); //$43x5-$43x6 - item("Transfer Size / Indirect Address", string("0x", strhex<4>(channel[i].transfer_size))); + item("Transfer Size / Indirect Address", string("0x", hex<4>(channel[i].transfer_size))); //$43x7 - item("Indirect Bank", string("0x", strhex<2>(channel[i].indirect_bank))); + item("Indirect Bank", string("0x", hex<2>(channel[i].indirect_bank))); //$43x8-$43x9 - item("Table Address", string("0x", strhex<4>(channel[i].hdma_addr))); + item("Table Address", string("0x", hex<4>(channel[i].hdma_addr))); //$43xa - item("Line Counter", string("0x", strhex<2>(channel[i].line_counter))); + item("Line Counter", string("0x", hex<2>(channel[i].line_counter))); } #undef item diff --git a/bsnes/snes/dsp/debugger/debugger.cpp b/bsnes/snes/dsp/debugger/debugger.cpp index 72d9d3e2..b7a5e3b5 100755 --- a/bsnes/snes/dsp/debugger/debugger.cpp +++ b/bsnes/snes/dsp/debugger/debugger.cpp @@ -14,30 +14,30 @@ bool DSPDebugger::property(unsigned id, string &name, string &value) { item("Main Volume - Right", (unsigned)state.regs[0x1c]); item("Echo Volume - Left", (unsigned)state.regs[0x2c]); item("Echo Volume - Right", (unsigned)state.regs[0x3c]); - item("Key On", string("0x", strhex<2>(state.regs[0x4c]))); - item("Key Off", string("0x", strhex<2>(state.regs[0x5c]))); + item("Key On", string("0x", hex<2>(state.regs[0x4c]))); + item("Key Off", string("0x", hex<2>(state.regs[0x5c]))); item("Flag - Reset", (bool)(state.regs[0x6c] & 0x80)); item("Flag - Mute", (bool)(state.regs[0x6c] & 0x40)); item("Flag - Echo Disable", (bool)(state.regs[0x6c] & 0x20)); item("Flag - Noise Clock", (unsigned)state.regs[0x6c] & 0x1f); item("Source End Block", (unsigned)state.regs[0x7c]); item("Echo Feedback", (unsigned)state.regs[0x0d]); - item("Pitch Modulation Enable", string("0x", strhex<2>(state.regs[0x2d]))); - item("Noise Enable", string("0x", strhex<2>(state.regs[0x3d]))); - item("Echo Enable", string("0x", strhex<2>(state.regs[0x4d]))); + item("Pitch Modulation Enable", string("0x", hex<2>(state.regs[0x2d]))); + item("Noise Enable", string("0x", hex<2>(state.regs[0x3d]))); + item("Echo Enable", string("0x", hex<2>(state.regs[0x4d]))); item("Source Directory", (unsigned)state.regs[0x5d]); item("Echo Start Address", (unsigned)state.regs[0x6d]); item("Echo Directory", (unsigned)state.regs[0x7d]); for(unsigned i = 0; i < 8; i++) { - item(string("Coefficient ", i), string("0x", strhex<2>(state.regs[(i << 4) + 0x0f]))); + item(string("Coefficient ", i), string("0x", hex<2>(state.regs[(i << 4) + 0x0f]))); } for(unsigned i = 0; i < 8; i++) { item(string("Voice ", i), ""); item("Volume - Left", (unsigned)state.regs[(i << 4) + 0x00]); item("Volume - Right", (unsigned)state.regs[(i << 4) + 0x01]); - item("Pitch Height", string("0x", strhex<4>(state.regs[(i << 4) + 0x02] + (state.regs[(i << 4) + 0x03] << 8)))); + item("Pitch Height", string("0x", hex<4>(state.regs[(i << 4) + 0x02] + (state.regs[(i << 4) + 0x03] << 8)))); item("Source Number", (unsigned)state.regs[(i << 4) + 0x04]); item("ADSR1", (unsigned)state.regs[(i << 4) + 0x05]); item("ADSR2", (unsigned)state.regs[(i << 4) + 0x06]); diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 858d3756..70521ecd 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,12 +1,12 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "072.14"; - static const unsigned SerializerVersion = 14; + static const char Version[] = "073"; + static const unsigned SerializerVersion = 15; } } -#define DEBUGGER +//#define DEBUGGER #define CHEAT_SYSTEM #include diff --git a/pixelshaders/GLSL/Curvature.shader b/shaders/GLSL/Curvature.shader similarity index 61% rename from pixelshaders/GLSL/Curvature.shader rename to shaders/GLSL/Curvature.shader index 49abf527..fb6b92ed 100755 --- a/pixelshaders/GLSL/Curvature.shader +++ b/shaders/GLSL/Curvature.shader @@ -2,6 +2,8 @@ diff --git a/pixelshaders/GLSL/HDR-TV.shader b/shaders/GLSL/HDR-TV.shader similarity index 100% rename from pixelshaders/GLSL/HDR-TV.shader rename to shaders/GLSL/HDR-TV.shader diff --git a/pixelshaders/GLSL/HQ2x.shader b/shaders/GLSL/HQ2x.shader similarity index 100% rename from pixelshaders/GLSL/HQ2x.shader rename to shaders/GLSL/HQ2x.shader diff --git a/pixelshaders/GLSL/Pixellate.shader b/shaders/GLSL/Pixellate.shader similarity index 100% rename from pixelshaders/GLSL/Pixellate.shader rename to shaders/GLSL/Pixellate.shader diff --git a/pixelshaders/GLSL/Scale2x.shader b/shaders/GLSL/Scale2x.shader similarity index 100% rename from pixelshaders/GLSL/Scale2x.shader rename to shaders/GLSL/Scale2x.shader diff --git a/pixelshaders/HLSL/Sepia.shader b/shaders/HLSL/Sepia.shader similarity index 100% rename from pixelshaders/HLSL/Sepia.shader rename to shaders/HLSL/Sepia.shader diff --git a/snespurify/cc-gtk.sh b/snespurify/cc-gtk.sh new file mode 100755 index 00000000..3d1ce69f --- /dev/null +++ b/snespurify/cc-gtk.sh @@ -0,0 +1,4 @@ +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp `pkg-config --cflags gtk+-2.0` -DPHOENIX_GTK +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp -DPHOENIX_GTK +g++-4.5 -s -o snespurify snespurify.o phoenix.o `pkg-config --libs gtk+-2.0` +rm *.o diff --git a/snespurify/cc-windows.bat b/snespurify/cc-windows.bat new file mode 100755 index 00000000..69c2194e --- /dev/null +++ b/snespurify/cc-windows.bat @@ -0,0 +1,7 @@ +windres phoenix/windows/phoenix.rc phoenix-resource.o +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp -DPHOENIX_WINDOWS +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp -DPHOENIX_WINDOWS +g++ -mwindows -s -o snespurify snespurify.o phoenix.o phoenix-resource.o -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 +upx --best snespurify.exe +@pause +@del *.o diff --git a/snespurify/nall/Makefile b/snespurify/nall/Makefile new file mode 100755 index 00000000..9a93bd23 --- /dev/null +++ b/snespurify/nall/Makefile @@ -0,0 +1,109 @@ +# 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 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + ifeq ($(platform),win) + compiler := gcc + else ifeq ($(platform),osx) + compiler := gcc-mp-4.5 + else + compiler := gcc-4.5 + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/snespurify/nall/algorithm.hpp b/snespurify/nall/algorithm.hpp new file mode 100755 index 00000000..037f0bb7 --- /dev/null +++ b/snespurify/nall/algorithm.hpp @@ -0,0 +1,17 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } +} + +#endif diff --git a/snespurify/nall/any.hpp b/snespurify/nall/any.hpp new file mode 100755 index 00000000..b31cff3c --- /dev/null +++ b/snespurify/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/snespurify/nall/array.hpp b/snespurify/nall/array.hpp new file mode 100755 index 00000000..9cfe7758 --- /dev/null +++ b/snespurify/nall/array.hpp @@ -0,0 +1,141 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void append(const T data) { + operator[](buffersize) = data; + } + + template void insert(unsigned index, const U list) { + unsigned listsize = container_size(list); + resize(buffersize + listsize); + memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T)); + foreach(item, list) pool[index++] = item; + } + + void insert(unsigned index, const T item) { + insert(index, array{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < buffersize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= buffersize) resize(index); //every element >= index was removed + else resize(buffersize - count); + } + + optional find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return { true, i }; + return { false, 0 }; + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //move + array& operator=(array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + array(array &&source) : pool(0), poolsize(0), buffersize(0) { + operator=(std::move(source)); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/nall/base64.hpp b/snespurify/nall/base64.hpp new file mode 100755 index 00000000..e41c87b7 --- /dev/null +++ b/snespurify/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/snespurify/nall/bit.hpp b/snespurify/nall/bit.hpp new file mode 100755 index 00000000..169fc144 --- /dev/null +++ b/snespurify/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/snespurify/nall/concept.hpp b/snespurify/nall/concept.hpp new file mode 100755 index 00000000..47167e21 --- /dev/null +++ b/snespurify/nall/concept.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +#include +#include + +namespace nall { + //unsigned count() const; + template struct has_count { enum { value = false }; }; + + //unsigned length() const; + template struct has_length { enum { value = false }; }; + + //unsigned size() const; + template struct has_size { enum { value = false }; }; + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.count(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.length(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.size(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/snespurify/nall/config.hpp b/snespurify/nall/config.hpp new file mode 100755 index 00000000..f555158e --- /dev/null +++ b/snespurify/nall/config.hpp @@ -0,0 +1,123 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: s.trim("\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0; + if(!qstrpos(line[i], " = ")) continue; + + lstring part; + part.qsplit(" = ", line[i]); + part[0].trim(); + part[1].trim(); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode::write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/snespurify/nall/crc32.hpp b/snespurify/nall/crc32.hpp new file mode 100755 index 00000000..ad36fbf6 --- /dev/null +++ b/snespurify/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + 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]); + } + return ~crc32; + } +} + +#endif diff --git a/snespurify/nall/detect.hpp b/snespurify/nall/detect.hpp new file mode 100755 index 00000000..b4991aaf --- /dev/null +++ b/snespurify/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/snespurify/nall/dictionary.hpp b/snespurify/nall/dictionary.hpp new file mode 100755 index 00000000..dcb04151 --- /dev/null +++ b/snespurify/nall/dictionary.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + if(auto pos = strpos(input, "}}")) { + string temp = substr(input, pos() + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + part[0].trim(); + part[1].trim(); + + //remove quotes + part[0].trim<1>("\""); + part[1].trim<1>("\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + dictionary& operator=(const dictionary&) = delete; + dictionary(const dictionary&) = delete; + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/snespurify/nall/directory.hpp b/snespurify/nall/directory.hpp new file mode 100755 index 00000000..df0bf086 --- /dev/null +++ b/snespurify/nall/directory.hpp @@ -0,0 +1,151 @@ +#ifndef NALL_DIRECTORY_HPP +#define NALL_DIRECTORY_HPP + +#include +#include +#include + +#if defined(_WIN32) + #include +#else + #include + #include + #include +#endif + +namespace nall { + +struct directory { + static bool exists(const string &pathname); + static lstring folders(const string &pathname, const string &pattern = "*"); + static lstring files(const string &pathname, const string &pattern = "*"); + static lstring contents(const string &pathname, const string &pattern = "*"); +}; + +#if defined(_WIN32) + inline bool directory::exists(const string &pathname) { + DWORD result = GetFileAttributes(utf16_t(pathname)); + if(result == INVALID_FILE_ATTRIBUTES) return false; + return (result & FILE_ATTRIBUTE_DIRECTORY); + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + while(FindNextFile(handle, &data) != false) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + while(FindNextFile(handle, &data) != false) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#else + inline bool directory::exists(const string &pathname) { + DIR *dp = opendir(pathname); + if(!dp) return false; + closedir(dp); + return true; + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if(ep->d_type & DT_DIR) { + if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/")); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if((ep->d_type & DT_DIR) == 0) { + if(wildcard(ep->d_name, pattern)) list.append(ep->d_name); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#endif + +} + +#endif diff --git a/snespurify/nall/dl.hpp b/snespurify/nall/dl.hpp new file mode 100755 index 00000000..6fa7603f --- /dev/null +++ b/snespurify/nall/dl.hpp @@ -0,0 +1,96 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*, const char* = ""); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(filepath)); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*, const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/snespurify/nall/endian.hpp b/snespurify/nall/endian.hpp new file mode 100755 index 00000000..40d15633 --- /dev/null +++ b/snespurify/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/snespurify/nall/file.hpp b/snespurify/nall/file.hpp new file mode 100755 index 00000000..103c7d4a --- /dev/null +++ b/snespurify/nall/file.hpp @@ -0,0 +1,261 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + enum class index : unsigned { absolute, relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode::write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + template void print(Args... args) { + string data(args...); + const char *p = data; + while(*p) write(*p++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, index index_ = index::absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(index_) { + case index::absolute: req_offset = offset; break; + case index::relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode::read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, mode mode_) { + if(fp) return false; + + switch(file_mode = mode_) { + #if !defined(_WIN32) + case mode::read: fp = fopen(fn, "rb"); break; + case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode::readwrite: fp = fopen(fn, "rb+"); break; + case mode::writeread: fp = fopen(fn, "wb+"); break; + #else + case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode::read; + } + + ~file() { + 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]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + mode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/snespurify/nall/filemap.hpp b/snespurify/nall/filemap.hpp new file mode 100755 index 00000000..52acb2fa --- /dev/null +++ b/snespurify/nall/filemap.hpp @@ -0,0 +1,200 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + + bool opened() const { return p_opened(); } + bool open(const char *filename, mode mode_) { return p_open(filename, mode_); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* data() { return p_handle; } + const uint8_t* data() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + filemap(const char *filename, mode mode_) : p_size(0), p_handle(0) { p_ctor(); p_open(filename, mode_); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode_) { + default: return false; + case mode::read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode::write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int open_flags, mmap_flags; + + switch(mode_) { + default: return false; + case mode::read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode::write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode::readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode::writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/snespurify/nall/foreach.hpp b/snespurify/nall/foreach.hpp new file mode 100755 index 00000000..00a039f3 --- /dev/null +++ b/snespurify/nall/foreach.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#include +#include + +#undef foreach +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) + +#endif diff --git a/snespurify/nall/function.hpp b/snespurify/nall/function.hpp new file mode 100755 index 00000000..645991fb --- /dev/null +++ b/snespurify/nall/function.hpp @@ -0,0 +1,60 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +namespace nall { + template class function; + + template class function { + struct container { + virtual R operator()(P... p) const = 0; + virtual container* copy() const = 0; + virtual ~container() {} + } *callback; + + struct global : container { + R (*function)(P...); + R operator()(P... p) const { return function(std::forward

(p)...); } + container* copy() const { return new global(function); } + global(R (*function)(P...)) : function(function) {} + }; + + template struct member : container { + R (C::*function)(P...); + C *object; + R operator()(P... p) const { return (object->*function)(std::forward

(p)...); } + container* copy() const { return new member(function, object); } + member(R (C::*function)(P...), C *object) : function(function), object(object) {} + }; + + template struct lambda : container { + L object; + R operator()(P... p) const { return object(std::forward

(p)...); } + container* copy() const { return new lambda(object); } + lambda(const L& object) : object(object) {} + }; + + public: + operator bool() const { return callback; } + R operator()(P... p) const { return (*callback)(std::forward

(p)...); } + void reset() { if(callback) { delete callback; callback = 0; } } + + function& operator=(const function &source) { + if(this != &source) { + if(callback) { delete callback; callback = 0; } + if(source.callback) callback = source.callback->copy(); + } + return *this; + } + + function(const function &source) { operator=(source); } + function() : callback(0) {} + function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); } + function(R (*function)(P...)) { callback = new global(function); } + template function(R (C::*function)(P...), C *object) { callback = new member(function, object); } + template function(R (C::*function)(P...) const, C *object) { callback = new member((R (C::*)(P...))function, object); } + template function(const L& object) { callback = new lambda(object); } + ~function() { if(callback) delete callback; } + }; +} + +#endif diff --git a/snespurify/nall/input.hpp b/snespurify/nall/input.hpp new file mode 100755 index 00000000..28b10453 --- /dev/null +++ b/snespurify/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "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", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + 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, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + s.ltrim("KB"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + s.ltrim("MS"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + s.ltrim("JP"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/snespurify/nall/lzss.hpp b/snespurify/nall/lzss.hpp new file mode 100755 index 00000000..202bc814 --- /dev/null +++ b/snespurify/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/snespurify/nall/moduloarray.hpp b/snespurify/nall/moduloarray.hpp new file mode 100755 index 00000000..be549ae9 --- /dev/null +++ b/snespurify/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/snespurify/nall/platform.hpp b/snespurify/nall/platform.hpp new file mode 100755 index 00000000..72eeec09 --- /dev/null +++ b/snespurify/nall/platform.hpp @@ -0,0 +1,122 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface + #define dllexport __declspec(dllexport) +#else + #include + #include + #include + #define dllexport +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +//========================= +//file system functionality +//========================= + +#if defined(_WIN32) + inline char* realpath(const char *filename, char *resolvedname) { + wchar_t fn[_MAX_PATH] = L""; + _wfullpath(fn, nall::utf16_t(filename), _MAX_PATH); + strcpy(resolvedname, nall::utf8_t(fn)); + return resolvedname; + } + + inline char* userpath(char *path) { + wchar_t fp[_MAX_PATH] = L""; + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp); + strcpy(path, nall::utf8_t(fp)); + return path; + } + + inline char* getcwd(char *path) { + wchar_t fp[_MAX_PATH] = L""; + _wgetcwd(fp, _MAX_PATH); + strcpy(path, nall::utf8_t(fp)); + return path; + } +#else + //realpath() already exists + + inline char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; + } + + inline char *getcwd(char *path) { + return getcwd(path, PATH_MAX); + } +#endif + +#endif + diff --git a/snespurify/nall/priorityqueue.hpp b/snespurify/nall/priorityqueue.hpp new file mode 100755 index 00000000..7104e791 --- /dev/null +++ b/snespurify/nall/priorityqueue.hpp @@ -0,0 +1,109 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/snespurify/nall/property.hpp b/snespurify/nall/property.hpp new file mode 100755 index 00000000..6fd33acd --- /dev/null +++ b/snespurify/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/snespurify/nall/random.hpp b/snespurify/nall/random.hpp new file mode 100755 index 00000000..74ebc2d2 --- /dev/null +++ b/snespurify/nall/random.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_RANDOM_HPP +#define NALL_RANDOM_HPP + +namespace nall { + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } + + struct random_cyclic { + unsigned seed; + inline unsigned operator()() { + return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320); + } + random_cyclic() : seed(0) {} + }; +} + +#endif diff --git a/snespurify/nall/serial.hpp b/snespurify/nall/serial.hpp new file mode 100755 index 00000000..9ac8451a --- /dev/null +++ b/snespurify/nall/serial.hpp @@ -0,0 +1,85 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate, bool flowcontrol) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL); + attr.c_cflag |= (CS8 | CREAD); + if(flowcontrol == false) { + attr.c_cflag &= ~CRTSCTS; + } else { + attr.c_cflag |= CRTSCTS; + } + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/snespurify/nall/serializer.hpp b/snespurify/nall/serializer.hpp new file mode 100755 index 00000000..ff2337ab --- /dev/null +++ b/snespurify/nall/serializer.hpp @@ -0,0 +1,146 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //move + serializer& operator=(serializer &&s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = s.idata; + isize = s.isize; + icapacity = s.icapacity; + + s.idata = 0; + return *this; + } + + serializer(serializer &&s) { + operator=(std::move(s)); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + icapacity = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/snespurify/nall/sha256.hpp b/snespurify/nall/sha256.hpp new file mode 100755 index 00000000..7f41f04e --- /dev/null +++ b/snespurify/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +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; + }; + + 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; + } + + 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); + } + } + + 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); + } + + 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/snespurify/nall/snes/info.hpp b/snespurify/nall/snes/info.hpp new file mode 100755 index 00000000..df8b5f59 --- /dev/null +++ b/snespurify/nall/snes/info.hpp @@ -0,0 +1,869 @@ +#ifndef NALL_SNES_INFO_HPP +#define NALL_SNES_INFO_HPP + +namespace nall { + +class snes_information { +public: + string xml_memory_map; + + inline snes_information(const uint8_t *data, unsigned size); + +//private: + inline void read_header(const uint8_t *data, unsigned size); + inline unsigned find_header(const uint8_t *data, unsigned size); + inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); + inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); + inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameBoy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + bool loaded; //is a base cartridge inserted? + unsigned crc32; //crc32 of all cartridges (base+slot(s)) + unsigned rom_size; + unsigned ram_size; + + Mode mode; + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + + bool has_bsx_slot; + bool has_superfx; + bool has_sa1; + bool has_srtc; + bool has_sdd1; + bool has_spc7110; + bool has_spc7110rtc; + bool has_cx4; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +snes_information::snes_information(const uint8_t *data, unsigned size) { + read_header(data, size); + + string xml = "\n"; + + if(type == TypeBsx) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeSufamiTurbo) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeGameBoy) { + xml << "\n"; + if(gameboy_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xml_memory_map = xml; + return; + } + + xml << "\n"; + + if(type == TypeSuperGameBoy1Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(type == TypeSuperGameBoy2Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(has_spc7110) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + if(has_spc7110rtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == LoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + xml << " \n"; + } else { + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == ExLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + } else if(mapper == ExHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == SuperFXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == SA1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == STROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_srtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_sdd1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_cx4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp1) { + xml << " \n"; + if(dsp1_mapper == DSP1LoROM1MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1LoROM2MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + + if(has_dsp2) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp3) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_obc1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st010) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st011) { + //ST-0011 addresses not verified; chip is unsupported + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st018) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + xml << "\n"; + xml_memory_map = xml; +} + +void snes_information::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + if(size < 32768) { + type = TypeUnknown; + return; + } + + const unsigned index = find_header(data, size); + const uint8_t mapperid = data[index + Mapper]; + const uint8_t rom_type = data[index + RomType]; + const uint8_t rom_size = data[index + RomSize]; + const uint8_t company = data[index + Company]; + const uint8_t regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 13 = NTSC; 2 - 12 = PAL + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8_t n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; + } else if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned snes_information::find_header(const uint8_t *data, unsigned size) { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned snes_information::score_header(const uint8_t *data, unsigned size, unsigned addr) { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +unsigned snes_information::gameboy_ram_size(const uint8_t *data, unsigned size) { + if(size < 512) return 0; + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 8 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool snes_information::gameboy_has_rtc(const uint8_t *data, unsigned size) { + if(size < 512) return false; + if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true; + return false; +} + +} + +#endif diff --git a/snespurify/nall/sort.hpp b/snespurify/nall/sort.hpp new file mode 100755 index 00000000..23c317a5 --- /dev/null +++ b/snespurify/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/snespurify/nall/static.hpp b/snespurify/nall/static.hpp new file mode 100755 index 00000000..4acb9fd0 --- /dev/null +++ b/snespurify/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/snespurify/nall/stdint.hpp b/snespurify/nall/stdint.hpp new file mode 100755 index 00000000..d8b6c788 --- /dev/null +++ b/snespurify/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + + static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); +} + +#endif diff --git a/snespurify/nall/string.hpp b/snespurify/nall/string.hpp new file mode 100755 index 00000000..9acc2e9d --- /dev/null +++ b/snespurify/nall/string.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/snespurify/nall/string/base.hpp b/snespurify/nall/string/base.hpp new file mode 100755 index 00000000..77f15e17 --- /dev/null +++ b/snespurify/nall/string/base.hpp @@ -0,0 +1,159 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + inline void reserve(unsigned); + + inline string& assign(const char*); + inline string& append(const char*); + inline string& append(bool); + inline string& append(signed int value); + inline string& append(unsigned int value); + inline string& append(double value); + + inline bool readfile(const char*); + + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + inline unsigned length() const; + + inline bool equals(const char*) const; + inline bool iequals(const char*) const; + + inline bool wildcard(const char*) const; + inline bool iwildcard(const char*) const; + + inline bool beginswith(const char*) const; + inline bool ibeginswith(const char*) const; + inline bool endswith(const char*) const; + inline bool iendswith(const char*) const; + + inline string& lower(); + inline string& upper(); + inline string& transform(const char *before, const char *after); + + template inline string& ltrim(const char *key = " "); + template inline string& rtrim(const char *key = " "); + template inline string& trim (const char *key = " "); + + inline optional position(const char *key) const; + inline optional qposition(const char *key) const; + + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string& operator=(const string&); + inline string& operator=(string&&); + + template inline string(Args&&... args); + inline string(const string&); + inline string(string&&); + inline ~string(); + + protected: + char *data; + unsigned size; + + #if defined(QSTRING_H) + public: + inline operator QString() const; + #endif + }; + + class lstring : public linear_vector { + public: + template inline lstring& operator<<(T value); + + inline optional find(const char*) const; + template inline void split (const char*, const char*); + template inline void qsplit(const char*, const char*); + + lstring(); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *str1, const char *str2); + inline bool wildcard(const char *str, const char *pattern); + inline bool iwildcard(const char *str, const char *pattern); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t strhex (const char *str); + inline intmax_t strsigned (const char *str); + inline uintmax_t strunsigned(const char *str); + inline uintmax_t strbin (const char *str); + inline double strdouble (const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //platform.hpp + inline string realpath(const char *name); + inline string userpath(); + inline string currentpath(); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //strpos.hpp + inline optional strpos(const char *str, const char *key); + inline optional qstrpos(const char *str, const char *key); + + //trim.hpp + template inline char* ltrim(char *str, const char *key = " "); + template inline char* rtrim(char *str, const char *key = " "); + template inline char* trim (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + template inline string strhex(uintmax_t value); + template inline string strsigned(intmax_t value); + template inline string strunsigned(uintmax_t value); + template inline string strbin(uintmax_t value); + inline unsigned strdouble(char *str, double value); + inline string strdouble(double value); + + //variadic.hpp + template inline void print(Args&&... args); +}; + +#endif diff --git a/snespurify/nall/string/bsv.hpp b/snespurify/nall/string/bsv.hpp new file mode 100755 index 00000000..d4b919e0 --- /dev/null +++ b/snespurify/nall/string/bsv.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_STRING_BSV_HPP +#define NALL_STRING_BSV_HPP + +//BSV parser +//version 0.01 + +namespace nall { + +inline string bsv_decode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '}' ) return ""; + if(*input == '\r') return ""; + if(*input == '\n') return ""; + + //normal characters + if(*input != '{') { output[offset++] = *input++; continue; } + + //entities + if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; } + if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; } + if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; } + + //illegal entities + return ""; + } + output[offset] = 0; + return output; +} + +inline string bsv_encode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '\r') return ""; + + if(*input == '\n') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'f'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '{') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '}') { + output[offset++] = '{'; + output[offset++] = 'r'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + output[offset++] = *input++; + } + output[offset] = 0; + return output; +} + +} + +#endif diff --git a/snespurify/nall/string/cast.hpp b/snespurify/nall/string/cast.hpp new file mode 100755 index 00000000..5b17c408 --- /dev/null +++ b/snespurify/nall/string/cast.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (bool v) { return v ? "true" : "false"; } +template<> inline string to_string (signed int v) { return strsigned(v); } +template<> inline string to_string (unsigned int v) { return strunsigned(v); } +template<> inline string to_string (double v) { return strdouble(v); } +template<> inline string to_string (char *v) { return v; } +template<> inline string to_string (const char *v) { return v; } +template<> inline string to_string (string v) { return v; } +template<> inline string to_string(const string &v) { return v; } + +template string& string::operator= (T value) { return assign(to_string(value)); } +template string& string::operator<<(T value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QSTRING_H) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/snespurify/nall/string/compare.hpp b/snespurify/nall/string/compare.hpp new file mode 100755 index 00000000..bce0895b --- /dev/null +++ b/snespurify/nall/string/compare.hpp @@ -0,0 +1,110 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_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 stricmp(const char *str1, const char *str2) { + while(*str1) { + if(chrlower(*str1) != chrlower(*str2)) break; + str1++, str2++; + } + return (int)chrlower(*str1) - (int)chrlower(*str2); +} + +bool wildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && *s != *p) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || *p == *s) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool iwildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && chrlower(*s) != chrlower(*p)) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || chrlower(*p) == chrlower(*s)) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + 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) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + 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/snespurify/nall/string/convert.hpp b/snespurify/nall/string/convert.hpp new file mode 100755 index 00000000..12a6c1ff --- /dev/null +++ b/snespurify/nall/string/convert.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* 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; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.' || x == ',') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +} + +#endif diff --git a/snespurify/nall/string/core.hpp b/snespurify/nall/string/core.hpp new file mode 100755 index 00000000..bad29030 --- /dev/null +++ b/snespurify/nall/string/core.hpp @@ -0,0 +1,139 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string& string::append(bool value) { append(value ? "true" : "false"); return *this; } +string& string::append(signed int value) { append(strsigned(value)); return *this; } +string& string::append(unsigned int value) { append(strunsigned(value)); return *this; } +string& string::append(double value) { append(strdouble(value)); return *this; } + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string& string::operator=(string &&source) { + if(data) free(data); + size = source.size; + data = source.data; + source.data = 0; + source.size = 0; + return *this; +} + +static void istring(string &output) { +} + +template +static void istring(string &output, const T &value, Args&&... args) { + output.append(value); + istring(output, std::forward(args)...); +} + +template string::string(Args&&... args) { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; + istring(*this, std::forward(args)...); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::string(string &&source) { + size = source.size; + data = source.data; + source.data = 0; +} + +string::~string() { + if(data) free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +optional lstring::find(const char *key) const { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return { true, i }; + } + return { false, 0 }; +} + +inline lstring::lstring() { +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/snespurify/nall/string/filename.hpp b/snespurify/nall/string/filename.hpp new file mode 100755 index 00000000..93d605ae --- /dev/null +++ b/snespurify/nall/string/filename.hpp @@ -0,0 +1,63 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/" +// "foo/" -> "foo/" +// "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/snespurify/nall/string/math.hpp b/snespurify/nall/string/math.hpp new file mode 100755 index 00000000..ea8b99c8 --- /dev/null +++ b/snespurify/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/snespurify/nall/string/platform.hpp b/snespurify/nall/string/platform.hpp new file mode 100755 index 00000000..42c1a756 --- /dev/null +++ b/snespurify/nall/string/platform.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_PLATFORM_HPP +#define NALL_STRING_PLATFORM_HPP + +namespace nall { + +string realpath(const char *name) { + char path[PATH_MAX]; + if(::realpath(name, path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string userpath() { + char path[PATH_MAX]; + if(::userpath(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string currentpath() { + char path[PATH_MAX]; + if(::getcwd(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +} + +#endif diff --git a/snespurify/nall/string/replace.hpp b/snespurify/nall/string/replace.hpp new file mode 100755 index 00000000..db405a9b --- /dev/null +++ b/snespurify/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/snespurify/nall/string/split.hpp b/snespurify/nall/string/split.hpp new file mode 100755 index 00000000..8d3ca877 --- /dev/null +++ b/snespurify/nall/string/split.hpp @@ -0,0 +1,58 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +template void lstring::split(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +template void lstring::qsplit(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/snespurify/nall/string/strl.hpp b/snespurify/nall/string/strl.hpp new file mode 100755 index 00000000..84c841fa --- /dev/null +++ b/snespurify/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/snespurify/nall/string/strpos.hpp b/snespurify/nall/string/strpos.hpp new file mode 100755 index 00000000..1907a2f3 --- /dev/null +++ b/snespurify/nall/string/strpos.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_STRPOS_HPP +#define NALL_STRING_STRPOS_HPP + +//usage example: +//if(auto pos = strpos(str, key)) print(pos(), "\n"); +//prints position of key within str, only if it is found + +namespace nall { + +optional strpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) return { true, i }; + } + + return { false, 0 }; +} + +optional qstrpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) return { true, i }; + i++; + } + + return { false, 0 }; +} + +} + +#endif diff --git a/snespurify/nall/string/trim.hpp b/snespurify/nall/string/trim.hpp new file mode 100755 index 00000000..f5355d7d --- /dev/null +++ b/snespurify/nall/string/trim.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +//limit defaults to zero, which will underflow on first compare; equivalent to no limit +template char* ltrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + if(--limit == 0) break; + } + return str; +} + +template char* rtrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strend(str, key)) { + str[strlen(str) - strlen(key)] = 0; + if(--limit == 0) break; + } + return str; +} + +template char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +} + +#endif diff --git a/snespurify/nall/string/utility.hpp b/snespurify/nall/string/utility.hpp new file mode 100755 index 00000000..d2bad881 --- /dev/null +++ b/snespurify/nall/string/utility.hpp @@ -0,0 +1,157 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +namespace nall { + +unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* arithmetic <> string */ + +template string strhex(uintmax_t value) { + string output; + unsigned offset = 0; + + //render string backwards, as we do not know its length yet + do { + unsigned n = value & 15; + output[offset++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + //reverse the string in-place + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strsigned(intmax_t value) { + string output; + unsigned offset = 0; + + bool negative = value < 0; + if(negative) value = abs(value); + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + if(negative) output[offset++] = '-'; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strunsigned(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strbin(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value & 1; + output[offset++] = '0' + n; + value >>= 1; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +//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 strdouble(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string strdouble(double value) { + string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +} + +#endif diff --git a/snespurify/nall/string/variadic.hpp b/snespurify/nall/string/variadic.hpp new file mode 100755 index 00000000..6c027fc8 --- /dev/null +++ b/snespurify/nall/string/variadic.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +template inline void print(Args&&... args) { + printf("%s", (const char*)string(std::forward(args)...)); +} + +} + +#endif diff --git a/snespurify/nall/string/wrapper.hpp b/snespurify/nall/string/wrapper.hpp new file mode 100755 index 00000000..eadf0a10 --- /dev/null +++ b/snespurify/nall/string/wrapper.hpp @@ -0,0 +1,33 @@ +#ifndef NALL_STRING_WRAPPER_HPP +#define NALL_STRING_WRAPPER_HPP + +namespace nall { + +unsigned string::length() const { return strlen(data); } + +bool string::equals(const char *str) const { return !strcmp(data, str); } +bool string::iequals(const char *str) const { return !stricmp(data, str); } + +bool string::wildcard(const char *str) const { return nall::wildcard(data, str); } +bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); } + +bool string::beginswith(const char *str) const { return strbegin(data, str); } +bool string::ibeginswith(const char *str) const { return stribegin(data, str); } + +bool string::endswith(const char *str) const { return strend(data, str); } +bool string::iendswith(const char *str) const { return striend(data, str); } + +string& string::lower() { nall::strlower(data); return *this; } +string& string::upper() { nall::strupper(data); return *this; } +string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; } + +template string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; } +template string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; } +template string& string::trim (const char *key) { nall::trim (data, key); return *this; } + +optional string::position(const char *key) const { return strpos(data, key); } +optional string::qposition(const char *key) const { return qstrpos(data, key); } + +} + +#endif diff --git a/snespurify/nall/string/xml.hpp b/snespurify/nall/string/xml.hpp new file mode 100755 index 00000000..185a89f9 --- /dev/null +++ b/snespurify/nall/string/xml.hpp @@ -0,0 +1,266 @@ +#ifndef NALL_STRING_XML_HPP +#define NALL_STRING_XML_HPP + +//XML subset parser +//version 0.05 + +namespace nall { + +struct xml_attribute { + string name; + string content; + virtual string parse() const; +}; + +struct xml_element : xml_attribute { + string parse() const; + linear_vector attribute; + linear_vector element; + +protected: + void parse_doctype(const char *&data); + bool parse_head(string data); + bool parse_body(const char *&data); + friend xml_element xml_parse(const char *data); +}; + +inline string xml_attribute::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline string xml_element::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + if(strbegin(source, "")) { + source += pos() + 3; + continue; + } else { + return ""; + } + } + + if(strbegin(source, "")) { + if(pos() - 9 > 0) { + string cdata = substr(source, 9, pos() - 9); + data << cdata; + offset += strlen(cdata); + } + source += 9 + offset + 3; + continue; + } else { + return ""; + } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline void xml_element::parse_doctype(const char *&data) { + name = "!DOCTYPE"; + const char *content_begin = data; + + signed counter = 0; + while(*data) { + char value = *data++; + if(value == '<') counter++; + if(value == '>') counter--; + if(counter < 0) { + content = substr(content_begin, 0, data - content_begin - 1); + return; + } + } + throw "..."; +} + +inline bool xml_element::parse_head(string data) { + data.qreplace("\t", " "); + data.qreplace("\r", " "); + data.qreplace("\n", " "); + while(qstrpos(data, " ")) data.qreplace(" ", " "); + data.qreplace(" =", "="); + data.qreplace("= ", "="); + data.rtrim(); + + lstring part; + part.qsplit(" ", data); + + name = part[0]; + if(name == "") throw "..."; + + for(unsigned i = 1; i < part.size(); i++) { + lstring side; + side.qsplit("=", part[i]); + if(side.size() != 2) throw "..."; + + xml_attribute attr; + attr.name = side[0]; + attr.content = side[1]; + if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\""); + else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'"); + else throw "..."; + attribute.append(attr); + } +} + +inline bool xml_element::parse_body(const char *&data) { + while(true) { + if(!*data) return false; + if(*data++ != '<') continue; + if(*data == '/') return false; + + if(strbegin(data, "!DOCTYPE") == true) { + parse_doctype(data); + return true; + } + + if(strbegin(data, "!--")) { + if(auto offset = strpos(data, "-->")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + if(strbegin(data, "![CDATA[")) { + if(auto offset = strpos(data, "]]>")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + string tag = substr(data, 0, offset()); + data += offset() + 1; + const char *content_begin = data; + + bool self_terminating = false; + + if(strend(tag, "?") == true) { + self_terminating = true; + tag.rtrim<1>("?"); + } else if(strend(tag, "/") == true) { + self_terminating = true; + tag.rtrim<1>("/"); + } + + parse_head(tag); + if(self_terminating) return true; + + while(*data) { + unsigned index = element.size(); + xml_element node; + if(node.parse_body(data) == false) { + if(*data == '/') { + signed length = data - content_begin - 1; + if(length > 0) content = substr(content_begin, 0, length); + + data++; + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + tag = substr(data, 0, offset()); + data += offset() + 1; + + tag.replace("\t", " "); + tag.replace("\r", " "); + tag.replace("\n", " "); + while(strpos(tag, " ")) tag.replace(" ", " "); + tag.rtrim(); + + if(name != tag) throw "..."; + return true; + } + } else { + element.append(node); + } + } + } +} + +//ensure there is only one root element +inline bool xml_validate(xml_element &document) { + unsigned root_counter = 0; + + for(unsigned i = 0; i < document.element.size(); i++) { + string &name = document.element[i].name; + if(strbegin(name, "?")) continue; + if(strbegin(name, "!")) continue; + if(++root_counter > 1) return false; + } + + return true; +} + +inline xml_element xml_parse(const char *data) { + xml_element self; + + try { + while(*data) { + xml_element node; + if(node.parse_body(data) == false) { + break; + } else { + self.element.append(node); + } + } + + if(xml_validate(self) == false) throw "..."; + return self; + } catch(const char*) { + xml_element empty; + return empty; + } +} + +} + +#endif diff --git a/snespurify/nall/ups.hpp b/snespurify/nall/ups.hpp new file mode 100755 index 00000000..ffcdb2d7 --- /dev/null +++ b/snespurify/nall/ups.hpp @@ -0,0 +1,223 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include +#include +#include +#include + +namespace nall { + +struct ups { + enum class result : unsigned { + unknown, + success, + patch_unwritable, + patch_invalid, + source_invalid, + target_invalid, + target_too_small, + patch_checksum_invalid, + source_checksum_invalid, + target_checksum_invalid, + }; + + function progress; + + result create( + const uint8_t *sourcedata, unsigned sourcelength, + const uint8_t *targetdata, unsigned targetlength, + const char *patchfilename + ) { + source_data = (uint8_t*)sourcedata, target_data = (uint8_t*)targetdata; + source_length = sourcelength, target_length = targetlength; + source_offset = target_offset = 0; + source_checksum = target_checksum = patch_checksum = ~0; + + if(patch_file.open(patchfilename, file::mode::write) == false) return result::patch_unwritable; + + patch_write('U'); + patch_write('P'); + patch_write('S'); + patch_write('1'); + encode(source_length); + encode(target_length); + + unsigned output_length = source_length > target_length ? source_length : target_length; + unsigned relative = 0; + for(unsigned offset = 0; offset < output_length;) { + uint8_t x = source_read(); + uint8_t y = target_read(); + + if(x == y) { + offset++; + continue; + } + + encode(offset++ - relative); + patch_write(x ^ y); + + while(true) { + if(offset >= output_length) { + patch_write(0x00); + break; + } + + x = source_read(); + y = target_read(); + offset++; + patch_write(x ^ y); + if(x == y) break; + } + + relative = offset; + } + + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8)); + for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8)); + uint32_t patch_result_checksum = ~patch_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8)); + + patch_file.close(); + return result::success; + } + + result apply( + const uint8_t *patchdata, unsigned patchlength, + const uint8_t *sourcedata, unsigned sourcelength, + uint8_t *targetdata, unsigned &targetlength + ) { + patch_data = (uint8_t*)patchdata, source_data = (uint8_t*)sourcedata, target_data = targetdata; + patch_length = patchlength, source_length = sourcelength, target_length = targetlength; + patch_offset = source_offset = target_offset = 0; + patch_checksum = source_checksum = target_checksum = ~0; + + if(patch_length < 18) return result::patch_invalid; + if(patch_read() != 'U') return result::patch_invalid; + if(patch_read() != 'P') return result::patch_invalid; + if(patch_read() != 'S') return result::patch_invalid; + if(patch_read() != '1') return result::patch_invalid; + + unsigned source_read_length = decode(); + unsigned target_read_length = decode(); + + if(source_length != source_read_length && source_length != target_read_length) return result::source_invalid; + targetlength = (source_length == source_read_length ? target_read_length : source_read_length); + if(target_length < targetlength) return result::target_too_small; + target_length = targetlength; + + while(patch_offset < patch_length - 12) { + unsigned length = decode(); + while(length--) target_write(source_read()); + while(true) { + uint8_t patch_xor = patch_read(); + target_write(patch_xor ^ source_read()); + if(patch_xor == 0) break; + } + } + while(source_offset < source_length) target_write(source_read()); + while(target_offset < target_length) target_write(source_read()); + + uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; + for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8); + for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8); + uint32_t patch_result_checksum = ~patch_checksum; + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8); + + if(patch_result_checksum != patch_read_checksum) return result::patch_invalid; + if(source_checksum == source_read_checksum && source_length == source_read_length) { + if(target_checksum == target_read_checksum && target_length == target_read_length) return result::success; + return result::target_invalid; + } else if(source_checksum == target_read_checksum && source_length == target_read_length) { + if(target_checksum == source_read_checksum && target_length == source_read_length) return result::success; + return result::target_invalid; + } else { + return result::source_invalid; + } + } + +private: + uint8_t *patch_data, *source_data, *target_data; + unsigned patch_length, source_length, target_length; + unsigned patch_offset, source_offset, target_offset; + unsigned patch_checksum, source_checksum, target_checksum; + file patch_file; + + uint8_t patch_read() { + if(patch_offset < patch_length) { + uint8_t n = patch_data[patch_offset++]; + patch_checksum = crc32_adjust(patch_checksum, n); + return n; + } + return 0x00; + } + + uint8_t source_read() { + if(source_offset < source_length) { + uint8_t n = source_data[source_offset++]; + source_checksum = crc32_adjust(source_checksum, n); + return n; + } + return 0x00; + } + + uint8_t target_read() { + uint8_t result = 0x00; + if(target_offset < target_length) { + result = target_data[target_offset]; + target_checksum = crc32_adjust(target_checksum, result); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + return result; + } + + void patch_write(uint8_t n) { + patch_file.write(n); + patch_checksum = crc32_adjust(patch_checksum, n); + } + + void target_write(uint8_t n) { + if(target_offset < target_length) { + target_data[target_offset] = n; + target_checksum = crc32_adjust(target_checksum, n); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + } + + void encode(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + patch_write(0x80 | x); + break; + } + patch_write(x); + offset--; + } + } + + uint64_t decode() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = patch_read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } +}; + +} + +#endif diff --git a/snespurify/nall/utf8.hpp b/snespurify/nall/utf8.hpp new file mode 100755 index 00000000..f5597b85 --- /dev/null +++ b/snespurify/nall/utf8.hpp @@ -0,0 +1,86 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef UNICODE +#undef _WIN32_WINNT +#undef NOMINMAX +#define UNICODE +#define _WIN32_WINNT 0x0501 +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + utf8_t(const utf8_t&) = delete; + utf8_t& operator=(const utf8_t&) = delete; + + private: + char *buffer; + }; + + inline void utf8_args(int &argc, char **&argv) { + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[_MAX_PATH]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } + } +} + +#endif //if defined(_WIN32) + +#endif diff --git a/snespurify/nall/utility.hpp b/snespurify/nall/utility.hpp new file mode 100755 index 00000000..60bda562 --- /dev/null +++ b/snespurify/nall/utility.hpp @@ -0,0 +1,39 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T &x, T &y) { + T temp(std::move(x)); + x = std::move(y); + y = std::move(temp); + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template class optional { + bool valid; + T value; + public: + inline operator bool() const { return valid; } + inline const T& operator()() const { if(!valid) throw; return value; } + inline optional(bool valid, const T &value) : valid(valid), value(value) {} + }; + + template inline T* allocate(unsigned size, const T &value) { + T *array = new T[size]; + for(unsigned i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/snespurify/nall/varint.hpp b/snespurify/nall/varint.hpp new file mode 100755 index 00000000..fe4732b1 --- /dev/null +++ b/snespurify/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/snespurify/nall/vector.hpp b/snespurify/nall/vector.hpp new file mode 100755 index 00000000..543c7b69 --- /dev/null +++ b/snespurify/nall/vector.hpp @@ -0,0 +1,281 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + template void insert(unsigned index, const U list) { + linear_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, linear_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline linear_vector& operator=(linear_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + linear_vector(linear_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~linear_vector() { + reset(); + } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + template void insert(unsigned index, const U list) { + pointer_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(*pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, pointer_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + *pool[i] = *pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline pointer_vector& operator=(pointer_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + pointer_vector(pointer_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~pointer_vector() { + reset(); + } + }; + + template struct has_size> { enum { value = true }; }; + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/cc-gtk.sh b/snespurify/phoenix/cc-gtk.sh new file mode 100755 index 00000000..779431e4 --- /dev/null +++ b/snespurify/phoenix/cc-gtk.sh @@ -0,0 +1,4 @@ +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix.cpp `pkg-config --cflags gtk+-2.0` -DPHOENIX_GTK +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c test.cpp -DPHOENIX_GTK +g++-4.5 -s -o test-gtk test.o phoenix.o `pkg-config --libs gtk+-2.0` +rm *.o diff --git a/snespurify/phoenix/cc-qt.sh b/snespurify/phoenix/cc-qt.sh new file mode 100755 index 00000000..45cc5237 --- /dev/null +++ b/snespurify/phoenix/cc-qt.sh @@ -0,0 +1,6 @@ +moc -i -o qt/qt.moc qt/qt.moc.hpp +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix.cpp `pkg-config --cflags QtCore QtGui` -DPHOENIX_QT +g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c test.cpp -DPHOENIX_QT +g++-4.5 -s -o test-qt test.o phoenix.o `pkg-config --libs QtCore QtGui` +rm *.o +#rm qt/qt.moc diff --git a/snespurify/phoenix/cc-windows.bat b/snespurify/phoenix/cc-windows.bat new file mode 100755 index 00000000..da544e37 --- /dev/null +++ b/snespurify/phoenix/cc-windows.bat @@ -0,0 +1,6 @@ +windres windows/phoenix.rc phoenix-resource.o +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix.cpp -DPHOENIX_WINDOWS +g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c test.cpp -DPHOENIX_WINDOWS +g++ -mconsole -s -o test-windows test.o phoenix.o phoenix-resource.o -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 +@pause +@del *.o diff --git a/snespurify/phoenix/gtk/button.cpp b/snespurify/phoenix/gtk/button.cpp new file mode 100755 index 00000000..af2a8a61 --- /dev/null +++ b/snespurify/phoenix/gtk/button.cpp @@ -0,0 +1,13 @@ +static void Button_tick(Button *self) { + if(self->onTick) self->onTick(); +} + +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_button_new_with_label(text); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "clicked", G_CALLBACK(Button_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} diff --git a/snespurify/phoenix/gtk/canvas.cpp b/snespurify/phoenix/gtk/canvas.cpp new file mode 100755 index 00000000..43913871 --- /dev/null +++ b/snespurify/phoenix/gtk/canvas.cpp @@ -0,0 +1,59 @@ +static void Canvas_expose(Canvas *self) { + uint32_t *rgb = self->canvas->bufferRGB; + uint32_t *bgr = self->canvas->bufferBGR; + for(unsigned y = self->object->widget->allocation.height; y; y--) { + for(unsigned x = self->object->widget->allocation.width; x; x--) { + uint32_t pixel = *rgb++; + *bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff); + } + } + + gdk_draw_rgb_32_image( + self->object->widget->window, + self->object->widget->style->fg_gc[GTK_WIDGET_STATE(self->object->widget)], + 0, 0, self->object->widget->allocation.width, self->object->widget->allocation.height, + GDK_RGB_DITHER_NONE, (guchar*)self->canvas->bufferBGR, self->canvas->pitch + ); +} + +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->bufferRGB = new uint32_t[width * height](); + canvas->bufferBGR = new uint32_t[width * height](); + canvas->pitch = width * sizeof(uint32_t); + + object->widget = gtk_drawing_area_new(); + widget->parent = &parent; + GdkColor color; + color.pixel = color.red = color.green = color.blue = 0; + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); + gtk_widget_set_double_buffered(object->widget, false); + gtk_widget_add_events(object->widget, GDK_EXPOSURE_MASK); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +uint32_t* Canvas::buffer() { + return canvas->bufferRGB; +} + +void Canvas::redraw() { + GdkRectangle rect; + rect.x = 0; + rect.y = 0; + rect.width = object->widget->allocation.width; + rect.height = object->widget->allocation.height; + gdk_window_invalidate_rect(object->widget->window, &rect, true); +} + +Canvas::Canvas() { + canvas = new Canvas::Data; + canvas->bufferRGB = 0; + canvas->bufferBGR = 0; +} + +Canvas::~Canvas() { + if(canvas->bufferRGB) delete[] canvas->bufferRGB; + if(canvas->bufferBGR) delete[] canvas->bufferBGR; +} diff --git a/snespurify/phoenix/gtk/checkbox.cpp b/snespurify/phoenix/gtk/checkbox.cpp new file mode 100755 index 00000000..f569775c --- /dev/null +++ b/snespurify/phoenix/gtk/checkbox.cpp @@ -0,0 +1,23 @@ +static void CheckBox_tick(CheckBox *self) { + if(self->onTick && self->object->locked == false) self->onTick(); +} + +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_check_button_new_with_label(text); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +bool CheckBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget)); +} + +void CheckBox::setChecked(bool checked) { + object->locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), checked); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/combobox.cpp b/snespurify/phoenix/gtk/combobox.cpp new file mode 100755 index 00000000..dbabf247 --- /dev/null +++ b/snespurify/phoenix/gtk/combobox.cpp @@ -0,0 +1,48 @@ +void ComboBox_change(ComboBox *self) { + if(self->object->locked == false && self->onChange) self->onChange(); +} + +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_combo_box_new_text(); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(ComboBox_change), (gpointer)this); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } + + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void ComboBox::reset() { + object->locked = true; + for(signed i = counter - 1; i >= 0; i--) { + gtk_combo_box_remove_text(GTK_COMBO_BOX(object->widget), i); + } + object->locked = false; + counter = 0; +} + +void ComboBox::addItem(const string &text) { + gtk_combo_box_append_text(GTK_COMBO_BOX(object->widget), text); + if(counter++ == 0) setSelection(0); +} + +unsigned ComboBox::selection() { + return gtk_combo_box_get_active(GTK_COMBO_BOX(object->widget)); +} + +void ComboBox::setSelection(unsigned item) { + object->locked = true; + gtk_combo_box_set_active(GTK_COMBO_BOX(object->widget), item); + object->locked = false; +} + +ComboBox::ComboBox() { + counter = 0; +} diff --git a/snespurify/phoenix/gtk/editbox.cpp b/snespurify/phoenix/gtk/editbox.cpp new file mode 100755 index 00000000..a821177d --- /dev/null +++ b/snespurify/phoenix/gtk/editbox.cpp @@ -0,0 +1,49 @@ +static void EditBox_change(EditBox *self) { + if(self->object->locked == false && self->onChange) self->onChange(); +} + +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_scrolled_window_new(0, 0); + widget->parent = &parent; + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request(object->widget, width, height); + object->subWidget = gtk_text_view_new(); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), GTK_WRAP_WORD_CHAR); + gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); + object->textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(object->subWidget)); + gtk_text_buffer_set_text(object->textBuffer, text, -1); + g_signal_connect_swapped(G_OBJECT(object->textBuffer), "changed", G_CALLBACK(EditBox_change), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->subWidget); + gtk_widget_show(object->widget); +} + +void EditBox::setFocused() { + gtk_widget_grab_focus(object->subWidget); +} + +void EditBox::setEditable(bool editable) { + gtk_text_view_set_editable(GTK_TEXT_VIEW(object->subWidget), editable); +} + +void EditBox::setWordWrap(bool wordWrap) { + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE); +} + +string EditBox::text() { + GtkTextIter start, end; + gtk_text_buffer_get_start_iter(object->textBuffer, &start); + gtk_text_buffer_get_end_iter(object->textBuffer, &end); + char *temp = gtk_text_buffer_get_text(object->textBuffer, &start, &end, true); + string text = temp; + g_free(temp); + return text; +} + +void EditBox::setText(const string &text) { + object->locked = true; + gtk_text_buffer_set_text(object->textBuffer, text, -1); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/font.cpp b/snespurify/phoenix/gtk/font.cpp new file mode 100755 index 00000000..459151e0 --- /dev/null +++ b/snespurify/phoenix/gtk/font.cpp @@ -0,0 +1,18 @@ +bool Font::create(const string &name, unsigned size, Font::Style style) { + font->font = pango_font_description_new(); + pango_font_description_set_family(font->font, name); + pango_font_description_set_size(font->font, size * PANGO_SCALE); + pango_font_description_set_style(font->font, (style & Style::Italic) == Style::Italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL); + pango_font_description_set_weight(font->font, (style & Style::Bold) == Style::Bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + return true; +} + +Font::Font() { + font = new Font::Data; + font->font = 0; +} + +Font::~Font() { + if(font->font) pango_font_description_free(font->font); + delete font; +} diff --git a/snespurify/phoenix/gtk/gtk.cpp b/snespurify/phoenix/gtk/gtk.cpp new file mode 100755 index 00000000..381e3250 --- /dev/null +++ b/snespurify/phoenix/gtk/gtk.cpp @@ -0,0 +1,189 @@ +#include +#include +#include + +#define None X11None +#define Window X11Window + +#include +#include +#include +#include + +#undef None +#undef Window + +using namespace nall; + +namespace phoenix { + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +Window Window::None; + +void OS::initialize() { + static bool initialized = false; + if(initialized == true) return; + initialized = true; + + int argc = 1; + char *argv[2]; + argv[0] = new char[8]; + argv[1] = 0; + strcpy(argv[0], "phoenix"); + char **argvp = argv; + gtk_init(&argc, &argvp); + + gtk_rc_parse_string( + "style \"phoenix-gtk\"\n" + "{\n" + " GtkComboBox::appears-as-list = 1\n" + " GtkTreeView::vertical-separator = 0\n" + "}\n" + "class \"GtkComboBox\" style \"phoenix-gtk\"\n" + "class \"GtkTreeView\" style \"phoenix-gtk\"\n" + ); +} + +bool OS::pending() { + return gtk_events_pending(); +} + +void OS::run() { + while(pending()) gtk_main_iteration_do(false); +} + +void OS::main() { + gtk_main(); +} + +void OS::quit() { + gtk_main_quit(); +} + +unsigned OS::desktopWidth() { + return gdk_screen_get_width(gdk_screen_get_default()); +} + +unsigned OS::desktopHeight() { + return gdk_screen_get_height(gdk_screen_get_default()); +} + +string OS::folderSelect(Window &parent, const string &path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Select Folder", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + if(name.endswith("/") == false) name.append("/"); + return name; +} + +string OS::fileOpen(Window &parent, const string &filter, const string &path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Open File", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")")); + lstring patterns; + patterns.split(",", part[1]); + foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +string OS::fileSave(Window &parent, const string &filter, const string &path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Save File", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")")); + lstring patterns; + patterns.split(",", part[1]); + foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +} diff --git a/snespurify/phoenix/gtk/gtk.hpp b/snespurify/phoenix/gtk/gtk.hpp new file mode 100755 index 00000000..3ba82fe2 --- /dev/null +++ b/snespurify/phoenix/gtk/gtk.hpp @@ -0,0 +1,265 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + virtual void unused(); + struct Data; + Data *object; +}; + +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const nall::string &name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + Action(); +//private: + struct Data; + Data *action; +}; + +struct Menu : Action { + void create(Window &parent, const nall::string &text); + void create(Menu &parent, const nall::string &text); +}; + +struct MenuSeparator : Action { + void create(Menu &parent); +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool checked(); + void setChecked(bool checked = true); +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + void create(MenuRadioItem &parent, const nall::string &text); + bool checked(); + void setChecked(); +private: + MenuRadioItem *first; +}; + +struct Widget : Object { + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + virtual bool focused(); + virtual void setFocused(); + virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool focused(); + void setFocused(); + Geometry geometry(); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setDefaultFont(Font &font); + void setFont(Font &font); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const nall::string &text); + void setStatusText(const nall::string &text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + Window(); +//private: + struct Data; + Data *window; + static Window None; +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(bool checked = true); +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void reset(); + void addItem(const nall::string &text); + unsigned selection(); + void setSelection(unsigned item); + ComboBox(); +private: + unsigned counter; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setFocused(); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + nall::string text(); + void setText(const nall::string &text); +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setText(const nall::string &text); +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setFocused(); + void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); + void setFont(Font &font); + void reset(); + void resizeColumnsToContent(); + void addItem(const nall::string &text); + void setItem(unsigned row, const nall::string &text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setPosition(unsigned position); +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(); +private: + RadioBox *first; +}; + +struct TextBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setEditable(bool editable = true); + nall::string text(); + void setText(const nall::string &text); +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + static bool pending(); + static void run(); + static void main(); + static void quit(); + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static nall::string folderSelect(Window &parent, const nall::string &path = ""); + static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = ""); + static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = ""); +//private: + static void initialize(); +}; + +} diff --git a/snespurify/phoenix/gtk/horizontalslider.cpp b/snespurify/phoenix/gtk/horizontalslider.cpp new file mode 100755 index 00000000..9e6e76c7 --- /dev/null +++ b/snespurify/phoenix/gtk/horizontalslider.cpp @@ -0,0 +1,25 @@ +static void HorizontalSlider_change(HorizontalSlider *self) { + if(self->object->position == self->position()) return; + self->object->position = self->position(); + if(self->onChange) self->onChange(); +} + +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + object->position = 0; + length += (length == 0); + object->widget = gtk_hscale_new_with_range(0, length - 1, 1); + widget->parent = &parent; + gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +unsigned HorizontalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget)); +} + +void HorizontalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(object->widget), position); +} diff --git a/snespurify/phoenix/gtk/label.cpp b/snespurify/phoenix/gtk/label.cpp new file mode 100755 index 00000000..8321523f --- /dev/null +++ b/snespurify/phoenix/gtk/label.cpp @@ -0,0 +1,13 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_label_new(text); + widget->parent = &parent; + gtk_misc_set_alignment(GTK_MISC(object->widget), 0.0, 0.5); + gtk_widget_set_size_request(object->widget, width, height); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void Label::setText(const string &text) { + gtk_label_set_text(GTK_LABEL(object->widget), text); +} diff --git a/snespurify/phoenix/gtk/listbox.cpp b/snespurify/phoenix/gtk/listbox.cpp new file mode 100755 index 00000000..e851e6ba --- /dev/null +++ b/snespurify/phoenix/gtk/listbox.cpp @@ -0,0 +1,195 @@ +static void ListBox_activate(ListBox *self) { + signed selection = -1; + if(auto position = self->selection()) selection = position(); + self->listBox->selection = selection; + if(self->onActivate) self->onActivate(); +} + +static void ListBox_change(ListBox *self) { + signed selection = -1; + if(auto position = self->selection()) selection = position(); + if(selection == self->listBox->selection) return; + self->listBox->selection = selection; + if(self->onChange) self->onChange(); +} + +static void ListBox_tick(GtkCellRendererToggle *cell, gchar *path_string, ListBox *self) { + unsigned index = strunsigned(path_string); + self->setChecked(index, !self->checked(index)); + if(self->onTick) self->onTick(index); +} + +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + listBox->selection = -1; + object->widget = gtk_scrolled_window_new(0, 0); + widget->parent = &parent; + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request(object->widget, width, height); + + lstring list; + list.split("\t", string("\t", text)); + + GType *v = (GType*)malloc(list.size() * sizeof(GType)); + for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING); + listBox->store = gtk_list_store_newv(list.size(), v); + free(v); + + object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store)); + gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); + g_object_unref(G_OBJECT(listBox->store)); + + for(unsigned i = 0; i < list.size(); i++) { + if(i == 0) { + listBox->column[i].renderer = gtk_cell_renderer_toggle_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "active", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, false); + gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable); + g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this); + } else { + listBox->column[i].renderer = gtk_cell_renderer_text_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "text", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, true); + } + listBox->column[i].label = gtk_label_new(list[i]); + gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label); + gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column); + gtk_widget_show(listBox->column[i].label); + } + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column + gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1); + + g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this); + g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this); + + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->subWidget); + gtk_widget_show(object->widget); +} + +void ListBox::setFocused() { + gtk_widget_grab_focus(object->subWidget); +} + +void ListBox::setHeaderVisible(bool visible) { + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), visible); +} + +void ListBox::setCheckable(bool checkable) { + listBox->checkable = checkable; + if(object->subWidget) gtk_tree_view_column_set_visible(listBox->column[0].column, checkable); +} + +void ListBox::setFont(Font &font) { + Widget::setFont(font); + unsigned columns = 1; + while(true) { + if(gtk_tree_view_get_column(GTK_TREE_VIEW(object->subWidget), columns) == 0) break; + columns++; + } + for(unsigned i = 0; i < columns; i++) { + gtk_widget_modify_font(listBox->column[i].label, font.font->font); + } +} + +void ListBox::reset() { + listBox->selection = -1; + gtk_list_store_clear(GTK_LIST_STORE(listBox->store)); + gtk_tree_view_set_model(GTK_TREE_VIEW(object->subWidget), GTK_TREE_MODEL(listBox->store)); + //reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListBox is now empty + gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(object->widget), 0); + gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(object->widget), 0); +} + +void ListBox::resizeColumnsToContent() { + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(object->subWidget)); +} + +void ListBox::addItem(const string &text) { + lstring list; + list.split("\t", text); + GtkTreeIter iter; + gtk_list_store_append(listBox->store, &iter); + unsigned index = 1; + foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); +} + +void ListBox::setItem(unsigned row, const string &text) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreeIter iter; + for(unsigned i = 0; i <= row; i++) { + if(i == 0) gtk_tree_model_get_iter_first(model, &iter); + else gtk_tree_model_iter_next(model, &iter); + } + + lstring list; + list.split("\t", text); + unsigned index = 1; + foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); +} + +bool ListBox::checked(unsigned row) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); + GtkTreeIter iter; + bool state; + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, 0, &state, -1); + gtk_tree_path_free(path); + return state; +} + +void ListBox::setChecked(unsigned row, bool checked) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); + GtkTreeIter iter; + gtk_tree_model_get_iter(model, &iter, path); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1); + gtk_tree_path_free(path); +} + +optional ListBox::selection() { + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreeIter iter; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, 0 }; + for(unsigned i = 1;; i++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, i }; + } + return { false, 0 }; +} + +void ListBox::setSelection(unsigned row) { + signed current = -1; + if(auto position = selection()) current = position(); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + gtk_tree_selection_unselect_all(selection); + + GtkTreeIter iter; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return; + if(row == 0) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + for(unsigned i = 1;; i++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return; + if(row == i) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + } +} + +ListBox::ListBox() { + listBox = new ListBox::Data; + listBox->checkable = false; +} diff --git a/snespurify/phoenix/gtk/menu.cpp b/snespurify/phoenix/gtk/menu.cpp new file mode 100755 index 00000000..fbfc67bc --- /dev/null +++ b/snespurify/phoenix/gtk/menu.cpp @@ -0,0 +1,129 @@ +static void Action_setFont(GtkWidget *widget, gpointer font) { + if(font) { + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font); + } + } +} + +bool Action::visible() { + return gtk_widget_get_visible(object->widget); +} + +void Action::setVisible(bool visible) { + gtk_widget_set_visible(object->widget, visible); +} + +bool Action::enabled() { + return gtk_widget_get_sensitive(object->widget); +} + +void Action::setEnabled(bool enabled) { + gtk_widget_set_sensitive(object->widget, enabled); +} + +Action::Action() { + action = new Action::Data; + action->font = 0; +} + +void Menu::create(Window &parent, const string &text) { + action->font = parent.window->defaultFont; + object->menu = gtk_menu_new(); + object->widget = gtk_menu_item_new_with_label(text); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_bar_append(parent.object->menu, object->widget); + gtk_widget_show(object->widget); +} + +void Menu::create(Menu &parent, const string &text) { + action->font = parent.action->font; + object->menu = gtk_menu_new(); + object->widget = gtk_menu_item_new_with_label(text); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +void MenuSeparator::create(Menu &parent) { + action->font = parent.action->font; + object->widget = gtk_separator_menu_item_new(); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +static void MenuItem_tick(MenuItem *self) { + if(self->onTick) self->onTick(); +} + +void MenuItem::create(Menu &parent, const string &text) { + action->font = parent.action->font; + object->widget = gtk_menu_item_new_with_label(text); + g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(MenuItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +static void MenuCheckItem_tick(MenuCheckItem *self) { + if(self->onTick && self->object->locked == false) self->onTick(); +} + +void MenuCheckItem::create(Menu &parent, const string &text) { + action->font = parent.action->font; + object->widget = gtk_check_menu_item_new_with_label(text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuCheckItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +bool MenuCheckItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget)); +} + +void MenuCheckItem::setChecked(bool state) { + object->locked = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), state); + object->locked = false; +} + +static void MenuRadioItem_tick(MenuRadioItem *self) { + if(self->onTick && self->checked() && self->object->locked == false) self->onTick(); +} + +void MenuRadioItem::create(Menu &parent, const string &text) { + first = this; + action->font = parent.action->font; + object->parentMenu = &parent; + object->widget = gtk_radio_menu_item_new_with_label(0, text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const string &text) { + first = parent.first; + action->font = parent.action->font; + object->parentMenu = parent.object->parentMenu; + object->widget = gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(first->object->widget), text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this); + if(action->font) Action_setFont(object->widget, action->font->font->font); + gtk_menu_shell_append(GTK_MENU_SHELL(object->parentMenu->object->menu), object->widget); + gtk_widget_show(object->widget); +} + +bool MenuRadioItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget)); +} + +void MenuRadioItem::setChecked() { + object->locked = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), true); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/messagewindow.cpp b/snespurify/phoenix/gtk/messagewindow.cpp new file mode 100755 index 00000000..6c41f022 --- /dev/null +++ b/snespurify/phoenix/gtk/messagewindow.cpp @@ -0,0 +1,65 @@ +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, gint response) { + if(response == GTK_RESPONSE_OK) return MessageWindow::Response::Ok; + if(response == GTK_RESPONSE_CANCEL) return MessageWindow::Response::Cancel; + if(response == GTK_RESPONSE_YES) return MessageWindow::Response::Yes; + if(response == GTK_RESPONSE_NO) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} diff --git a/snespurify/phoenix/gtk/object.cpp b/snespurify/phoenix/gtk/object.cpp new file mode 100755 index 00000000..f09960ac --- /dev/null +++ b/snespurify/phoenix/gtk/object.cpp @@ -0,0 +1,57 @@ +struct Object::Data { + bool locked; + GtkWidget *widget; + GtkWidget *subWidget; + GtkWidget *menuContainer; + GtkWidget *formContainer; + GtkWidget *statusContainer; + GtkWidget *menu; + GtkWidget *status; + Menu *parentMenu; + Window *parentWindow; + GtkTextBuffer *textBuffer; + unsigned position; +}; + +struct Font::Data { + PangoFontDescription *font; +}; + +struct Action::Data { + Font *font; +}; + +struct Widget::Data { + Window *parent; +}; + +struct Window::Data { + Font *defaultFont; +}; + +struct Canvas::Data { + uint32_t *bufferRGB; + uint32_t *bufferBGR; + unsigned pitch; +}; + +struct ListBox::Data { + GtkListStore *store; + struct GtkColumn { + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *label; + }; + linear_vector column; + bool checkable; + signed selection; +}; + +void Object::unused() { +} + +Object::Object() { + OS::initialize(); + object = new Object::Data; + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/progressbar.cpp b/snespurify/phoenix/gtk/progressbar.cpp new file mode 100755 index 00000000..193e924d --- /dev/null +++ b/snespurify/phoenix/gtk/progressbar.cpp @@ -0,0 +1,12 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + object->widget = gtk_progress_bar_new(); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void ProgressBar::setPosition(unsigned position) { + position = position <= 100 ? position : 0; + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)position / 100.0); +} diff --git a/snespurify/phoenix/gtk/radiobox.cpp b/snespurify/phoenix/gtk/radiobox.cpp new file mode 100755 index 00000000..603e199f --- /dev/null +++ b/snespurify/phoenix/gtk/radiobox.cpp @@ -0,0 +1,36 @@ +static void RadioBox_tick(RadioBox *self) { + if(self->onTick && self->checked() && self->object->locked == false) self->onTick(); +} + +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + first = this; + object->parentWindow = &parent; + object->widget = gtk_radio_button_new_with_label(0, text); + widget->parent = &parent; + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + first = parent.first; + object->parentWindow = parent.object->parentWindow; + object->widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(parent.object->widget), text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this); + if(object->parentWindow->window->defaultFont) setFont(*object->parentWindow->window->defaultFont); + gtk_fixed_put(GTK_FIXED(object->parentWindow->object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +bool RadioBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget)); +} + +void RadioBox::setChecked() { + object->locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), true); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/textbox.cpp b/snespurify/phoenix/gtk/textbox.cpp new file mode 100755 index 00000000..66455f03 --- /dev/null +++ b/snespurify/phoenix/gtk/textbox.cpp @@ -0,0 +1,33 @@ +static void TextBox_activate(TextBox *self) { + if(self->onActivate) self->onActivate(); +} + +static void TextBox_change(TextBox *self) { + if(self->object->locked == false && self->onChange) self->onChange(); +} + +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_entry_new(); + widget->parent = &parent; + gtk_entry_set_text(GTK_ENTRY(object->widget), text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(TextBox_activate), (gpointer)this); + g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(TextBox_change), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void TextBox::setEditable(bool editable) { + gtk_entry_set_editable(GTK_ENTRY(object->widget), editable); +} + +string TextBox::text() { + return gtk_entry_get_text(GTK_ENTRY(object->widget)); +} + +void TextBox::setText(const string &text) { + object->locked = true; + gtk_entry_set_text(GTK_ENTRY(object->widget), text); + object->locked = false; +} diff --git a/snespurify/phoenix/gtk/verticalslider.cpp b/snespurify/phoenix/gtk/verticalslider.cpp new file mode 100755 index 00000000..1cca9e4c --- /dev/null +++ b/snespurify/phoenix/gtk/verticalslider.cpp @@ -0,0 +1,25 @@ +static void VerticalSlider_change(VerticalSlider *self) { + if(self->object->position == self->position()) return; + self->object->position = self->position(); + if(self->onChange) self->onChange(); +} + +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + object->position = 0; + length += (length == 0); + object->widget = gtk_vscale_new_with_range(0, length - 1, 1); + widget->parent = &parent; + gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +unsigned VerticalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget)); +} + +void VerticalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(object->widget), position); +} diff --git a/snespurify/phoenix/gtk/viewport.cpp b/snespurify/phoenix/gtk/viewport.cpp new file mode 100755 index 00000000..3b407727 --- /dev/null +++ b/snespurify/phoenix/gtk/viewport.cpp @@ -0,0 +1,20 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + object->widget = gtk_drawing_area_new(); + widget->parent = &parent; +//gtk_widget_set_double_buffered(object->widget, false); + gtk_widget_set_size_request(object->widget, width, height); + + GdkColor color; + color.pixel = 0; + color.red = 0; + color.green = 0; + color.blue = 0; + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); + + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +uintptr_t Viewport::handle() { + return GDK_WINDOW_XID(object->widget->window); +} diff --git a/snespurify/phoenix/gtk/widget.cpp b/snespurify/phoenix/gtk/widget.cpp new file mode 100755 index 00000000..4f0bf079 --- /dev/null +++ b/snespurify/phoenix/gtk/widget.cpp @@ -0,0 +1,47 @@ +static void Widget_setFont(GtkWidget *widget, gpointer font) { + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Widget_setFont, font); + } +} + +void Widget::setFont(Font &font) { + Widget_setFont(object->widget, font.font->font); +} + +bool Widget::visible() { + return gtk_widget_get_visible(object->widget); +} + +void Widget::setVisible(bool visible) { + if(visible) gtk_widget_show(object->widget); + else gtk_widget_hide(object->widget); +} + +bool Widget::enabled() { + return gtk_widget_get_sensitive(object->widget); +} + +void Widget::setEnabled(bool enabled) { + gtk_widget_set_sensitive(object->widget, enabled); +} + +bool Widget::focused() { + return gtk_widget_is_focus(object->widget); +} + +void Widget::setFocused() { + if(visible() == false) setVisible(true); + gtk_widget_grab_focus(object->widget); +} + +void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + if(widget->parent == 0) return; + gtk_fixed_move(GTK_FIXED(widget->parent->object->formContainer), object->widget, x, y); + gtk_widget_set_size_request(object->widget, width, height); +} + +Widget::Widget() { + widget = new Widget::Data; + widget->parent = 0; +} diff --git a/snespurify/phoenix/gtk/window.cpp b/snespurify/phoenix/gtk/window.cpp new file mode 100755 index 00000000..5b289296 --- /dev/null +++ b/snespurify/phoenix/gtk/window.cpp @@ -0,0 +1,99 @@ +static gint Window_close(Window *window) { + if(window->onClose) { + if(window->onClose()) window->setVisible(false); + return true; + } + window->setVisible(false); + return true; +} + +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + object->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_move(GTK_WINDOW(object->widget), x, y); + + gtk_window_set_title(GTK_WINDOW(object->widget), text); + gtk_window_set_resizable(GTK_WINDOW(object->widget), false); + gtk_widget_set_app_paintable(object->widget, true); + + g_signal_connect_swapped(G_OBJECT(object->widget), "delete_event", G_CALLBACK(Window_close), (gpointer)this); + + object->menuContainer = gtk_vbox_new(false, 0); + gtk_container_add(GTK_CONTAINER(object->widget), object->menuContainer); + gtk_widget_show(object->menuContainer); + + object->menu = gtk_menu_bar_new(); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->menu, false, false, 0); + + object->formContainer = gtk_fixed_new(); + gtk_widget_set_size_request(object->formContainer, width, height); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->formContainer, true, true, 0); + gtk_widget_show(object->formContainer); + + object->statusContainer = gtk_event_box_new(); + object->status = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(object->status), false); + gtk_container_add(GTK_CONTAINER(object->statusContainer), object->status); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->statusContainer, false, false, 0); + gtk_widget_show(object->statusContainer); + + gtk_widget_realize(object->widget); +} + +bool Window::focused() { + return gtk_window_is_active(GTK_WINDOW(object->widget)); +} + +void Window::setFocused() { + gtk_window_present(GTK_WINDOW(object->widget)); +} + +Geometry Window::geometry() { + gint x, y, width, height; + gtk_window_get_position(GTK_WINDOW(object->widget), &x, &y); + gtk_widget_get_size_request(object->formContainer, &width, &height); + return Geometry(x, y, width, height); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + gtk_window_move(GTK_WINDOW(object->widget), x, y); + gtk_widget_set_size_request(object->formContainer, width, height); +} + +void Window::setDefaultFont(Font &font) { + window->defaultFont = &font; +} + +void Window::setFont(Font &font) { + Widget_setFont(object->status, font.font->font); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + GdkColor color; + color.pixel = (red << 16) | (green << 8) | (blue << 0); + color.red = (red << 8) | (red << 0); + color.green = (green << 8) | (green << 0); + color.blue = (blue << 8) | (blue << 0); + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); +} + +void Window::setTitle(const string &text) { + gtk_window_set_title(GTK_WINDOW(object->widget), text); +} + +void Window::setStatusText(const string &text) { + gtk_statusbar_pop(GTK_STATUSBAR(object->status), 1); + gtk_statusbar_push(GTK_STATUSBAR(object->status), 1, text); +} + +void Window::setMenuVisible(bool visible) { + gtk_widget_set_visible(object->menu, visible); +} + +void Window::setStatusVisible(bool visible) { + gtk_widget_set_visible(object->status, visible); +} + +Window::Window() { + window = new Window::Data; + window->defaultFont = 0; +} diff --git a/snespurify/phoenix/nall/Makefile b/snespurify/phoenix/nall/Makefile new file mode 100755 index 00000000..9a93bd23 --- /dev/null +++ b/snespurify/phoenix/nall/Makefile @@ -0,0 +1,109 @@ +# 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 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + ifeq ($(platform),win) + compiler := gcc + else ifeq ($(platform),osx) + compiler := gcc-mp-4.5 + else + compiler := gcc-4.5 + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/snespurify/phoenix/nall/algorithm.hpp b/snespurify/phoenix/nall/algorithm.hpp new file mode 100755 index 00000000..037f0bb7 --- /dev/null +++ b/snespurify/phoenix/nall/algorithm.hpp @@ -0,0 +1,17 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } +} + +#endif diff --git a/snespurify/phoenix/nall/any.hpp b/snespurify/phoenix/nall/any.hpp new file mode 100755 index 00000000..b31cff3c --- /dev/null +++ b/snespurify/phoenix/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/snespurify/phoenix/nall/array.hpp b/snespurify/phoenix/nall/array.hpp new file mode 100755 index 00000000..9cfe7758 --- /dev/null +++ b/snespurify/phoenix/nall/array.hpp @@ -0,0 +1,141 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void append(const T data) { + operator[](buffersize) = data; + } + + template void insert(unsigned index, const U list) { + unsigned listsize = container_size(list); + resize(buffersize + listsize); + memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T)); + foreach(item, list) pool[index++] = item; + } + + void insert(unsigned index, const T item) { + insert(index, array{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < buffersize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= buffersize) resize(index); //every element >= index was removed + else resize(buffersize - count); + } + + optional find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return { true, i }; + return { false, 0 }; + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //move + array& operator=(array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + array(array &&source) : pool(0), poolsize(0), buffersize(0) { + operator=(std::move(source)); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/nall/base64.hpp b/snespurify/phoenix/nall/base64.hpp new file mode 100755 index 00000000..e41c87b7 --- /dev/null +++ b/snespurify/phoenix/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/bit.hpp b/snespurify/phoenix/nall/bit.hpp new file mode 100755 index 00000000..169fc144 --- /dev/null +++ b/snespurify/phoenix/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/snespurify/phoenix/nall/concept.hpp b/snespurify/phoenix/nall/concept.hpp new file mode 100755 index 00000000..47167e21 --- /dev/null +++ b/snespurify/phoenix/nall/concept.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +#include +#include + +namespace nall { + //unsigned count() const; + template struct has_count { enum { value = false }; }; + + //unsigned length() const; + template struct has_length { enum { value = false }; }; + + //unsigned size() const; + template struct has_size { enum { value = false }; }; + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.count(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.length(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return object.size(); + } + + template unsigned container_size(const T& object, typename mp_enable_if>::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/snespurify/phoenix/nall/config.hpp b/snespurify/phoenix/nall/config.hpp new file mode 100755 index 00000000..f555158e --- /dev/null +++ b/snespurify/phoenix/nall/config.hpp @@ -0,0 +1,123 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: s.trim("\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0; + if(!qstrpos(line[i], " = ")) continue; + + lstring part; + part.qsplit(" = ", line[i]); + part[0].trim(); + part[1].trim(); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode::write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/crc32.hpp b/snespurify/phoenix/nall/crc32.hpp new file mode 100755 index 00000000..ad36fbf6 --- /dev/null +++ b/snespurify/phoenix/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + 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]); + } + return ~crc32; + } +} + +#endif diff --git a/snespurify/phoenix/nall/detect.hpp b/snespurify/phoenix/nall/detect.hpp new file mode 100755 index 00000000..b4991aaf --- /dev/null +++ b/snespurify/phoenix/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/snespurify/phoenix/nall/dictionary.hpp b/snespurify/phoenix/nall/dictionary.hpp new file mode 100755 index 00000000..dcb04151 --- /dev/null +++ b/snespurify/phoenix/nall/dictionary.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + if(auto pos = strpos(input, "}}")) { + string temp = substr(input, pos() + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + part[0].trim(); + part[1].trim(); + + //remove quotes + part[0].trim<1>("\""); + part[1].trim<1>("\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + dictionary& operator=(const dictionary&) = delete; + dictionary(const dictionary&) = delete; + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/directory.hpp b/snespurify/phoenix/nall/directory.hpp new file mode 100755 index 00000000..df0bf086 --- /dev/null +++ b/snespurify/phoenix/nall/directory.hpp @@ -0,0 +1,151 @@ +#ifndef NALL_DIRECTORY_HPP +#define NALL_DIRECTORY_HPP + +#include +#include +#include + +#if defined(_WIN32) + #include +#else + #include + #include + #include +#endif + +namespace nall { + +struct directory { + static bool exists(const string &pathname); + static lstring folders(const string &pathname, const string &pattern = "*"); + static lstring files(const string &pathname, const string &pattern = "*"); + static lstring contents(const string &pathname, const string &pattern = "*"); +}; + +#if defined(_WIN32) + inline bool directory::exists(const string &pathname) { + DWORD result = GetFileAttributes(utf16_t(pathname)); + if(result == INVALID_FILE_ATTRIBUTES) return false; + return (result & FILE_ATTRIBUTE_DIRECTORY); + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + while(FindNextFile(handle, &data) != false) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(string(name, "/")); + } + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + while(FindNextFile(handle, &data) != false) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = utf8_t(data.cFileName); + if(wildcard(name, pattern)) list.append(name); + } + } + FindClose(handle); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#else + inline bool directory::exists(const string &pathname) { + DIR *dp = opendir(pathname); + if(!dp) return false; + closedir(dp); + return true; + } + + inline lstring directory::folders(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if(ep->d_type & DT_DIR) { + if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/")); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + + } + + inline lstring directory::files(const string &pathname, const string &pattern) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if((ep->d_type & DT_DIR) == 0) { + if(wildcard(ep->d_name, pattern)) list.append(ep->d_name); + } + } + closedir(dp); + } + if(list.size() > 0) sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const string &pathname, const string &pattern) { + lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files + lstring files = directory::files(pathname, pattern); + foreach(file, files) folders.append(file); + return folders; + } +#endif + +} + +#endif diff --git a/snespurify/phoenix/nall/dl.hpp b/snespurify/phoenix/nall/dl.hpp new file mode 100755 index 00000000..6fa7603f --- /dev/null +++ b/snespurify/phoenix/nall/dl.hpp @@ -0,0 +1,96 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*, const char* = ""); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(filepath)); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*, const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/snespurify/phoenix/nall/endian.hpp b/snespurify/phoenix/nall/endian.hpp new file mode 100755 index 00000000..40d15633 --- /dev/null +++ b/snespurify/phoenix/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/snespurify/phoenix/nall/file.hpp b/snespurify/phoenix/nall/file.hpp new file mode 100755 index 00000000..103c7d4a --- /dev/null +++ b/snespurify/phoenix/nall/file.hpp @@ -0,0 +1,261 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + enum class index : unsigned { absolute, relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode::write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + template void print(Args... args) { + string data(args...); + const char *p = data; + while(*p) write(*p++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, index index_ = index::absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(index_) { + case index::absolute: req_offset = offset; break; + case index::relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode::read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, mode mode_) { + if(fp) return false; + + switch(file_mode = mode_) { + #if !defined(_WIN32) + case mode::read: fp = fopen(fn, "rb"); break; + case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode::readwrite: fp = fopen(fn, "rb+"); break; + case mode::writeread: fp = fopen(fn, "wb+"); break; + #else + case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode::read; + } + + ~file() { + 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]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + mode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode::read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/filemap.hpp b/snespurify/phoenix/nall/filemap.hpp new file mode 100755 index 00000000..52acb2fa --- /dev/null +++ b/snespurify/phoenix/nall/filemap.hpp @@ -0,0 +1,200 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + + bool opened() const { return p_opened(); } + bool open(const char *filename, mode mode_) { return p_open(filename, mode_); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* data() { return p_handle; } + const uint8_t* data() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + filemap(const char *filename, mode mode_) : p_size(0), p_handle(0) { p_ctor(); p_open(filename, mode_); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode_) { + default: return false; + case mode::read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode::write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_opened() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int open_flags, mmap_flags; + + switch(mode_) { + default: return false; + case mode::read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode::write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode::readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode::writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/snespurify/phoenix/nall/foreach.hpp b/snespurify/phoenix/nall/foreach.hpp new file mode 100755 index 00000000..00a039f3 --- /dev/null +++ b/snespurify/phoenix/nall/foreach.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#include +#include + +#undef foreach +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) + +#endif diff --git a/snespurify/phoenix/nall/function.hpp b/snespurify/phoenix/nall/function.hpp new file mode 100755 index 00000000..645991fb --- /dev/null +++ b/snespurify/phoenix/nall/function.hpp @@ -0,0 +1,60 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +namespace nall { + template class function; + + template class function { + struct container { + virtual R operator()(P... p) const = 0; + virtual container* copy() const = 0; + virtual ~container() {} + } *callback; + + struct global : container { + R (*function)(P...); + R operator()(P... p) const { return function(std::forward

(p)...); } + container* copy() const { return new global(function); } + global(R (*function)(P...)) : function(function) {} + }; + + template struct member : container { + R (C::*function)(P...); + C *object; + R operator()(P... p) const { return (object->*function)(std::forward

(p)...); } + container* copy() const { return new member(function, object); } + member(R (C::*function)(P...), C *object) : function(function), object(object) {} + }; + + template struct lambda : container { + L object; + R operator()(P... p) const { return object(std::forward

(p)...); } + container* copy() const { return new lambda(object); } + lambda(const L& object) : object(object) {} + }; + + public: + operator bool() const { return callback; } + R operator()(P... p) const { return (*callback)(std::forward

(p)...); } + void reset() { if(callback) { delete callback; callback = 0; } } + + function& operator=(const function &source) { + if(this != &source) { + if(callback) { delete callback; callback = 0; } + if(source.callback) callback = source.callback->copy(); + } + return *this; + } + + function(const function &source) { operator=(source); } + function() : callback(0) {} + function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); } + function(R (*function)(P...)) { callback = new global(function); } + template function(R (C::*function)(P...), C *object) { callback = new member(function, object); } + template function(R (C::*function)(P...) const, C *object) { callback = new member((R (C::*)(P...))function, object); } + template function(const L& object) { callback = new lambda(object); } + ~function() { if(callback) delete callback; } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/input.hpp b/snespurify/phoenix/nall/input.hpp new file mode 100755 index 00000000..28b10453 --- /dev/null +++ b/snespurify/phoenix/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "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", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + 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, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + s.ltrim("KB"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + s.ltrim("MS"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + s.ltrim("JP"); + unsigned id = strunsigned(s); + auto pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/snespurify/phoenix/nall/lzss.hpp b/snespurify/phoenix/nall/lzss.hpp new file mode 100755 index 00000000..202bc814 --- /dev/null +++ b/snespurify/phoenix/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/moduloarray.hpp b/snespurify/phoenix/nall/moduloarray.hpp new file mode 100755 index 00000000..be549ae9 --- /dev/null +++ b/snespurify/phoenix/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/platform.hpp b/snespurify/phoenix/nall/platform.hpp new file mode 100755 index 00000000..72eeec09 --- /dev/null +++ b/snespurify/phoenix/nall/platform.hpp @@ -0,0 +1,122 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface + #define dllexport __declspec(dllexport) +#else + #include + #include + #include + #define dllexport +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +//========================= +//file system functionality +//========================= + +#if defined(_WIN32) + inline char* realpath(const char *filename, char *resolvedname) { + wchar_t fn[_MAX_PATH] = L""; + _wfullpath(fn, nall::utf16_t(filename), _MAX_PATH); + strcpy(resolvedname, nall::utf8_t(fn)); + return resolvedname; + } + + inline char* userpath(char *path) { + wchar_t fp[_MAX_PATH] = L""; + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp); + strcpy(path, nall::utf8_t(fp)); + return path; + } + + inline char* getcwd(char *path) { + wchar_t fp[_MAX_PATH] = L""; + _wgetcwd(fp, _MAX_PATH); + strcpy(path, nall::utf8_t(fp)); + return path; + } +#else + //realpath() already exists + + inline char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; + } + + inline char *getcwd(char *path) { + return getcwd(path, PATH_MAX); + } +#endif + +#endif + diff --git a/snespurify/phoenix/nall/priorityqueue.hpp b/snespurify/phoenix/nall/priorityqueue.hpp new file mode 100755 index 00000000..7104e791 --- /dev/null +++ b/snespurify/phoenix/nall/priorityqueue.hpp @@ -0,0 +1,109 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/snespurify/phoenix/nall/property.hpp b/snespurify/phoenix/nall/property.hpp new file mode 100755 index 00000000..6fd33acd --- /dev/null +++ b/snespurify/phoenix/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/random.hpp b/snespurify/phoenix/nall/random.hpp new file mode 100755 index 00000000..74ebc2d2 --- /dev/null +++ b/snespurify/phoenix/nall/random.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_RANDOM_HPP +#define NALL_RANDOM_HPP + +namespace nall { + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } + + struct random_cyclic { + unsigned seed; + inline unsigned operator()() { + return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320); + } + random_cyclic() : seed(0) {} + }; +} + +#endif diff --git a/snespurify/phoenix/nall/serial.hpp b/snespurify/phoenix/nall/serial.hpp new file mode 100755 index 00000000..9ac8451a --- /dev/null +++ b/snespurify/phoenix/nall/serial.hpp @@ -0,0 +1,85 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate, bool flowcontrol) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL); + attr.c_cflag |= (CS8 | CREAD); + if(flowcontrol == false) { + attr.c_cflag &= ~CRTSCTS; + } else { + attr.c_cflag |= CRTSCTS; + } + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/snespurify/phoenix/nall/serializer.hpp b/snespurify/phoenix/nall/serializer.hpp new file mode 100755 index 00000000..ff2337ab --- /dev/null +++ b/snespurify/phoenix/nall/serializer.hpp @@ -0,0 +1,146 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //move + serializer& operator=(serializer &&s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = s.idata; + isize = s.isize; + icapacity = s.icapacity; + + s.idata = 0; + return *this; + } + + serializer(serializer &&s) { + operator=(std::move(s)); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + icapacity = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/snespurify/phoenix/nall/sha256.hpp b/snespurify/phoenix/nall/sha256.hpp new file mode 100755 index 00000000..7f41f04e --- /dev/null +++ b/snespurify/phoenix/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +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; + }; + + 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; + } + + 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); + } + } + + 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); + } + + 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/snespurify/phoenix/nall/snes/info.hpp b/snespurify/phoenix/nall/snes/info.hpp new file mode 100755 index 00000000..67db4748 --- /dev/null +++ b/snespurify/phoenix/nall/snes/info.hpp @@ -0,0 +1,864 @@ +#ifndef NALL_SNES_INFO_HPP +#define NALL_SNES_INFO_HPP + +namespace nall { + +class snes_information { +public: + string xml_memory_map; + + inline snes_information(const uint8_t *data, unsigned size); + +//private: + inline void read_header(const uint8_t *data, unsigned size); + inline unsigned find_header(const uint8_t *data, unsigned size); + inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); + inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); + inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameBoy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + bool loaded; //is a base cartridge inserted? + unsigned crc32; //crc32 of all cartridges (base+slot(s)) + unsigned rom_size; + unsigned ram_size; + + Mode mode; + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + + bool has_bsx_slot; + bool has_superfx; + bool has_sa1; + bool has_srtc; + bool has_sdd1; + bool has_spc7110; + bool has_spc7110rtc; + bool has_cx4; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +snes_information::snes_information(const uint8_t *data, unsigned size) { + read_header(data, size); + + string xml = "\n"; + + if(type == TypeBsx) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeSufamiTurbo) { + xml << ""; + xml_memory_map = xml; + return; + } + + if(type == TypeGameBoy) { + xml << "\n"; + if(gameboy_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xml_memory_map = xml; + return; + } + + xml << "\n"; + + if(type == TypeSuperGameBoy1Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(type == TypeSuperGameBoy2Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(has_spc7110) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + if(has_spc7110rtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == LoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + xml << " \n"; + } else { + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == ExLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + } else if(mapper == ExHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == SuperFXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == SA1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == STROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_srtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_sdd1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_cx4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp1) { + xml << " \n"; + if(dsp1_mapper == DSP1LoROM1MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1LoROM2MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + + if(has_dsp2) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp3) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_obc1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st010) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st011) { + //ST-0011 addresses not verified; chip is unsupported + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st018) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + xml << "\n"; + xml_memory_map = xml; +} + +void snes_information::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + const unsigned index = find_header(data, size); + const uint8_t mapperid = data[index + Mapper]; + const uint8_t rom_type = data[index + RomType]; + const uint8_t rom_size = data[index + RomSize]; + const uint8_t company = data[index + Company]; + const uint8_t regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 13 = NTSC; 2 - 12 = PAL + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8_t n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; + } else if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned snes_information::find_header(const uint8_t *data, unsigned size) { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned snes_information::score_header(const uint8_t *data, unsigned size, unsigned addr) { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +unsigned snes_information::gameboy_ram_size(const uint8_t *data, unsigned size) { + if(size < 512) return 0; + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 8 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool snes_information::gameboy_has_rtc(const uint8_t *data, unsigned size) { + if(size < 512) return false; + if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true; + return false; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/sort.hpp b/snespurify/phoenix/nall/sort.hpp new file mode 100755 index 00000000..23c317a5 --- /dev/null +++ b/snespurify/phoenix/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/snespurify/phoenix/nall/static.hpp b/snespurify/phoenix/nall/static.hpp new file mode 100755 index 00000000..4acb9fd0 --- /dev/null +++ b/snespurify/phoenix/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/snespurify/phoenix/nall/stdint.hpp b/snespurify/phoenix/nall/stdint.hpp new file mode 100755 index 00000000..d8b6c788 --- /dev/null +++ b/snespurify/phoenix/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + + static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); +} + +#endif diff --git a/snespurify/phoenix/nall/string.hpp b/snespurify/phoenix/nall/string.hpp new file mode 100755 index 00000000..9acc2e9d --- /dev/null +++ b/snespurify/phoenix/nall/string.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/nall/string/base.hpp b/snespurify/phoenix/nall/string/base.hpp new file mode 100755 index 00000000..77f15e17 --- /dev/null +++ b/snespurify/phoenix/nall/string/base.hpp @@ -0,0 +1,159 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + inline void reserve(unsigned); + + inline string& assign(const char*); + inline string& append(const char*); + inline string& append(bool); + inline string& append(signed int value); + inline string& append(unsigned int value); + inline string& append(double value); + + inline bool readfile(const char*); + + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + inline unsigned length() const; + + inline bool equals(const char*) const; + inline bool iequals(const char*) const; + + inline bool wildcard(const char*) const; + inline bool iwildcard(const char*) const; + + inline bool beginswith(const char*) const; + inline bool ibeginswith(const char*) const; + inline bool endswith(const char*) const; + inline bool iendswith(const char*) const; + + inline string& lower(); + inline string& upper(); + inline string& transform(const char *before, const char *after); + + template inline string& ltrim(const char *key = " "); + template inline string& rtrim(const char *key = " "); + template inline string& trim (const char *key = " "); + + inline optional position(const char *key) const; + inline optional qposition(const char *key) const; + + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string& operator=(const string&); + inline string& operator=(string&&); + + template inline string(Args&&... args); + inline string(const string&); + inline string(string&&); + inline ~string(); + + protected: + char *data; + unsigned size; + + #if defined(QSTRING_H) + public: + inline operator QString() const; + #endif + }; + + class lstring : public linear_vector { + public: + template inline lstring& operator<<(T value); + + inline optional find(const char*) const; + template inline void split (const char*, const char*); + template inline void qsplit(const char*, const char*); + + lstring(); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *str1, const char *str2); + inline bool wildcard(const char *str, const char *pattern); + inline bool iwildcard(const char *str, const char *pattern); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t strhex (const char *str); + inline intmax_t strsigned (const char *str); + inline uintmax_t strunsigned(const char *str); + inline uintmax_t strbin (const char *str); + inline double strdouble (const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //platform.hpp + inline string realpath(const char *name); + inline string userpath(); + inline string currentpath(); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //strpos.hpp + inline optional strpos(const char *str, const char *key); + inline optional qstrpos(const char *str, const char *key); + + //trim.hpp + template inline char* ltrim(char *str, const char *key = " "); + template inline char* rtrim(char *str, const char *key = " "); + template inline char* trim (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + template inline string strhex(uintmax_t value); + template inline string strsigned(intmax_t value); + template inline string strunsigned(uintmax_t value); + template inline string strbin(uintmax_t value); + inline unsigned strdouble(char *str, double value); + inline string strdouble(double value); + + //variadic.hpp + template inline void print(Args&&... args); +}; + +#endif diff --git a/snespurify/phoenix/nall/string/bsv.hpp b/snespurify/phoenix/nall/string/bsv.hpp new file mode 100755 index 00000000..d4b919e0 --- /dev/null +++ b/snespurify/phoenix/nall/string/bsv.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_STRING_BSV_HPP +#define NALL_STRING_BSV_HPP + +//BSV parser +//version 0.01 + +namespace nall { + +inline string bsv_decode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '}' ) return ""; + if(*input == '\r') return ""; + if(*input == '\n') return ""; + + //normal characters + if(*input != '{') { output[offset++] = *input++; continue; } + + //entities + if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; } + if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; } + if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; } + + //illegal entities + return ""; + } + output[offset] = 0; + return output; +} + +inline string bsv_encode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '\r') return ""; + + if(*input == '\n') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'f'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '{') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '}') { + output[offset++] = '{'; + output[offset++] = 'r'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + output[offset++] = *input++; + } + output[offset] = 0; + return output; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/cast.hpp b/snespurify/phoenix/nall/string/cast.hpp new file mode 100755 index 00000000..5b17c408 --- /dev/null +++ b/snespurify/phoenix/nall/string/cast.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (bool v) { return v ? "true" : "false"; } +template<> inline string to_string (signed int v) { return strsigned(v); } +template<> inline string to_string (unsigned int v) { return strunsigned(v); } +template<> inline string to_string (double v) { return strdouble(v); } +template<> inline string to_string (char *v) { return v; } +template<> inline string to_string (const char *v) { return v; } +template<> inline string to_string (string v) { return v; } +template<> inline string to_string(const string &v) { return v; } + +template string& string::operator= (T value) { return assign(to_string(value)); } +template string& string::operator<<(T value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QSTRING_H) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/snespurify/phoenix/nall/string/compare.hpp b/snespurify/phoenix/nall/string/compare.hpp new file mode 100755 index 00000000..bce0895b --- /dev/null +++ b/snespurify/phoenix/nall/string/compare.hpp @@ -0,0 +1,110 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_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 stricmp(const char *str1, const char *str2) { + while(*str1) { + if(chrlower(*str1) != chrlower(*str2)) break; + str1++, str2++; + } + return (int)chrlower(*str1) - (int)chrlower(*str2); +} + +bool wildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && *s != *p) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || *p == *s) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool iwildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && chrlower(*s) != chrlower(*p)) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || chrlower(*p) == chrlower(*s)) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + 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) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + 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/snespurify/phoenix/nall/string/convert.hpp b/snespurify/phoenix/nall/string/convert.hpp new file mode 100755 index 00000000..12a6c1ff --- /dev/null +++ b/snespurify/phoenix/nall/string/convert.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* 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; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.' || x == ',') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/core.hpp b/snespurify/phoenix/nall/string/core.hpp new file mode 100755 index 00000000..bad29030 --- /dev/null +++ b/snespurify/phoenix/nall/string/core.hpp @@ -0,0 +1,139 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string& string::append(bool value) { append(value ? "true" : "false"); return *this; } +string& string::append(signed int value) { append(strsigned(value)); return *this; } +string& string::append(unsigned int value) { append(strunsigned(value)); return *this; } +string& string::append(double value) { append(strdouble(value)); return *this; } + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string& string::operator=(string &&source) { + if(data) free(data); + size = source.size; + data = source.data; + source.data = 0; + source.size = 0; + return *this; +} + +static void istring(string &output) { +} + +template +static void istring(string &output, const T &value, Args&&... args) { + output.append(value); + istring(output, std::forward(args)...); +} + +template string::string(Args&&... args) { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; + istring(*this, std::forward(args)...); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::string(string &&source) { + size = source.size; + data = source.data; + source.data = 0; +} + +string::~string() { + if(data) free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +optional lstring::find(const char *key) const { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return { true, i }; + } + return { false, 0 }; +} + +inline lstring::lstring() { +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/filename.hpp b/snespurify/phoenix/nall/string/filename.hpp new file mode 100755 index 00000000..93d605ae --- /dev/null +++ b/snespurify/phoenix/nall/string/filename.hpp @@ -0,0 +1,63 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/" +// "foo/" -> "foo/" +// "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/math.hpp b/snespurify/phoenix/nall/string/math.hpp new file mode 100755 index 00000000..ea8b99c8 --- /dev/null +++ b/snespurify/phoenix/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/platform.hpp b/snespurify/phoenix/nall/string/platform.hpp new file mode 100755 index 00000000..42c1a756 --- /dev/null +++ b/snespurify/phoenix/nall/string/platform.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_PLATFORM_HPP +#define NALL_STRING_PLATFORM_HPP + +namespace nall { + +string realpath(const char *name) { + char path[PATH_MAX]; + if(::realpath(name, path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string userpath() { + char path[PATH_MAX]; + if(::userpath(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +string currentpath() { + char path[PATH_MAX]; + if(::getcwd(path)) { + string result(path); + result.transform("\\", "/"); + if(result.endswith("/") == false) result.append("/"); + return result; + } + return ""; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/replace.hpp b/snespurify/phoenix/nall/string/replace.hpp new file mode 100755 index 00000000..db405a9b --- /dev/null +++ b/snespurify/phoenix/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/snespurify/phoenix/nall/string/split.hpp b/snespurify/phoenix/nall/string/split.hpp new file mode 100755 index 00000000..8d3ca877 --- /dev/null +++ b/snespurify/phoenix/nall/string/split.hpp @@ -0,0 +1,58 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +template void lstring::split(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +template void lstring::qsplit(const char *key, const char *src) { + unsigned limit = Limit; + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/snespurify/phoenix/nall/string/strl.hpp b/snespurify/phoenix/nall/string/strl.hpp new file mode 100755 index 00000000..84c841fa --- /dev/null +++ b/snespurify/phoenix/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/strpos.hpp b/snespurify/phoenix/nall/string/strpos.hpp new file mode 100755 index 00000000..1907a2f3 --- /dev/null +++ b/snespurify/phoenix/nall/string/strpos.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_STRING_STRPOS_HPP +#define NALL_STRING_STRPOS_HPP + +//usage example: +//if(auto pos = strpos(str, key)) print(pos(), "\n"); +//prints position of key within str, only if it is found + +namespace nall { + +optional strpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) return { true, i }; + } + + return { false, 0 }; +} + +optional qstrpos(const char *str, const char *key) { + unsigned ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl) return { false, 0 }; + + for(unsigned i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) return { true, i }; + i++; + } + + return { false, 0 }; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/trim.hpp b/snespurify/phoenix/nall/string/trim.hpp new file mode 100755 index 00000000..f5355d7d --- /dev/null +++ b/snespurify/phoenix/nall/string/trim.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +//limit defaults to zero, which will underflow on first compare; equivalent to no limit +template char* ltrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + if(--limit == 0) break; + } + return str; +} + +template char* rtrim(char *str, const char *key) { + unsigned limit = Limit; + if(!key || !*key) return str; + while(strend(str, key)) { + str[strlen(str) - strlen(key)] = 0; + if(--limit == 0) break; + } + return str; +} + +template char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/utility.hpp b/snespurify/phoenix/nall/string/utility.hpp new file mode 100755 index 00000000..d2bad881 --- /dev/null +++ b/snespurify/phoenix/nall/string/utility.hpp @@ -0,0 +1,157 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +namespace nall { + +unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* arithmetic <> string */ + +template string strhex(uintmax_t value) { + string output; + unsigned offset = 0; + + //render string backwards, as we do not know its length yet + do { + unsigned n = value & 15; + output[offset++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + //reverse the string in-place + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strsigned(intmax_t value) { + string output; + unsigned offset = 0; + + bool negative = value < 0; + if(negative) value = abs(value); + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + if(negative) output[offset++] = '-'; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strunsigned(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strbin(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value & 1; + output[offset++] = '0' + n; + value >>= 1; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +//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 strdouble(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string strdouble(double value) { + string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/variadic.hpp b/snespurify/phoenix/nall/string/variadic.hpp new file mode 100755 index 00000000..6c027fc8 --- /dev/null +++ b/snespurify/phoenix/nall/string/variadic.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +template inline void print(Args&&... args) { + printf("%s", (const char*)string(std::forward(args)...)); +} + +} + +#endif diff --git a/snespurify/phoenix/nall/string/wrapper.hpp b/snespurify/phoenix/nall/string/wrapper.hpp new file mode 100755 index 00000000..eadf0a10 --- /dev/null +++ b/snespurify/phoenix/nall/string/wrapper.hpp @@ -0,0 +1,33 @@ +#ifndef NALL_STRING_WRAPPER_HPP +#define NALL_STRING_WRAPPER_HPP + +namespace nall { + +unsigned string::length() const { return strlen(data); } + +bool string::equals(const char *str) const { return !strcmp(data, str); } +bool string::iequals(const char *str) const { return !stricmp(data, str); } + +bool string::wildcard(const char *str) const { return nall::wildcard(data, str); } +bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); } + +bool string::beginswith(const char *str) const { return strbegin(data, str); } +bool string::ibeginswith(const char *str) const { return stribegin(data, str); } + +bool string::endswith(const char *str) const { return strend(data, str); } +bool string::iendswith(const char *str) const { return striend(data, str); } + +string& string::lower() { nall::strlower(data); return *this; } +string& string::upper() { nall::strupper(data); return *this; } +string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; } + +template string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; } +template string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; } +template string& string::trim (const char *key) { nall::trim (data, key); return *this; } + +optional string::position(const char *key) const { return strpos(data, key); } +optional string::qposition(const char *key) const { return qstrpos(data, key); } + +} + +#endif diff --git a/snespurify/phoenix/nall/string/xml.hpp b/snespurify/phoenix/nall/string/xml.hpp new file mode 100755 index 00000000..185a89f9 --- /dev/null +++ b/snespurify/phoenix/nall/string/xml.hpp @@ -0,0 +1,266 @@ +#ifndef NALL_STRING_XML_HPP +#define NALL_STRING_XML_HPP + +//XML subset parser +//version 0.05 + +namespace nall { + +struct xml_attribute { + string name; + string content; + virtual string parse() const; +}; + +struct xml_element : xml_attribute { + string parse() const; + linear_vector attribute; + linear_vector element; + +protected: + void parse_doctype(const char *&data); + bool parse_head(string data); + bool parse_body(const char *&data); + friend xml_element xml_parse(const char *data); +}; + +inline string xml_attribute::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline string xml_element::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + if(strbegin(source, "")) { + source += pos() + 3; + continue; + } else { + return ""; + } + } + + if(strbegin(source, "")) { + if(pos() - 9 > 0) { + string cdata = substr(source, 9, pos() - 9); + data << cdata; + offset += strlen(cdata); + } + source += 9 + offset + 3; + continue; + } else { + return ""; + } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline void xml_element::parse_doctype(const char *&data) { + name = "!DOCTYPE"; + const char *content_begin = data; + + signed counter = 0; + while(*data) { + char value = *data++; + if(value == '<') counter++; + if(value == '>') counter--; + if(counter < 0) { + content = substr(content_begin, 0, data - content_begin - 1); + return; + } + } + throw "..."; +} + +inline bool xml_element::parse_head(string data) { + data.qreplace("\t", " "); + data.qreplace("\r", " "); + data.qreplace("\n", " "); + while(qstrpos(data, " ")) data.qreplace(" ", " "); + data.qreplace(" =", "="); + data.qreplace("= ", "="); + data.rtrim(); + + lstring part; + part.qsplit(" ", data); + + name = part[0]; + if(name == "") throw "..."; + + for(unsigned i = 1; i < part.size(); i++) { + lstring side; + side.qsplit("=", part[i]); + if(side.size() != 2) throw "..."; + + xml_attribute attr; + attr.name = side[0]; + attr.content = side[1]; + if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\""); + else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'"); + else throw "..."; + attribute.append(attr); + } +} + +inline bool xml_element::parse_body(const char *&data) { + while(true) { + if(!*data) return false; + if(*data++ != '<') continue; + if(*data == '/') return false; + + if(strbegin(data, "!DOCTYPE") == true) { + parse_doctype(data); + return true; + } + + if(strbegin(data, "!--")) { + if(auto offset = strpos(data, "-->")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + if(strbegin(data, "![CDATA[")) { + if(auto offset = strpos(data, "]]>")) { + data += offset() + 3; + continue; + } else { + throw "..."; + } + } + + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + string tag = substr(data, 0, offset()); + data += offset() + 1; + const char *content_begin = data; + + bool self_terminating = false; + + if(strend(tag, "?") == true) { + self_terminating = true; + tag.rtrim<1>("?"); + } else if(strend(tag, "/") == true) { + self_terminating = true; + tag.rtrim<1>("/"); + } + + parse_head(tag); + if(self_terminating) return true; + + while(*data) { + unsigned index = element.size(); + xml_element node; + if(node.parse_body(data) == false) { + if(*data == '/') { + signed length = data - content_begin - 1; + if(length > 0) content = substr(content_begin, 0, length); + + data++; + auto offset = strpos(data, ">"); + if(!offset) throw "..."; + + tag = substr(data, 0, offset()); + data += offset() + 1; + + tag.replace("\t", " "); + tag.replace("\r", " "); + tag.replace("\n", " "); + while(strpos(tag, " ")) tag.replace(" ", " "); + tag.rtrim(); + + if(name != tag) throw "..."; + return true; + } + } else { + element.append(node); + } + } + } +} + +//ensure there is only one root element +inline bool xml_validate(xml_element &document) { + unsigned root_counter = 0; + + for(unsigned i = 0; i < document.element.size(); i++) { + string &name = document.element[i].name; + if(strbegin(name, "?")) continue; + if(strbegin(name, "!")) continue; + if(++root_counter > 1) return false; + } + + return true; +} + +inline xml_element xml_parse(const char *data) { + xml_element self; + + try { + while(*data) { + xml_element node; + if(node.parse_body(data) == false) { + break; + } else { + self.element.append(node); + } + } + + if(xml_validate(self) == false) throw "..."; + return self; + } catch(const char*) { + xml_element empty; + return empty; + } +} + +} + +#endif diff --git a/snespurify/phoenix/nall/ups.hpp b/snespurify/phoenix/nall/ups.hpp new file mode 100755 index 00000000..ffcdb2d7 --- /dev/null +++ b/snespurify/phoenix/nall/ups.hpp @@ -0,0 +1,223 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include +#include +#include +#include + +namespace nall { + +struct ups { + enum class result : unsigned { + unknown, + success, + patch_unwritable, + patch_invalid, + source_invalid, + target_invalid, + target_too_small, + patch_checksum_invalid, + source_checksum_invalid, + target_checksum_invalid, + }; + + function progress; + + result create( + const uint8_t *sourcedata, unsigned sourcelength, + const uint8_t *targetdata, unsigned targetlength, + const char *patchfilename + ) { + source_data = (uint8_t*)sourcedata, target_data = (uint8_t*)targetdata; + source_length = sourcelength, target_length = targetlength; + source_offset = target_offset = 0; + source_checksum = target_checksum = patch_checksum = ~0; + + if(patch_file.open(patchfilename, file::mode::write) == false) return result::patch_unwritable; + + patch_write('U'); + patch_write('P'); + patch_write('S'); + patch_write('1'); + encode(source_length); + encode(target_length); + + unsigned output_length = source_length > target_length ? source_length : target_length; + unsigned relative = 0; + for(unsigned offset = 0; offset < output_length;) { + uint8_t x = source_read(); + uint8_t y = target_read(); + + if(x == y) { + offset++; + continue; + } + + encode(offset++ - relative); + patch_write(x ^ y); + + while(true) { + if(offset >= output_length) { + patch_write(0x00); + break; + } + + x = source_read(); + y = target_read(); + offset++; + patch_write(x ^ y); + if(x == y) break; + } + + relative = offset; + } + + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(source_checksum >> (i * 8)); + for(unsigned i = 0; i < 4; i++) patch_write(target_checksum >> (i * 8)); + uint32_t patch_result_checksum = ~patch_checksum; + for(unsigned i = 0; i < 4; i++) patch_write(patch_result_checksum >> (i * 8)); + + patch_file.close(); + return result::success; + } + + result apply( + const uint8_t *patchdata, unsigned patchlength, + const uint8_t *sourcedata, unsigned sourcelength, + uint8_t *targetdata, unsigned &targetlength + ) { + patch_data = (uint8_t*)patchdata, source_data = (uint8_t*)sourcedata, target_data = targetdata; + patch_length = patchlength, source_length = sourcelength, target_length = targetlength; + patch_offset = source_offset = target_offset = 0; + patch_checksum = source_checksum = target_checksum = ~0; + + if(patch_length < 18) return result::patch_invalid; + if(patch_read() != 'U') return result::patch_invalid; + if(patch_read() != 'P') return result::patch_invalid; + if(patch_read() != 'S') return result::patch_invalid; + if(patch_read() != '1') return result::patch_invalid; + + unsigned source_read_length = decode(); + unsigned target_read_length = decode(); + + if(source_length != source_read_length && source_length != target_read_length) return result::source_invalid; + targetlength = (source_length == source_read_length ? target_read_length : source_read_length); + if(target_length < targetlength) return result::target_too_small; + target_length = targetlength; + + while(patch_offset < patch_length - 12) { + unsigned length = decode(); + while(length--) target_write(source_read()); + while(true) { + uint8_t patch_xor = patch_read(); + target_write(patch_xor ^ source_read()); + if(patch_xor == 0) break; + } + } + while(source_offset < source_length) target_write(source_read()); + while(target_offset < target_length) target_write(source_read()); + + uint32_t patch_read_checksum = 0, source_read_checksum = 0, target_read_checksum = 0; + for(unsigned i = 0; i < 4; i++) source_read_checksum |= patch_read() << (i * 8); + for(unsigned i = 0; i < 4; i++) target_read_checksum |= patch_read() << (i * 8); + uint32_t patch_result_checksum = ~patch_checksum; + source_checksum = ~source_checksum; + target_checksum = ~target_checksum; + for(unsigned i = 0; i < 4; i++) patch_read_checksum |= patch_read() << (i * 8); + + if(patch_result_checksum != patch_read_checksum) return result::patch_invalid; + if(source_checksum == source_read_checksum && source_length == source_read_length) { + if(target_checksum == target_read_checksum && target_length == target_read_length) return result::success; + return result::target_invalid; + } else if(source_checksum == target_read_checksum && source_length == target_read_length) { + if(target_checksum == source_read_checksum && target_length == source_read_length) return result::success; + return result::target_invalid; + } else { + return result::source_invalid; + } + } + +private: + uint8_t *patch_data, *source_data, *target_data; + unsigned patch_length, source_length, target_length; + unsigned patch_offset, source_offset, target_offset; + unsigned patch_checksum, source_checksum, target_checksum; + file patch_file; + + uint8_t patch_read() { + if(patch_offset < patch_length) { + uint8_t n = patch_data[patch_offset++]; + patch_checksum = crc32_adjust(patch_checksum, n); + return n; + } + return 0x00; + } + + uint8_t source_read() { + if(source_offset < source_length) { + uint8_t n = source_data[source_offset++]; + source_checksum = crc32_adjust(source_checksum, n); + return n; + } + return 0x00; + } + + uint8_t target_read() { + uint8_t result = 0x00; + if(target_offset < target_length) { + result = target_data[target_offset]; + target_checksum = crc32_adjust(target_checksum, result); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + return result; + } + + void patch_write(uint8_t n) { + patch_file.write(n); + patch_checksum = crc32_adjust(patch_checksum, n); + } + + void target_write(uint8_t n) { + if(target_offset < target_length) { + target_data[target_offset] = n; + target_checksum = crc32_adjust(target_checksum, n); + } + if(((target_offset++ & 255) == 0) && progress) { + progress(target_offset, source_length > target_length ? source_length : target_length); + } + } + + void encode(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + patch_write(0x80 | x); + break; + } + patch_write(x); + offset--; + } + } + + uint64_t decode() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = patch_read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } +}; + +} + +#endif diff --git a/snespurify/phoenix/nall/utf8.hpp b/snespurify/phoenix/nall/utf8.hpp new file mode 100755 index 00000000..f5597b85 --- /dev/null +++ b/snespurify/phoenix/nall/utf8.hpp @@ -0,0 +1,86 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef UNICODE +#undef _WIN32_WINNT +#undef NOMINMAX +#define UNICODE +#define _WIN32_WINNT 0x0501 +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + utf8_t(const utf8_t&) = delete; + utf8_t& operator=(const utf8_t&) = delete; + + private: + char *buffer; + }; + + inline void utf8_args(int &argc, char **&argv) { + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[_MAX_PATH]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } + } +} + +#endif //if defined(_WIN32) + +#endif diff --git a/snespurify/phoenix/nall/utility.hpp b/snespurify/phoenix/nall/utility.hpp new file mode 100755 index 00000000..60bda562 --- /dev/null +++ b/snespurify/phoenix/nall/utility.hpp @@ -0,0 +1,39 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T &x, T &y) { + T temp(std::move(x)); + x = std::move(y); + y = std::move(temp); + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template class optional { + bool valid; + T value; + public: + inline operator bool() const { return valid; } + inline const T& operator()() const { if(!valid) throw; return value; } + inline optional(bool valid, const T &value) : valid(valid), value(value) {} + }; + + template inline T* allocate(unsigned size, const T &value) { + T *array = new T[size]; + for(unsigned i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/snespurify/phoenix/nall/varint.hpp b/snespurify/phoenix/nall/varint.hpp new file mode 100755 index 00000000..fe4732b1 --- /dev/null +++ b/snespurify/phoenix/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert(!std::is_same::value, ""); + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/snespurify/phoenix/nall/vector.hpp b/snespurify/phoenix/nall/vector.hpp new file mode 100755 index 00000000..543c7b69 --- /dev/null +++ b/snespurify/phoenix/nall/vector.hpp @@ -0,0 +1,281 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + template void insert(unsigned index, const U list) { + linear_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, linear_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline linear_vector& operator=(linear_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + linear_vector(linear_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~linear_vector() { + reset(); + } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + template void insert(unsigned index, const U list) { + pointer_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(*pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, pointer_vector{ item }); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + *pool[i] = *pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline pointer_vector& operator=(pointer_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + pointer_vector(pointer_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~pointer_vector() { + reset(); + } + }; + + template struct has_size> { enum { value = true }; }; + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snespurify/phoenix/phoenix.cpp b/snespurify/phoenix/phoenix.cpp new file mode 100755 index 00000000..bf51451b --- /dev/null +++ b/snespurify/phoenix/phoenix.cpp @@ -0,0 +1,17 @@ +#if defined(PHOENIX_WINDOWS) + #define UNICODE + #define WINVER 0x0501 + #define _WIN32_WINNT 0x0501 + #define _WIN32_IE 0x0600 + #define NOMINMAX +#endif + +#include "phoenix.hpp" + +#if defined(PHOENIX_WINDOWS) + #include "windows/windows.cpp" +#elif defined(PHOENIX_GTK) + #include "gtk/gtk.cpp" +#elif defined(PHOENIX_QT) + #include "qt/qt.cpp" +#endif diff --git a/snespurify/phoenix/phoenix.hpp b/snespurify/phoenix/phoenix.hpp new file mode 100755 index 00000000..99c82d15 --- /dev/null +++ b/snespurify/phoenix/phoenix.hpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include + +#if defined(PHOENIX_WINDOWS) + #include "windows/windows.hpp" +#elif defined(PHOENIX_GTK) + #include "gtk/gtk.hpp" +#elif defined(PHOENIX_QT) + #include "qt/qt.hpp" +#endif diff --git a/snespurify/phoenix/qt/button.cpp b/snespurify/phoenix/qt/button.cpp new file mode 100755 index 00000000..e11a14b5 --- /dev/null +++ b/snespurify/phoenix/qt/button.cpp @@ -0,0 +1,13 @@ +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + button->setParent(parent.window->container); + button->setGeometry(x, y, width, height); + button->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) button->setFont(*parent.window->defaultFont); + button->show(); + button->connect(button, SIGNAL(released()), SLOT(onTick())); +} + +Button::Button() { + button = new Button::Data(*this); + widget->widget = button; +} diff --git a/snespurify/phoenix/qt/canvas.cpp b/snespurify/phoenix/qt/canvas.cpp new file mode 100755 index 00000000..a0887604 --- /dev/null +++ b/snespurify/phoenix/qt/canvas.cpp @@ -0,0 +1,39 @@ +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->image = new QImage(width, height, QImage::Format_RGB32); + canvas->image->fill(0); + canvas->setParent(parent.window->container); + canvas->setGeometry(x, y, width, height); + canvas->show(); +} + +void Canvas::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + delete canvas->image; + canvas->image = new QImage(width, height, QImage::Format_RGB32); + canvas->image->fill(0); + canvas->setGeometry(x, y, width, height); + canvas->update(); +} + +uint32_t* Canvas::buffer() { + return (uint32_t*)canvas->image->bits(); +} + +void Canvas::redraw() { + canvas->update(); +} + +Canvas::Canvas() { + canvas = new Canvas::Data(*this); + canvas->image = 0; + widget->widget = canvas; +} + +Canvas::~Canvas() { + if(canvas->image) delete canvas->image; + delete canvas; +} + +void Canvas::Data::paintEvent(QPaintEvent *event) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} diff --git a/snespurify/phoenix/qt/checkbox.cpp b/snespurify/phoenix/qt/checkbox.cpp new file mode 100755 index 00000000..a0ab121b --- /dev/null +++ b/snespurify/phoenix/qt/checkbox.cpp @@ -0,0 +1,21 @@ +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + checkBox->setParent(parent.window->container); + checkBox->setGeometry(x, y, width, height); + checkBox->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) checkBox->setFont(*parent.window->defaultFont); + checkBox->show(); + checkBox->connect(checkBox, SIGNAL(stateChanged(int)), SLOT(onTick())); +} + +bool CheckBox::checked() { + return checkBox->isChecked(); +} + +void CheckBox::setChecked(bool checked) { + checkBox->setChecked(checked); +} + +CheckBox::CheckBox() { + checkBox = new CheckBox::Data(*this); + widget->widget = checkBox; +} diff --git a/snespurify/phoenix/qt/combobox.cpp b/snespurify/phoenix/qt/combobox.cpp new file mode 100755 index 00000000..41bd7d8f --- /dev/null +++ b/snespurify/phoenix/qt/combobox.cpp @@ -0,0 +1,38 @@ +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + comboBox->setParent(parent.window->container); + comboBox->setGeometry(x, y, width, height); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } + + comboBox->connect(comboBox, SIGNAL(currentIndexChanged(int)), SLOT(onChange())); + if(parent.window->defaultFont) comboBox->setFont(*parent.window->defaultFont); + comboBox->show(); +} + +void ComboBox::reset() { + while(comboBox->count()) comboBox->removeItem(0); +} + +void ComboBox::addItem(const string &text) { + comboBox->addItem(QString::fromUtf8(text)); +} + +unsigned ComboBox::selection() { + signed index = comboBox->currentIndex(); + return (index >= 0 ? index : 0); +} + +void ComboBox::setSelection(unsigned row) { + object->locked = true; + comboBox->setCurrentIndex(row); + object->locked = false; +} + +ComboBox::ComboBox() { + comboBox = new ComboBox::Data(*this); + widget->widget = comboBox; +} diff --git a/snespurify/phoenix/qt/editbox.cpp b/snespurify/phoenix/qt/editbox.cpp new file mode 100755 index 00000000..cb8bc61b --- /dev/null +++ b/snespurify/phoenix/qt/editbox.cpp @@ -0,0 +1,29 @@ +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + editBox->setParent(parent.window->container); + editBox->setGeometry(x, y, width, height); + editBox->setPlainText(QString::fromUtf8(text)); + if(parent.window->defaultFont) editBox->setFont(*parent.window->defaultFont); + editBox->show(); + editBox->connect(editBox, SIGNAL(textChanged()), SLOT(onChange())); +} + +void EditBox::setEditable(bool editable) { + editBox->setReadOnly(editable == false); +} + +void EditBox::setWordWrap(bool wordWrap) { + editBox->setWordWrapMode(wordWrap ? QTextOption::WordWrap : QTextOption::NoWrap); +} + +string EditBox::text() { + return editBox->toPlainText().toUtf8().constData(); +} + +void EditBox::setText(const string &text) { + editBox->setPlainText(QString::fromUtf8(text)); +} + +EditBox::EditBox() { + editBox = new EditBox::Data(*this); + widget->widget = editBox; +} diff --git a/snespurify/phoenix/qt/font.cpp b/snespurify/phoenix/qt/font.cpp new file mode 100755 index 00000000..01d6bab6 --- /dev/null +++ b/snespurify/phoenix/qt/font.cpp @@ -0,0 +1,14 @@ +bool Font::create(const string &name, unsigned size, Font::Style style) { + font->setFamily(QString::fromUtf8(name)); + font->setPointSize(size); + font->setBold((style & Style::Bold) == Style::Bold); + font->setItalic((style & Style::Italic) == Style::Italic); +} + +Font::Font() { + font = new Font::Data(*this); +} + +Font::~Font() { + delete font; +} diff --git a/snespurify/phoenix/qt/horizontalslider.cpp b/snespurify/phoenix/qt/horizontalslider.cpp new file mode 100755 index 00000000..48f9e18c --- /dev/null +++ b/snespurify/phoenix/qt/horizontalslider.cpp @@ -0,0 +1,22 @@ +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + horizontalSlider->setParent(parent.window->container); + horizontalSlider->setGeometry(x, y, width, height); + horizontalSlider->setRange(0, length - 1); + horizontalSlider->setPageStep(length >> 3); + horizontalSlider->connect(horizontalSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + horizontalSlider->show(); +} + +unsigned HorizontalSlider::position() { + return horizontalSlider->value(); +} + +void HorizontalSlider::setPosition(unsigned position) { + horizontalSlider->setValue(position); +} + +HorizontalSlider::HorizontalSlider() { + horizontalSlider = new HorizontalSlider::Data(*this); + widget->widget = horizontalSlider; +} diff --git a/snespurify/phoenix/qt/label.cpp b/snespurify/phoenix/qt/label.cpp new file mode 100755 index 00000000..cee55452 --- /dev/null +++ b/snespurify/phoenix/qt/label.cpp @@ -0,0 +1,16 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + label->setParent(parent.window->container); + label->setGeometry(x, y, width, height); + label->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) label->setFont(*parent.window->defaultFont); + label->show(); +} + +void Label::setText(const string &text) { + label->setText(QString::fromUtf8(text)); +} + +Label::Label() { + label = new Label::Data(*this); + widget->widget = label; +} diff --git a/snespurify/phoenix/qt/listbox.cpp b/snespurify/phoenix/qt/listbox.cpp new file mode 100755 index 00000000..ac5e97ee --- /dev/null +++ b/snespurify/phoenix/qt/listbox.cpp @@ -0,0 +1,102 @@ +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + listBox->setParent(parent.window->container); + listBox->setGeometry(x, y, width, height); + listBox->setAllColumnsShowFocus(true); + listBox->setRootIsDecorated(false); + + lstring list; + list.split("\t", text); + QStringList labels; + foreach(item, list) labels << QString::fromUtf8(item); + listBox->setColumnCount(list.size()); + listBox->setHeaderLabels(labels); + for(unsigned i = 0; i < list.size(); i++) listBox->resizeColumnToContents(i); + + listBox->setHeaderHidden(true); + listBox->setAlternatingRowColors(list.size() >= 2); + listBox->connect(listBox, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate())); + listBox->connect(listBox, SIGNAL(itemSelectionChanged()), SLOT(onChange())); + listBox->connect(listBox, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(onTick(QTreeWidgetItem*))); + if(parent.window->defaultFont) listBox->setFont(*parent.window->defaultFont); + listBox->show(); +} + +void ListBox::setHeaderVisible(bool headerVisible) { + listBox->setHeaderHidden(headerVisible == false); +} + +void ListBox::setCheckable(bool checkable) { + listBox->checkable = checkable; + if(listBox->checkable) { + auto items = listBox->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.size(); i++) items[i]->setCheckState(0, Qt::Unchecked); + } +} + +void ListBox::reset() { + listBox->clear(); +} + +void ListBox::resizeColumnsToContent() { + for(unsigned i = 0; i < listBox->columnCount(); i++) listBox->resizeColumnToContents(i); +} + +void ListBox::addItem(const string &text) { + object->locked = true; + auto items = listBox->findItems("", Qt::MatchContains); + QTreeWidgetItem *item = new QTreeWidgetItem(listBox); + if(listBox->checkable) item->setCheckState(0, Qt::Unchecked); + item->setData(0, Qt::UserRole, (unsigned)items.size()); + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) item->setText(i, QString::fromUtf8(list[i])); + object->locked = false; +} + +void ListBox::setItem(unsigned row, const string &text) { + object->locked = true; + QTreeWidgetItem *item = listBox->topLevelItem(row); + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) item->setText(i, QString::fromUtf8(list[i])); + object->locked = false; +} + +bool ListBox::checked(unsigned row) { + QTreeWidgetItem *item = listBox->topLevelItem(row); + return (item ? item->checkState(0) == Qt::Checked : false); +} + +void ListBox::setChecked(unsigned row, bool checked) { + object->locked = true; + QTreeWidgetItem *item = listBox->topLevelItem(row); + if(item) item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked); + object->locked = false; +} + +optional ListBox::selection() { + QTreeWidgetItem *item = listBox->currentItem(); + if(item == 0) return { false, 0 }; + if(item->isSelected() == false) return { false, 0 }; + unsigned row = item->data(0, Qt::UserRole).toUInt(); + return { true, row }; +} + +void ListBox::setSelection(unsigned row) { + object->locked = true; + QTreeWidgetItem *item = listBox->currentItem(); + if(item) item->setSelected(false); + auto items = listBox->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.size(); i++) { + if(items[i]->data(0, Qt::UserRole).toUInt() == row) { + listBox->setCurrentItem(items[i]); + break; + } + } + object->locked = false; +} + +ListBox::ListBox() { + listBox = new ListBox::Data(*this); + widget->widget = listBox; +} diff --git a/snespurify/phoenix/qt/menu.cpp b/snespurify/phoenix/qt/menu.cpp new file mode 100755 index 00000000..8e6fa819 --- /dev/null +++ b/snespurify/phoenix/qt/menu.cpp @@ -0,0 +1,169 @@ +void Menu::create(Window &parent, const string &text) { + menu->parent = &parent; + if(menu->parent->window->defaultFont) menu->setFont(*menu->parent->window->defaultFont); + menu->setTitle(QString::fromUtf8(text)); + parent.window->menuBar->addMenu(menu); +} + +void Menu::create(Menu &parent, const string &text) { + menu->parent = parent.menu->parent; + if(menu->parent->window->defaultFont) menu->setFont(*menu->parent->window->defaultFont); + menu->setTitle(QString::fromUtf8(text)); + parent.menu->addMenu(menu); +} + +bool Menu::visible() { + return menu->isVisible(); +} + +void Menu::setVisible(bool visible) { + menu->setVisible(visible); +} + +bool Menu::enabled() { + return menu->isEnabled(); +} + +void Menu::setEnabled(bool enabled) { + menu->setEnabled(enabled); +} + +Menu::Menu() { + menu = new Menu::Data(*this); +} + +void MenuSeparator::create(Menu &parent) { + menuSeparator->action = parent.menu->addSeparator(); +} + +bool MenuSeparator::visible() { + return menuSeparator->action->isVisible(); +} + +void MenuSeparator::setVisible(bool visible) { + menuSeparator->action->setVisible(visible); +} + +bool MenuSeparator::enabled() { + return menuSeparator->action->isEnabled(); +} + +void MenuSeparator::setEnabled(bool enabled) { + menuSeparator->action->setEnabled(enabled); +} + +MenuSeparator::MenuSeparator() { + menuSeparator = new MenuSeparator::Data(*this); +} + +void MenuItem::create(Menu &parent, const string &text) { + menuItem->setText(QString::fromUtf8(text)); + menuItem->connect(menuItem, SIGNAL(triggered()), SLOT(onTick())); + parent.menu->addAction(menuItem); +} + +bool MenuItem::visible() { + return menuItem->isVisible(); +} + +void MenuItem::setVisible(bool visible) { + menuItem->setVisible(visible); +} + +bool MenuItem::enabled() { + return menuItem->isEnabled(); +} + +void MenuItem::setEnabled(bool enabled) { + menuItem->setEnabled(enabled); +} + +MenuItem::MenuItem() { + menuItem = new MenuItem::Data(*this); +} + +void MenuCheckItem::create(Menu &parent, const string &text) { + menuCheckItem->setText(QString::fromUtf8(text)); + menuCheckItem->setCheckable(true); + menuCheckItem->connect(menuCheckItem, SIGNAL(triggered()), SLOT(onTick())); + parent.menu->addAction(menuCheckItem); +} + +bool MenuCheckItem::visible() { + return menuCheckItem->isVisible(); +} + +void MenuCheckItem::setVisible(bool visible) { + menuCheckItem->setVisible(visible); +} + +bool MenuCheckItem::enabled() { + return menuCheckItem->isEnabled(); +} + +void MenuCheckItem::setEnabled(bool enabled) { + menuCheckItem->setEnabled(enabled); +} + +bool MenuCheckItem::checked() { + return menuCheckItem->isChecked(); +} + +void MenuCheckItem::setChecked(bool checked) { + menuCheckItem->setChecked(checked); +} + +MenuCheckItem::MenuCheckItem() { + menuCheckItem = new MenuCheckItem::Data(*this); +} + +void MenuRadioItem::create(Menu &parent, const string &text) { + menuRadioItem->parent = &parent; + menuRadioItem->actionGroup = new QActionGroup(0); + menuRadioItem->actionGroup->addAction(menuRadioItem); + menuRadioItem->setText(QString::fromUtf8(text)); + menuRadioItem->setCheckable(true); + menuRadioItem->setChecked(true); + menuRadioItem->connect(menuRadioItem, SIGNAL(changed()), SLOT(onTick())); + menuRadioItem->parent->menu->addAction(menuRadioItem); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const string &text) { + menuRadioItem->parent = parent.menuRadioItem->parent; + menuRadioItem->actionGroup = parent.menuRadioItem->actionGroup; + menuRadioItem->actionGroup->addAction(menuRadioItem); + menuRadioItem->setText(QString::fromUtf8(text)); + menuRadioItem->setCheckable(true); + menuRadioItem->connect(menuRadioItem, SIGNAL(changed()), SLOT(onTick())); + menuRadioItem->parent->menu->addAction(menuRadioItem); +} + +bool MenuRadioItem::visible() { + return menuRadioItem->isVisible(); +} + +void MenuRadioItem::setVisible(bool visible) { + menuRadioItem->setVisible(visible); +} + +bool MenuRadioItem::enabled() { + return menuRadioItem->isEnabled(); +} + +void MenuRadioItem::setEnabled(bool enabled) { + menuRadioItem->setEnabled(enabled); +} + +bool MenuRadioItem::checked() { + return menuRadioItem->isChecked(); +} + +void MenuRadioItem::setChecked() { + object->locked = true; + menuRadioItem->setChecked(true); + object->locked = false; +} + +MenuRadioItem::MenuRadioItem() { + menuRadioItem = new MenuRadioItem::Data(*this); +} diff --git a/snespurify/phoenix/qt/messagewindow.cpp b/snespurify/phoenix/qt/messagewindow.cpp new file mode 100755 index 00000000..5d60ea01 --- /dev/null +++ b/snespurify/phoenix/qt/messagewindow.cpp @@ -0,0 +1,45 @@ +static QMessageBox::StandardButtons MessageWindow_buttons(MessageWindow::Buttons buttons) { + QMessageBox::StandardButtons standardButtons = QMessageBox::NoButton; + if(buttons == MessageWindow::Buttons::Ok) standardButtons = QMessageBox::Ok; + if(buttons == MessageWindow::Buttons::OkCancel) standardButtons = QMessageBox::Ok | QMessageBox::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) standardButtons = QMessageBox::Yes | QMessageBox::No; + return standardButtons; +} + +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, QMessageBox::StandardButton response) { + if(response == QMessageBox::Ok) return MessageWindow::Response::Ok; + if(response == QMessageBox::Cancel) return MessageWindow::Response::Cancel; + if(response == QMessageBox::Yes) return MessageWindow::Response::Yes; + if(response == QMessageBox::No) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::information(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::question(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::warning(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::critical(&parent != &Window::None ? parent.window : 0, " ", + QString::fromUtf8(text), MessageWindow_buttons(buttons)) + ); +} diff --git a/snespurify/phoenix/qt/object.cpp b/snespurify/phoenix/qt/object.cpp new file mode 100755 index 00000000..ae6cc6d9 --- /dev/null +++ b/snespurify/phoenix/qt/object.cpp @@ -0,0 +1,7 @@ +void Object::unused() { +} + +Object::Object() { + OS::initialize(); + object = new Object::Data(*this); +} diff --git a/snespurify/phoenix/qt/progressbar.cpp b/snespurify/phoenix/qt/progressbar.cpp new file mode 100755 index 00000000..ab21f882 --- /dev/null +++ b/snespurify/phoenix/qt/progressbar.cpp @@ -0,0 +1,16 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + progressBar->setParent(parent.window->container); + progressBar->setGeometry(x, y, width, height); + progressBar->setRange(0, 100); + progressBar->setTextVisible(false); + progressBar->show(); +} + +void ProgressBar::setPosition(unsigned position) { + progressBar->setValue(position); +} + +ProgressBar::ProgressBar() { + progressBar = new ProgressBar::Data(*this); + widget->widget = progressBar; +} diff --git a/snespurify/phoenix/qt/qt.cpp b/snespurify/phoenix/qt/qt.cpp new file mode 100755 index 00000000..203b4b6f --- /dev/null +++ b/snespurify/phoenix/qt/qt.cpp @@ -0,0 +1,128 @@ +#include +#include +using namespace nall; + +namespace phoenix { + +#include "qt.moc.hpp" +#include "qt.moc" + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +OS::Data *OS::os = 0; +Window Window::None; + +void OS::initialize() { + static bool initialized = false; + if(initialized == true) return; + initialized = true; + + os = new OS::Data; + static int argc = 1; + static char *argv[2]; + argv[0] = new char[8]; + argv[1] = 0; + strcpy(argv[0], "phoenix"); + char **argvp = argv; + os->application = new QApplication(argc, argvp); +} + +bool OS::pending() { + return QApplication::hasPendingEvents(); +} + +void OS::run() { + QApplication::processEvents(); +} + +void OS::main() { + QApplication::exec(); +} + +void OS::quit() { + QApplication::quit(); +} + +unsigned OS::desktopWidth() { + return QApplication::desktop()->screenGeometry().width(); +} + +unsigned OS::desktopHeight() { + return QApplication::desktop()->screenGeometry().height(); +} + +string OS::folderSelect(Window &parent, const string &path) { + QString directory = QFileDialog::getExistingDirectory( + &parent != &Window::None ? parent.window : 0, "Select Directory", + QString::fromUtf8(path), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks + ); + string name = directory.toUtf8().constData(); + if(name.endswith("/") == false) name.append("/"); + return name; +} + +string OS::fileOpen(Window &parent, const string &filter, const string &path) { + string filters; + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + if(part.size() != 2) continue; + part[1].replace(",", " "); + filters.append(part[0]); + filters.append(" ("); + filters.append(part[1]); + filters.append(");;"); + } + filters.rtrim(";;"); + + QString filename = QFileDialog::getOpenFileName( + &parent != &Window::None ? parent.window : 0, "Open File", + QString::fromUtf8(path), QString::fromUtf8(filters) + ); + return filename.toUtf8().constData(); +} + +string OS::fileSave(Window &parent, const string &filter, const string &path) { + string filters; + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + if(part.size() != 2) continue; + part[1].replace(",", " "); + filters.append(part[0]); + filters.append(" ("); + filters.append(part[1]); + filters.append(");;"); + } + filters.rtrim(";;"); + + QString filename = QFileDialog::getSaveFileName( + &parent != &Window::None ? parent.window : 0, "Save File", + QString::fromUtf8(path), QString::fromUtf8(filters) + ); + return filename.toUtf8().constData(); +} + +} diff --git a/snespurify/phoenix/qt/qt.hpp b/snespurify/phoenix/qt/qt.hpp new file mode 100755 index 00000000..3740e4c6 --- /dev/null +++ b/snespurify/phoenix/qt/qt.hpp @@ -0,0 +1,337 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + virtual void unused(); + struct Data; + Data *object; +}; + +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const nall::string &name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + virtual bool visible() = 0; + virtual void setVisible(bool visible = true) = 0; + virtual bool enabled() = 0; + virtual void setEnabled(bool enabled = true) = 0; +}; + +struct Menu : Action { + void create(Window &parent, const nall::string &text); + void create(Menu &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + Menu(); +//private: + struct Data; + Data *menu; +}; + +struct MenuSeparator : Action { + void create(Menu &parent); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + MenuSeparator(); +//private: + struct Data; + Data *menuSeparator; +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + MenuItem(); +//private: + struct Data; + Data *menuItem; +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(bool checked = true); + MenuCheckItem(); +//private: + struct Data; + Data *menuCheckItem; +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + void create(MenuRadioItem &parent, const nall::string &text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(); + MenuRadioItem(); +//private: + struct Data; + Data *menuRadioItem; +}; + +struct Widget : Object { + virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + virtual bool focused(); + virtual void setFocused(); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + Geometry geometry(); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setDefaultFont(Font &font); + void setFont(Font &font); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const nall::string &text); + void setStatusText(const nall::string &text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + bool focused(); + Window(); +//private: + struct Data; + Data *window; + static Window None; +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + Button(); +//private: + struct Data; + Data *button; +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(bool checked = true); + CheckBox(); +//private: + struct Data; + Data *checkBox; +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void reset(); + void addItem(const nall::string &text); + unsigned selection(); + void setSelection(unsigned row); + ComboBox(); +//private: + struct Data; + Data *comboBox; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + nall::string text(); + void setText(const nall::string &text); + EditBox(); +//private: + struct Data; + Data *editBox; +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + HorizontalSlider(); +//private: + struct Data; + Data *horizontalSlider; +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setText(const nall::string &text); + Label(); +//private: + struct Data; + Data *label; +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); + void reset(); + void resizeColumnsToContent(); + void addItem(const nall::string &text); + void setItem(unsigned row, const nall::string &text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setPosition(unsigned position); + ProgressBar(); +//private: + struct Data; + Data *progressBar; +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(); + RadioBox(); +//private: + struct Data; + Data *radioBox; +}; + +struct TextBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setEditable(bool editable = true); + nall::string text(); + void setText(const nall::string &text); + TextBox(); +//private: + struct Data; + Data *textBox; +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + VerticalSlider(); +//private: + struct Data; + Data *verticalSlider; +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); + Viewport(); +//private: + struct Data; + Data *viewport; +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + static bool pending(); + static void run(); + static void main(); + static void quit(); + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static nall::string folderSelect(Window &parent, const nall::string &path = ""); + static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = ""); + static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = ""); +//private: + struct Data; + static Data *os; + static void initialize(); +}; + +} diff --git a/snespurify/phoenix/qt/qt.moc b/snespurify/phoenix/qt/qt.moc new file mode 100755 index 00000000..5bb99a3b --- /dev/null +++ b/snespurify/phoenix/qt/qt.moc @@ -0,0 +1,930 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'qt.moc.hpp' +** +** Created: Mon Oct 11 13:03:04 2010 +** by: The Qt Meta Object Compiler version 62 (Qt 4.6.2) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'qt.moc.hpp' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 62 +#error "This file was generated using the moc from 4.6.2. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_MenuItem__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_MenuItem__Data[] = { + "MenuItem::Data\0\0onTick()\0" +}; + +const QMetaObject MenuItem::Data::staticMetaObject = { + { &QAction::staticMetaObject, qt_meta_stringdata_MenuItem__Data, + qt_meta_data_MenuItem__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &MenuItem::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *MenuItem::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *MenuItem::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_MenuItem__Data)) + return static_cast(const_cast< Data*>(this)); + return QAction::qt_metacast(_clname); +} + +int MenuItem::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QAction::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_MenuCheckItem__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 21, 20, 20, 20, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_MenuCheckItem__Data[] = { + "MenuCheckItem::Data\0\0onTick()\0" +}; + +const QMetaObject MenuCheckItem::Data::staticMetaObject = { + { &QAction::staticMetaObject, qt_meta_stringdata_MenuCheckItem__Data, + qt_meta_data_MenuCheckItem__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &MenuCheckItem::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *MenuCheckItem::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *MenuCheckItem::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_MenuCheckItem__Data)) + return static_cast(const_cast< Data*>(this)); + return QAction::qt_metacast(_clname); +} + +int MenuCheckItem::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QAction::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_MenuRadioItem__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 21, 20, 20, 20, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_MenuRadioItem__Data[] = { + "MenuRadioItem::Data\0\0onTick()\0" +}; + +const QMetaObject MenuRadioItem::Data::staticMetaObject = { + { &QAction::staticMetaObject, qt_meta_stringdata_MenuRadioItem__Data, + qt_meta_data_MenuRadioItem__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &MenuRadioItem::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *MenuRadioItem::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *MenuRadioItem::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_MenuRadioItem__Data)) + return static_cast(const_cast< Data*>(this)); + return QAction::qt_metacast(_clname); +} + +int MenuRadioItem::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QAction::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_Window__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Window__Data[] = { + "Window::Data\0" +}; + +const QMetaObject Window::Data::staticMetaObject = { + { &QWidget::staticMetaObject, qt_meta_stringdata_Window__Data, + qt_meta_data_Window__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Window::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Window::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Window::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Window__Data)) + return static_cast(const_cast< Data*>(this)); + return QWidget::qt_metacast(_clname); +} + +int Window::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +static const uint qt_meta_data_Button__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 14, 13, 13, 13, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_Button__Data[] = { + "Button::Data\0\0onTick()\0" +}; + +const QMetaObject Button::Data::staticMetaObject = { + { &QPushButton::staticMetaObject, qt_meta_stringdata_Button__Data, + qt_meta_data_Button__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Button::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Button::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Button::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Button__Data)) + return static_cast(const_cast< Data*>(this)); + return QPushButton::qt_metacast(_clname); +} + +int Button::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QPushButton::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_Canvas__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Canvas__Data[] = { + "Canvas::Data\0" +}; + +const QMetaObject Canvas::Data::staticMetaObject = { + { &QWidget::staticMetaObject, qt_meta_stringdata_Canvas__Data, + qt_meta_data_Canvas__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Canvas::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Canvas::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Canvas::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Canvas__Data)) + return static_cast(const_cast< Data*>(this)); + return QWidget::qt_metacast(_clname); +} + +int Canvas::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +static const uint qt_meta_data_CheckBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_CheckBox__Data[] = { + "CheckBox::Data\0\0onTick()\0" +}; + +const QMetaObject CheckBox::Data::staticMetaObject = { + { &QCheckBox::staticMetaObject, qt_meta_stringdata_CheckBox__Data, + qt_meta_data_CheckBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &CheckBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *CheckBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *CheckBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_CheckBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QCheckBox::qt_metacast(_clname); +} + +int CheckBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QCheckBox::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_ComboBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_ComboBox__Data[] = { + "ComboBox::Data\0\0onChange()\0" +}; + +const QMetaObject ComboBox::Data::staticMetaObject = { + { &QComboBox::staticMetaObject, qt_meta_stringdata_ComboBox__Data, + qt_meta_data_ComboBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &ComboBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *ComboBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *ComboBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_ComboBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QComboBox::qt_metacast(_clname); +} + +int ComboBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QComboBox::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_EditBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 15, 14, 14, 14, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_EditBox__Data[] = { + "EditBox::Data\0\0onChange()\0" +}; + +const QMetaObject EditBox::Data::staticMetaObject = { + { &QTextEdit::staticMetaObject, qt_meta_stringdata_EditBox__Data, + qt_meta_data_EditBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &EditBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *EditBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *EditBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_EditBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QTextEdit::qt_metacast(_clname); +} + +int EditBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QTextEdit::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_HorizontalSlider__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 24, 23, 23, 23, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_HorizontalSlider__Data[] = { + "HorizontalSlider::Data\0\0onChange()\0" +}; + +const QMetaObject HorizontalSlider::Data::staticMetaObject = { + { &QSlider::staticMetaObject, qt_meta_stringdata_HorizontalSlider__Data, + qt_meta_data_HorizontalSlider__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &HorizontalSlider::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *HorizontalSlider::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *HorizontalSlider::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_HorizontalSlider__Data)) + return static_cast(const_cast< Data*>(this)); + return QSlider::qt_metacast(_clname); +} + +int HorizontalSlider::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QSlider::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_Label__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_Label__Data[] = { + "Label::Data\0" +}; + +const QMetaObject Label::Data::staticMetaObject = { + { &QLabel::staticMetaObject, qt_meta_stringdata_Label__Data, + qt_meta_data_Label__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &Label::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *Label::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *Label::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Label__Data)) + return static_cast(const_cast< Data*>(this)); + return QLabel::qt_metacast(_clname); +} + +int Label::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QLabel::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +static const uint qt_meta_data_ListBox__Data[] = { + + // content: + 4, // 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 + 15, 14, 14, 14, 0x0a, + 28, 14, 14, 14, 0x0a, + 44, 39, 14, 14, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_ListBox__Data[] = { + "ListBox::Data\0\0onActivate()\0onChange()\0" + "item\0onTick(QTreeWidgetItem*)\0" +}; + +const QMetaObject ListBox::Data::staticMetaObject = { + { &QTreeWidget::staticMetaObject, qt_meta_stringdata_ListBox__Data, + qt_meta_data_ListBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &ListBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *ListBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *ListBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_ListBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QTreeWidget::qt_metacast(_clname); +} + +int ListBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QTreeWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onActivate(); break; + case 1: onChange(); break; + case 2: onTick((*reinterpret_cast< QTreeWidgetItem*(*)>(_a[1]))); break; + default: ; + } + _id -= 3; + } + return _id; +} +static const uint qt_meta_data_RadioBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 15, 15, 15, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_RadioBox__Data[] = { + "RadioBox::Data\0\0onTick()\0" +}; + +const QMetaObject RadioBox::Data::staticMetaObject = { + { &QRadioButton::staticMetaObject, qt_meta_stringdata_RadioBox__Data, + qt_meta_data_RadioBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &RadioBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *RadioBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *RadioBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_RadioBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QRadioButton::qt_metacast(_clname); +} + +int RadioBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QRadioButton::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onTick(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_TextBox__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 2, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 15, 14, 14, 14, 0x0a, + 28, 14, 14, 14, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_TextBox__Data[] = { + "TextBox::Data\0\0onActivate()\0onChange()\0" +}; + +const QMetaObject TextBox::Data::staticMetaObject = { + { &QLineEdit::staticMetaObject, qt_meta_stringdata_TextBox__Data, + qt_meta_data_TextBox__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &TextBox::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *TextBox::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *TextBox::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_TextBox__Data)) + return static_cast(const_cast< Data*>(this)); + return QLineEdit::qt_metacast(_clname); +} + +int TextBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QLineEdit::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onActivate(); break; + case 1: onChange(); break; + default: ; + } + _id -= 2; + } + return _id; +} +static const uint qt_meta_data_VerticalSlider__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 22, 21, 21, 21, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_VerticalSlider__Data[] = { + "VerticalSlider::Data\0\0onChange()\0" +}; + +const QMetaObject VerticalSlider::Data::staticMetaObject = { + { &QSlider::staticMetaObject, qt_meta_stringdata_VerticalSlider__Data, + qt_meta_data_VerticalSlider__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &VerticalSlider::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *VerticalSlider::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *VerticalSlider::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_VerticalSlider__Data)) + return static_cast(const_cast< Data*>(this)); + return QSlider::qt_metacast(_clname); +} + +int VerticalSlider::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QSlider::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: onChange(); break; + default: ; + } + _id -= 1; + } + return _id; +} +static const uint qt_meta_data_OS__Data[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_OS__Data[] = { + "OS::Data\0" +}; + +const QMetaObject OS::Data::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_OS__Data, + qt_meta_data_OS__Data, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &OS::Data::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *OS::Data::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *OS::Data::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_OS__Data)) + return static_cast(const_cast< Data*>(this)); + return QObject::qt_metacast(_clname); +} + +int OS::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/snespurify/phoenix/qt/qt.moc.hpp b/snespurify/phoenix/qt/qt.moc.hpp new file mode 100755 index 00000000..8beaed5e --- /dev/null +++ b/snespurify/phoenix/qt/qt.moc.hpp @@ -0,0 +1,311 @@ +struct Object::Data { +public: + Object &self; + bool locked; + + Data(Object &self) : self(self) { + locked = false; + } +}; + +struct Font::Data : public QFont { +public: + Font &self; + + Data(Font &self) : self(self) { + } +}; + +struct Menu::Data : public QMenu { +public: + Menu &self; + Window *parent; + + Data(Menu &self) : self(self), parent(0) { + } +}; + +struct MenuSeparator::Data { +public: + MenuSeparator &self; + QAction *action; + + Data(MenuSeparator &self) : self(self) { + } +}; + +struct MenuItem::Data : public QAction { + Q_OBJECT + +public: + MenuItem &self; + + Data(MenuItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct MenuCheckItem::Data : public QAction { + Q_OBJECT + +public: + MenuCheckItem &self; + + Data(MenuCheckItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct MenuRadioItem::Data : public QAction { + Q_OBJECT + +public: + MenuRadioItem &self; + Menu *parent; + QActionGroup *actionGroup; + + Data(MenuRadioItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.object->locked == false && self.onTick && self.checked()) self.onTick(); + } +}; + +struct Widget::Data { +public: + Widget &self; + QWidget *widget; + + Data(Widget &self) : self(self) { + } +}; + +struct Window::Data : public QWidget { + Q_OBJECT + +public: + Window &self; + QFont *defaultFont; + QVBoxLayout *layout; + QMenuBar *menuBar; + QWidget *container; + QStatusBar *statusBar; + + void closeEvent(QCloseEvent *event) { + if(self.onClose) { + bool result = self.onClose(); + if(result == false) event->ignore(); + } + } + + Data(Window &self) : self(self) { + } +}; + +struct Button::Data : public QPushButton { + Q_OBJECT + +public: + Button &self; + + Data(Button &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct Canvas::Data : public QWidget { + Q_OBJECT + +public: + Canvas &self; + QImage *image; + void paintEvent(QPaintEvent*); + + Data(Canvas &self) : self(self) { + } +}; + +struct CheckBox::Data : public QCheckBox { + Q_OBJECT + +public: + CheckBox &self; + + Data(CheckBox &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct ComboBox::Data : public QComboBox { + Q_OBJECT + +public: + ComboBox &self; + + Data(ComboBox &self) : self(self) { + } + +public slots: + void onChange() { + if(self.object->locked == false && self.onChange) self.onChange(); + } +}; + +struct EditBox::Data : public QTextEdit { + Q_OBJECT + +public: + EditBox &self; + + Data(EditBox &self) : self(self) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct HorizontalSlider::Data : public QSlider { + Q_OBJECT + +public: + HorizontalSlider &self; + + Data(HorizontalSlider &self) : self(self), QSlider(Qt::Horizontal) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct Label::Data : public QLabel { + Q_OBJECT + +public: + Label &self; + + Data(Label &self) : self(self) { + } +}; + +struct ListBox::Data : public QTreeWidget { + Q_OBJECT + +public: + ListBox &self; + bool checkable; + + Data(ListBox &self) : self(self) { + checkable = false; + } + +public slots: + void onActivate() { + if(self.object->locked == false && self.onActivate) self.onActivate(); + } + + void onChange() { + if(self.object->locked == false && self.onChange) self.onChange(); + } + + void onTick(QTreeWidgetItem *item) { + if(self.object->locked == false && self.onTick) self.onTick(item->data(0, Qt::UserRole).toUInt()); + } +}; + +struct ProgressBar::Data : public QProgressBar { +public: + ProgressBar &self; + + Data(ProgressBar &self) : self(self) { + } +}; + +struct RadioBox::Data : public QRadioButton { + Q_OBJECT + +public: + RadioBox &self; + Window *parent; + QButtonGroup *buttonGroup; + + Data(RadioBox &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick && self.checked()) self.onTick(); + } +}; + +struct TextBox::Data : public QLineEdit { + Q_OBJECT + +public: + TextBox &self; + + Data(TextBox &self) : self(self) { + } + +public slots: + void onActivate() { + if(self.onActivate) self.onActivate(); + } + + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct VerticalSlider::Data : public QSlider { + Q_OBJECT + +public: + VerticalSlider &self; + + Data(VerticalSlider &self) : self(self), QSlider(Qt::Vertical) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct Viewport::Data : public QWidget { +public: + Viewport &self; + + Data(Viewport &self) : self(self) { + } +}; + +struct OS::Data : public QObject { + Q_OBJECT + +public: + QApplication *application; + +public slots: +}; diff --git a/snespurify/phoenix/qt/radiobox.cpp b/snespurify/phoenix/qt/radiobox.cpp new file mode 100755 index 00000000..b6f3a846 --- /dev/null +++ b/snespurify/phoenix/qt/radiobox.cpp @@ -0,0 +1,37 @@ +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parent = &parent; + radioBox->buttonGroup = new QButtonGroup; + radioBox->buttonGroup->addButton(radioBox); + radioBox->setParent(radioBox->parent->window->container); + radioBox->setGeometry(x, y, width, height); + radioBox->setText(QString::fromUtf8(text)); + radioBox->setChecked(true); + if(parent.window->defaultFont) radioBox->setFont(*parent.window->defaultFont); + radioBox->show(); + radioBox->connect(radioBox, SIGNAL(toggled(bool)), SLOT(onTick())); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parent = parent.radioBox->parent; + radioBox->buttonGroup = parent.radioBox->buttonGroup; + radioBox->buttonGroup->addButton(radioBox); + radioBox->setParent(radioBox->parent->window->container); + radioBox->setGeometry(x, y, width, height); + radioBox->setText(QString::fromUtf8(text)); + if(radioBox->parent->window->defaultFont) radioBox->setFont(*radioBox->parent->window->defaultFont); + radioBox->show(); + radioBox->connect(radioBox, SIGNAL(toggled(bool)), SLOT(onTick())); +} + +bool RadioBox::checked() { + return radioBox->isChecked(); +} + +void RadioBox::setChecked() { + radioBox->setChecked(true); +} + +RadioBox::RadioBox() { + radioBox = new RadioBox::Data(*this); + widget->widget = radioBox; +} diff --git a/snespurify/phoenix/qt/textbox.cpp b/snespurify/phoenix/qt/textbox.cpp new file mode 100755 index 00000000..ffe2c82f --- /dev/null +++ b/snespurify/phoenix/qt/textbox.cpp @@ -0,0 +1,26 @@ +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + textBox->setParent(parent.window->container); + textBox->setGeometry(x, y, width, height); + textBox->setText(QString::fromUtf8(text)); + if(parent.window->defaultFont) textBox->setFont(*parent.window->defaultFont); + textBox->show(); + textBox->connect(textBox, SIGNAL(returnPressed()), SLOT(onActivate())); + textBox->connect(textBox, SIGNAL(textEdited(const QString&)), SLOT(onChange())); +} + +void TextBox::setEditable(bool editable) { + textBox->setReadOnly(editable == false); +} + +string TextBox::text() { + return textBox->text().toUtf8().constData(); +} + +void TextBox::setText(const string &text) { + textBox->setText(QString::fromUtf8(text)); +} + +TextBox::TextBox() { + textBox = new TextBox::Data(*this); + widget->widget = textBox; +} diff --git a/snespurify/phoenix/qt/verticalslider.cpp b/snespurify/phoenix/qt/verticalslider.cpp new file mode 100755 index 00000000..98d9f22a --- /dev/null +++ b/snespurify/phoenix/qt/verticalslider.cpp @@ -0,0 +1,24 @@ +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + verticalSlider->setParent(parent.window->container); + verticalSlider->setGeometry(x, y, width, height); + verticalSlider->setInvertedAppearance(true); + verticalSlider->setInvertedControls(true); + verticalSlider->setRange(0, length - 1); + verticalSlider->setPageStep(length >> 3); + verticalSlider->connect(verticalSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + verticalSlider->show(); +} + +unsigned VerticalSlider::position() { + return verticalSlider->value(); +} + +void VerticalSlider::setPosition(unsigned position) { + verticalSlider->setValue(position); +} + +VerticalSlider::VerticalSlider() { + verticalSlider = new VerticalSlider::Data(*this); + widget->widget = verticalSlider; +} diff --git a/snespurify/phoenix/qt/viewport.cpp b/snespurify/phoenix/qt/viewport.cpp new file mode 100755 index 00000000..7c4b0f19 --- /dev/null +++ b/snespurify/phoenix/qt/viewport.cpp @@ -0,0 +1,16 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + viewport->setParent(parent.window->container); + viewport->setGeometry(x, y, width, height); + viewport->setAttribute(Qt::WA_PaintOnScreen, true); + viewport->setStyleSheet("background: #000000"); + viewport->show(); +} + +uintptr_t Viewport::handle() { + return (uintptr_t)viewport->winId(); +} + +Viewport::Viewport() { + viewport = new Viewport::Data(*this); + widget->widget = viewport; +} diff --git a/snespurify/phoenix/qt/widget.cpp b/snespurify/phoenix/qt/widget.cpp new file mode 100755 index 00000000..9510607a --- /dev/null +++ b/snespurify/phoenix/qt/widget.cpp @@ -0,0 +1,35 @@ +void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + widget->widget->setGeometry(x, y, width, height); +} + +void Widget::setFont(Font &font) { + widget->widget->setFont(*font.font); +} + +bool Widget::visible() { + return widget->widget->isVisible(); +} + +void Widget::setVisible(bool visible) { + widget->widget->setVisible(visible); +} + +bool Widget::enabled() { + return widget->widget->isEnabled(); +} + +void Widget::setEnabled(bool enabled) { + widget->widget->setEnabled(enabled); +} + +bool Widget::focused() { + return widget->widget->hasFocus(); +} + +void Widget::setFocused() { + widget->widget->setFocus(Qt::OtherFocusReason); +} + +Widget::Widget() { + widget = new Widget::Data(*this); +} diff --git a/snespurify/phoenix/qt/window.cpp b/snespurify/phoenix/qt/window.cpp new file mode 100755 index 00000000..ef209ab0 --- /dev/null +++ b/snespurify/phoenix/qt/window.cpp @@ -0,0 +1,77 @@ +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + window->setWindowTitle(QString::fromUtf8(text)); + window->move(x, y); + + window->layout = new QVBoxLayout(window); + window->layout->setMargin(0); + window->layout->setSpacing(0); + window->layout->setSizeConstraint(QLayout::SetFixedSize); + window->setLayout(window->layout); + + window->menuBar = new QMenuBar(window); + window->menuBar->setVisible(false); + window->layout->addWidget(window->menuBar); + + window->container = new QWidget(window); + window->container->setFixedSize(width, height); + window->container->setVisible(true); + window->layout->addWidget(window->container); + + window->statusBar = new QStatusBar(window); + window->statusBar->setSizeGripEnabled(false); + window->statusBar->setVisible(false); + window->layout->addWidget(window->statusBar); +} + +Geometry Window::geometry() { + return Geometry(window->x(), window->y(), window->container->width(), window->container->height()); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + window->container->setFixedSize(width, height); + window->move(x, y); +} + +void Window::setDefaultFont(Font &font) { + window->defaultFont = font.font; + window->menuBar->setFont(*font.font); +} + +void Window::setFont(Font &font) { + window->statusBar->setFont(*font.font); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + QPalette palette; + palette.setColor(QPalette::Window, QColor(red, green, blue)); + window->setPalette(palette); + window->setAutoFillBackground(true); +} + +void Window::setTitle(const string &text) { + window->setWindowTitle(QString::fromUtf8(text)); +} + +void Window::setStatusText(const string &text) { + window->statusBar->showMessage(QString::fromUtf8(text), 0); +} + +void Window::setMenuVisible(bool visible) { + if(visible) window->menuBar->show(); + else window->menuBar->hide(); +} + +void Window::setStatusVisible(bool visible) { + if(visible) window->statusBar->show(); + else window->statusBar->hide(); +} + +bool Window::focused() { + return window->isActiveWindow() && !window->isMinimized(); +} + +Window::Window() { + window = new Window::Data(*this); + window->defaultFont = 0; + widget->widget = window; +} diff --git a/snespurify/phoenix/sync.sh b/snespurify/phoenix/sync.sh new file mode 100755 index 00000000..b51e3b40 --- /dev/null +++ b/snespurify/phoenix/sync.sh @@ -0,0 +1,8 @@ +synchronize() { + if [ -d ../"$1" ]; then + test -d "$1" && rm -r "$1" + cp -r ../"$1" ./"$1" + fi +} + +synchronize "nall" diff --git a/snespurify/phoenix/windows/button.cpp b/snespurify/phoenix/windows/button.cpp new file mode 100755 index 00000000..267b446e --- /dev/null +++ b/snespurify/phoenix/windows/button.cpp @@ -0,0 +1,10 @@ +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} diff --git a/snespurify/phoenix/windows/canvas.cpp b/snespurify/phoenix/windows/canvas.cpp new file mode 100755 index 00000000..783d7042 --- /dev/null +++ b/snespurify/phoenix/windows/canvas.cpp @@ -0,0 +1,60 @@ +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->buffer = new uint32_t[width * height](); + canvas->pitch = width * sizeof(uint32_t); + canvas->width = width; + canvas->height = height; + memset(&canvas->bmi, 0, sizeof(BITMAPINFO)); + canvas->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + canvas->bmi.bmiHeader.biPlanes = 1; + canvas->bmi.bmiHeader.biBitCount = 32; + canvas->bmi.bmiHeader.biCompression = BI_RGB; + canvas->bmi.bmiHeader.biWidth = width; + canvas->bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside down; negative height flips bitmap + canvas->bmi.bmiHeader.biSizeImage = canvas->pitch * canvas->height; + + widget->window = CreateWindow( + L"phoenix_canvas", L"", + WS_CHILD | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +uint32_t* Canvas::buffer() { + return canvas->buffer; +} + +void Canvas::redraw() { + PAINTSTRUCT ps; + BeginPaint(widget->window, &ps); + SetDIBitsToDevice(ps.hdc, 0, 0, canvas->width, canvas->height, 0, 0, 0, canvas->height, (void*)canvas->buffer, &canvas->bmi, DIB_RGB_COLORS); + EndPaint(widget->window, &ps); + InvalidateRect(widget->window, 0, false); +} + +Canvas::Canvas() { + canvas = new Canvas::Data; + canvas->buffer = 0; +} + +Canvas::~Canvas() { + delete[] canvas->buffer; + delete canvas; +} + +static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch(msg) { + case WM_PAINT: { + Object *object_ptr = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + Canvas &canvas = (Canvas&)*object_ptr; + canvas.redraw(); + } + } + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/snespurify/phoenix/windows/checkbox.cpp b/snespurify/phoenix/windows/checkbox.cpp new file mode 100755 index 00000000..035deb56 --- /dev/null +++ b/snespurify/phoenix/windows/checkbox.cpp @@ -0,0 +1,18 @@ +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_CHECKBOX, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} + +bool CheckBox::checked() { + return SendMessage(widget->window, BM_GETCHECK, 0, 0); +} + +void CheckBox::setChecked(bool checked) { + SendMessage(widget->window, BM_SETCHECK, (WPARAM)checked, 0); +} diff --git a/snespurify/phoenix/windows/combobox.cpp b/snespurify/phoenix/windows/combobox.cpp new file mode 100755 index 00000000..4965a529 --- /dev/null +++ b/snespurify/phoenix/windows/combobox.cpp @@ -0,0 +1,46 @@ +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + 0, L"COMBOBOX", L"", + WS_CHILD | WS_TABSTOP | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS, + x, y, width, 200, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + + //CreateWindow height parameter is the height of the expanded list box; + //need additional code to override default ComboBox control height + RECT rc; + GetWindowRect(widget->window, &rc); + unsigned adjustedHeight = height - ((rc.bottom - rc.top) - SendMessage(widget->window, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); + SendMessage(widget->window, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } +} + +void ComboBox::reset() { + SendMessage(widget->window, CB_RESETCONTENT, 0, 0); +} + +void ComboBox::addItem(const string &text) { + SendMessage(widget->window, CB_ADDSTRING, 0, (LPARAM)(wchar_t*)utf16_t(text)); + if(SendMessage(widget->window, CB_GETCOUNT, 0, 0) == 1) setSelection(0); +} + +unsigned ComboBox::selection() { + return SendMessage(widget->window, CB_GETCURSEL, 0, 0); +} + +void ComboBox::setSelection(unsigned row) { + SendMessage(widget->window, CB_SETCURSEL, comboBox->selection = row, 0); +} + +ComboBox::ComboBox() { + comboBox = new ComboBox::Data; + comboBox->selection = 0; +} diff --git a/snespurify/phoenix/windows/editbox.cpp b/snespurify/phoenix/windows/editbox.cpp new file mode 100755 index 00000000..2d165b3a --- /dev/null +++ b/snespurify/phoenix/windows/editbox.cpp @@ -0,0 +1,53 @@ +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | + (editBox->wordWrap == false ? ES_AUTOHSCROLL : 0), + editBox->x = x, editBox->y = y, editBox->width = width, editBox->height = height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + setText(text); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} + +string EditBox::getText() { + unsigned length = GetWindowTextLength(widget->window); + wchar_t buffer[length + 1]; + GetWindowText(widget->window, buffer, length + 1); + buffer[length] = 0; + string text = utf8_t(buffer); + text.replace("\r", ""); + return text; +} + +void EditBox::setText(const string &text) { + string output = text; + output.replace("\r", ""); + output.replace("\n", "\r\n"); + object->locked = true; + SetWindowText(widget->window, utf16_t(output)); + object->locked = false; +} + +void EditBox::setEditable(bool editable) { + SendMessage(widget->window, EM_SETREADONLY, editable == false, (LPARAM)0); +} + +void EditBox::setWordWrap(bool wordWrap) { + editBox->wordWrap = wordWrap; + if(widget->window == 0) return; + + //ES_AUTOSCROLL options cannot be changed after control has been created; + //so destroy the control and recreate it with desired options + HWND hparent = GetParent(widget->window); + Window *parent = (Window*)GetWindowLongPtr(hparent, GWLP_USERDATA); + string text = getText(); + DestroyWindow(widget->window); + create(*parent, editBox->x, editBox->y, editBox->width, editBox->height, text); +} + +EditBox::EditBox() { + editBox = new EditBox::Data; + editBox->wordWrap = true; +} diff --git a/snespurify/phoenix/windows/font.cpp b/snespurify/phoenix/windows/font.cpp new file mode 100755 index 00000000..712b928f --- /dev/null +++ b/snespurify/phoenix/windows/font.cpp @@ -0,0 +1,26 @@ +static HFONT Font_createFont(const string &name, unsigned size, bool bold, bool italic) { + return CreateFont( + -(size * 96.0 / 72.0 + 0.5), + 0, 0, 0, bold == false ? FW_NORMAL : FW_BOLD, italic, 0, 0, 0, 0, 0, 0, 0, + utf16_t(name) + ); +} + +bool Font::create(const string &name, unsigned size, Font::Style style) { + font->font = Font_createFont( + name, size, + (style & Font::Style::Bold) == Font::Style::Bold, + (style & Font::Style::Italic) == Font::Style::Italic + ); + return font->font; +} + +Font::Font() { + font = new Font::Data; + font->font = 0; +} + +Font::~Font() { + if(font->font) DeleteObject(font->font); + delete font; +} diff --git a/snespurify/phoenix/windows/horizontalslider.cpp b/snespurify/phoenix/windows/horizontalslider.cpp new file mode 100755 index 00000000..9f5939bd --- /dev/null +++ b/snespurify/phoenix/windows/horizontalslider.cpp @@ -0,0 +1,25 @@ +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + widget->window = CreateWindow( + TRACKBAR_CLASS, L"", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_HORZ, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); + SendMessage(widget->window, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3)); + setPosition(0); +} + +unsigned HorizontalSlider::position() { + return SendMessage(widget->window, TBM_GETPOS, 0, 0); +} + +void HorizontalSlider::setPosition(unsigned position) { + SendMessage(widget->window, TBM_SETPOS, (WPARAM)true, (LPARAM)(horizontalSlider->position = position)); +} + +HorizontalSlider::HorizontalSlider() { + horizontalSlider = new HorizontalSlider::Data; +} diff --git a/snespurify/phoenix/windows/label.cpp b/snespurify/phoenix/windows/label.cpp new file mode 100755 index 00000000..e20c7cf0 --- /dev/null +++ b/snespurify/phoenix/windows/label.cpp @@ -0,0 +1,51 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindow( + L"phoenix_label", L"", + WS_CHILD | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + setText(text); +} + +void Label::setText(const string &text) { + SetWindowText(widget->window, utf16_t(text)); + InvalidateRect(widget->window, 0, false); +} + +//all of this for want of a STATIC SS_VCENTER flag ... +LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + Window *window_ptr = (Window*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + if(!window_ptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Label *label_ptr = (Label*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(!label_ptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Window &window = *window_ptr; + Label &label = *label_ptr; + + switch(msg) { + case WM_PAINT: { + PAINTSTRUCT ps; + RECT rc; + BeginPaint(hwnd, &ps); + GetClientRect(hwnd, &rc); + FillRect(ps.hdc, &rc, window.window->brush ? window.window->brush : GetSysColorBrush(COLOR_3DFACE)); + SetBkColor(ps.hdc, window.window->brush ? window.window->brushColor : GetSysColor(COLOR_3DFACE)); + SelectObject(ps.hdc, label.widget->font); + unsigned length = GetWindowTextLength(hwnd); + wchar_t text[length + 1]; + GetWindowText(hwnd, text, length + 1); + text[length] = 0; + DrawText(ps.hdc, text, -1, &rc, DT_CALCRECT | DT_END_ELLIPSIS); + unsigned height = rc.bottom; + GetClientRect(hwnd, &rc); + rc.top = (rc.bottom - height) / 2; + rc.bottom = rc.top + height; + DrawText(ps.hdc, text, -1, &rc, DT_LEFT | DT_END_ELLIPSIS); + EndPaint(hwnd, &ps); + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/snespurify/phoenix/windows/listbox.cpp b/snespurify/phoenix/windows/listbox.cpp new file mode 100755 index 00000000..86d82255 --- /dev/null +++ b/snespurify/phoenix/windows/listbox.cpp @@ -0,0 +1,114 @@ +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", + WS_CHILD | WS_TABSTOP | WS_VISIBLE | + LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT); + + lstring list; + list.split("\t", text); + listBox->columns = list.size(); + for(unsigned i = 0; i < list.size(); i++) { + LVCOLUMN column; + column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM; + column.fmt = LVCFMT_LEFT; + column.iSubItem = list.size(); + utf16_t text(list[i]); + column.pszText = text; + ListView_InsertColumn(widget->window, i, &column); + } + resizeColumnsToContent(); +} + +void ListBox::setHeaderVisible(bool headerVisible) { + SetWindowLong( + widget->window, + GWL_STYLE, + (GetWindowLong(widget->window, GWL_STYLE) & ~LVS_NOCOLUMNHEADER) | + (headerVisible == false ? LVS_NOCOLUMNHEADER : 0) + ); +} + +void ListBox::setCheckable(bool checkable) { + ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT | (checkable ? LVS_EX_CHECKBOXES : 0)); +} + +void ListBox::reset() { + ListView_DeleteAllItems(widget->window); +} + +void ListBox::resizeColumnsToContent() { + for(unsigned i = 0; i < listBox->columns; i++) { + ListView_SetColumnWidth(widget->window, i, LVSCW_AUTOSIZE_USEHEADER); + } +} + +void ListBox::addItem(const string &text) { + lstring list; + list.split("\t", text); + LVITEM item; + unsigned row = ListView_GetItemCount(widget->window); + item.mask = LVIF_TEXT; + item.iItem = row; + item.iSubItem = 0; + utf16_t wtext(list[0]); + item.pszText = wtext; + object->locked = true; + ListView_InsertItem(widget->window, &item); + object->locked = false; + for(unsigned i = 1; i < list.size(); i++) { + utf16_t wtext(list[i]); + ListView_SetItemText(widget->window, row, i, wtext); + } + + //workaround: when there is only one column, the horizontal scrollbar will always appear without this + if(listBox->columns == 1) ListView_SetColumnWidth(widget->window, 0, LVSCW_AUTOSIZE_USEHEADER); +} + +void ListBox::setItem(unsigned row, const string &text) { + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) { + utf16_t wtext(list[i]); + ListView_SetItemText(widget->window, row, i, wtext); + } + + //workaround: when there is only one column, the horizontal scrollbar will always appear without this + if(listBox->columns == 1) ListView_SetColumnWidth(widget->window, 0, LVSCW_AUTOSIZE_USEHEADER); +} + +optional ListBox::selection() { + unsigned count = ListView_GetItemCount(widget->window); + for(unsigned i = 0; i < count; i++) { + if(ListView_GetItemState(widget->window, i, LVIS_SELECTED)) return { true, i }; + } + return { false, 0 }; +} + +void ListBox::setSelection(unsigned row) { + unsigned count = ListView_GetItemCount(widget->window); + for(unsigned i = 0; i < count; i++) { + ListView_SetItemState(widget->window, i, LVIS_FOCUSED, (i == row ? LVIS_FOCUSED : 0)); + ListView_SetItemState(widget->window, i, LVIS_SELECTED, (i == row ? LVIS_SELECTED : 0)); + } +} + +bool ListBox::checked(unsigned row) { + return ListView_GetCheckState(widget->window, row); +} + +void ListBox::setChecked(unsigned row, bool checked) { + object->locked = true; + ListView_SetCheckState(widget->window, row, checked); + object->locked = false; +} + +ListBox::ListBox() { + listBox = new ListBox::Data; + listBox->lostFocus = false; +} diff --git a/snespurify/phoenix/windows/menu.cpp b/snespurify/phoenix/windows/menu.cpp new file mode 100755 index 00000000..789ed382 --- /dev/null +++ b/snespurify/phoenix/windows/menu.cpp @@ -0,0 +1,144 @@ +Action::Action() { + OS::os->objects.append(this); + action = new Action::Data; +} + +void Menu::create(Window &parent, const string &text) { + action->parentMenu = parent.window->menu; + action->menu = CreatePopupMenu(); + AppendMenu(parent.window->menu, MF_STRING | MF_POPUP, (UINT_PTR)action->menu, utf16_t(text)); +} + +void Menu::create(Menu &parent, const string &text) { + action->parentMenu = parent.action->menu; + action->menu = CreatePopupMenu(); + AppendMenu(parent.action->menu, MF_STRING | MF_POPUP, (UINT_PTR)action->menu, utf16_t(text)); +} + +bool Menu::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parentMenu, (UINT_PTR)action->menu, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void Menu::setEnabled(bool enabled) { + EnableMenuItem(action->parentMenu, (UINT_PTR)action->menu, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuSeparator::create(Menu &parent) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_SEPARATOR, object->id, L""); +} + +bool MenuSeparator::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuSeparator::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuItem::create(Menu &parent, const string &text) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuCheckItem::create(Menu &parent, const string &text) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuCheckItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuCheckItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +bool MenuCheckItem::checked() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return info.fState & MFS_CHECKED; +} + +void MenuCheckItem::setChecked(bool checked) { + CheckMenuItem(action->parent->action->menu, object->id, checked ? MF_CHECKED : MF_UNCHECKED); +} + +void MenuRadioItem::create(Menu &parent, const string &text) { + action->parent = &parent; + action->radioParent = this; + action->items.append(this); + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); + setChecked(); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const string &text) { + action->parent = parent.action->parent; + action->radioParent = parent.action->radioParent; + action->radioParent->action->items.append(this); + AppendMenu(action->parent->action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuRadioItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuRadioItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +bool MenuRadioItem::checked() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return info.fState & MFS_CHECKED; +} + +void MenuRadioItem::setChecked() { + MenuRadioItem *parent = action->radioParent; + foreach(item, parent->action->items) { + CheckMenuRadioItem( + action->parent->action->menu, + item->object->id, item->object->id, item->object->id + (item != this), + MF_BYCOMMAND + ); + } +} diff --git a/snespurify/phoenix/windows/messagewindow.cpp b/snespurify/phoenix/windows/messagewindow.cpp new file mode 100755 index 00000000..b2439dc3 --- /dev/null +++ b/snespurify/phoenix/windows/messagewindow.cpp @@ -0,0 +1,41 @@ +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, UINT response) { + if(response == IDOK) return MessageWindow::Response::Ok; + if(response == IDCANCEL) return MessageWindow::Response::Cancel; + if(response == IDYES) return MessageWindow::Response::Yes; + if(response == IDNO) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONINFORMATION; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONQUESTION; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONWARNING; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONERROR; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} diff --git a/snespurify/phoenix/windows/object.cpp b/snespurify/phoenix/windows/object.cpp new file mode 100755 index 00000000..2a5b8785 --- /dev/null +++ b/snespurify/phoenix/windows/object.cpp @@ -0,0 +1,87 @@ +struct Object::Data { + unsigned id; + bool locked; +}; + +struct Font::Data { + HFONT font; +}; + +struct Action::Data { + Menu *parent; + HMENU parentMenu; + HMENU menu; + MenuRadioItem *radioParent; + array items; +}; + +struct Widget::Data { + HWND window; + HFONT font; +}; + +struct Window::Data { + HFONT defaultFont; + HBRUSH brush; + COLORREF brushColor; + HMENU menu; + HWND status; + unsigned width; + unsigned height; +}; + +struct Canvas::Data { + uint32_t *buffer; + BITMAPINFO bmi; + unsigned pitch; + unsigned width; + unsigned height; +}; + +struct ComboBox::Data { + unsigned selection; +}; + +struct EditBox::Data { + bool wordWrap; + unsigned x; + unsigned y; + unsigned width; + unsigned height; +}; + +struct HorizontalSlider::Data { + unsigned position; +}; + +struct ListBox::Data { + unsigned columns; + bool lostFocus; +}; + +struct RadioBox::Data { + Window *parentWindow; + RadioBox *parent; + array items; +}; + +struct VerticalSlider::Data { + unsigned position; +}; + +struct OS::Data { + nall::array objects; + HFONT proportionalFont; + HFONT monospaceFont; +}; + +void Object::unused() { +} + +Object::Object() { + OS::initialize(); + static unsigned guid = 100; + object = new Object::Data; + object->id = guid++; + object->locked = false; +} diff --git a/snespurify/phoenix/windows/phoenix.Manifest b/snespurify/phoenix/windows/phoenix.Manifest new file mode 100755 index 00000000..71013ffe --- /dev/null +++ b/snespurify/phoenix/windows/phoenix.Manifest @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/snespurify/phoenix/windows/phoenix.rc b/snespurify/phoenix/windows/phoenix.rc new file mode 100755 index 00000000..89fb8dc2 --- /dev/null +++ b/snespurify/phoenix/windows/phoenix.rc @@ -0,0 +1 @@ +1 24 "phoenix.Manifest" diff --git a/snespurify/phoenix/windows/progressbar.cpp b/snespurify/phoenix/windows/progressbar.cpp new file mode 100755 index 00000000..230f12b4 --- /dev/null +++ b/snespurify/phoenix/windows/progressbar.cpp @@ -0,0 +1,18 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + widget->window = CreateWindow( + PROGRESS_CLASS, L"", + WS_CHILD | WS_VISIBLE | PBS_SMOOTH, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SendMessage(widget->window, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); + SendMessage(widget->window, PBM_SETSTEP, MAKEWPARAM(1, 0), 0); +} + +unsigned ProgressBar::position() { + return SendMessage(widget->window, PBM_GETPOS, 0, 0); +} + +void ProgressBar::setPosition(unsigned position) { + SendMessage(widget->window, PBM_SETPOS, (WPARAM)position, 0); +} diff --git a/snespurify/phoenix/windows/radiobox.cpp b/snespurify/phoenix/windows/radiobox.cpp new file mode 100755 index 00000000..9c49297b --- /dev/null +++ b/snespurify/phoenix/windows/radiobox.cpp @@ -0,0 +1,42 @@ +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parentWindow = &parent; + radioBox->parent = this; + radioBox->parent->radioBox->items.append(this); + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_RADIOBUTTON, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); + setChecked(); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + radioBox->parentWindow = parent.radioBox->parentWindow; + radioBox->parent = parent.radioBox->parent; + radioBox->parent->radioBox->items.append(this); + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_RADIOBUTTON, + x, y, width, height, + GetParent(radioBox->parent->widget->window), (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(radioBox->parentWindow->window->defaultFont ? radioBox->parentWindow->window->defaultFont : OS::os->proportionalFont), 0); +} + +bool RadioBox::checked() { + return SendMessage(widget->window, BM_GETCHECK, 0, 0); +} + +void RadioBox::setChecked() { + foreach(item, radioBox->parent->radioBox->items) { + SendMessage(item->widget->window, BM_SETCHECK, (WPARAM)(item == this), 0); + } +} + +RadioBox::RadioBox() { + radioBox = new RadioBox::Data; +} diff --git a/snespurify/phoenix/windows/textbox.cpp b/snespurify/phoenix/windows/textbox.cpp new file mode 100755 index 00000000..e63f2dac --- /dev/null +++ b/snespurify/phoenix/windows/textbox.cpp @@ -0,0 +1,28 @@ +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +} + +string TextBox::text() { + unsigned length = GetWindowTextLength(widget->window); + wchar_t text[length + 1]; + GetWindowText(widget->window, text, length + 1); + text[length] = 0; + return utf8_t(text); +} + +void TextBox::setText(const string &text) { + object->locked = true; + SetWindowText(widget->window, utf16_t(text)); + object->locked = false; +} + +void TextBox::setEditable(bool editable) { + SendMessage(widget->window, EM_SETREADONLY, editable == false, 0); +} diff --git a/snespurify/phoenix/windows/verticalslider.cpp b/snespurify/phoenix/windows/verticalslider.cpp new file mode 100755 index 00000000..43024432 --- /dev/null +++ b/snespurify/phoenix/windows/verticalslider.cpp @@ -0,0 +1,25 @@ +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + widget->window = CreateWindow( + TRACKBAR_CLASS, L"", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_VERT, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); + SendMessage(widget->window, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3)); + setPosition(0); +} + +unsigned VerticalSlider::position() { + return SendMessage(widget->window, TBM_GETPOS, 0, 0); +} + +void VerticalSlider::setPosition(unsigned position) { + SendMessage(widget->window, TBM_SETPOS, (WPARAM)true, (LPARAM)(verticalSlider->position = position)); +} + +VerticalSlider::VerticalSlider() { + verticalSlider = new VerticalSlider::Data; +} diff --git a/snespurify/phoenix/windows/viewport.cpp b/snespurify/phoenix/windows/viewport.cpp new file mode 100755 index 00000000..21e38d79 --- /dev/null +++ b/snespurify/phoenix/windows/viewport.cpp @@ -0,0 +1,17 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + widget->window = CreateWindow( + L"phoenix_viewport", L"", + WS_CHILD | WS_VISIBLE | WS_DISABLED, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +uintptr_t Viewport::handle() { + return (uintptr_t)widget->window; +} + +static LRESULT CALLBACK Viewport_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/snespurify/phoenix/windows/widget.cpp b/snespurify/phoenix/windows/widget.cpp new file mode 100755 index 00000000..70ac5301 --- /dev/null +++ b/snespurify/phoenix/windows/widget.cpp @@ -0,0 +1,40 @@ +void Widget::setFont(Font &font) { + widget->font = font.font->font; + SendMessage(widget->window, WM_SETFONT, (WPARAM)font.font->font, 0); +} + +bool Widget::visible() { + return GetWindowLong(widget->window, GWL_STYLE) & WS_VISIBLE; +} + +void Widget::setVisible(bool visible) { + ShowWindow(widget->window, visible ? SW_SHOWNORMAL : SW_HIDE); +} + +bool Widget::enabled() { + return IsWindowEnabled(widget->window); +} + +void Widget::setEnabled(bool enabled) { + EnableWindow(widget->window, enabled); +} + +bool Widget::focused() { + return (GetForegroundWindow() == widget->window); +} + +void Widget::setFocused() { + if(visible() == false) setVisible(true); + SetFocus(widget->window); +} + +void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER); +} + +Widget::Widget() { + OS::os->objects.append(this); + widget = new Widget::Data; + widget->window = 0; + widget->font = OS::os->proportionalFont; +} diff --git a/snespurify/phoenix/windows/window.cpp b/snespurify/phoenix/windows/window.cpp new file mode 100755 index 00000000..5f86d547 --- /dev/null +++ b/snespurify/phoenix/windows/window.cpp @@ -0,0 +1,96 @@ +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { + widget->window = CreateWindowEx( + 0, L"phoenix_window", utf16_t(text), + WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, + x, y, width, height, + 0, 0, GetModuleHandle(0), 0 + ); + window->menu = CreateMenu(); + window->status = CreateWindowEx( + 0, STATUSCLASSNAME, L"", + WS_CHILD, + 0, 0, 0, 0, + widget->window, 0, GetModuleHandle(0), 0 + ); + //StatusBar will be capable of receiving tab focus if it is not disabled + SetWindowLongPtr(window->status, GWL_STYLE, GetWindowLong(window->status, GWL_STYLE) | WS_DISABLED); + resize(width, height); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +void Window::setDefaultFont(Font &font) { + window->defaultFont = font.font->font; +} + +void Window::setFont(Font &font) { + SendMessage(window->status, WM_SETFONT, (WPARAM)font.font->font, 0); +} + +Geometry Window::geometry() { + RECT position, size; + GetWindowRect(widget->window, &position); + GetClientRect(widget->window, &size); + if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { + RECT status; + GetClientRect(window->status, &status); + size.bottom -= status.bottom - status.top; + } + return Geometry(position.left, position.top, size.right, size.bottom); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + bool isVisible = visible(); + if(isVisible) setVisible(false); + SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER | SWP_FRAMECHANGED); + resize(width, height); + if(isVisible) setVisible(true); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + if(window->brush) DeleteObject(window->brush); + window->brushColor = RGB(red, green, blue); + window->brush = CreateSolidBrush(window->brushColor); +} + +void Window::setTitle(const string &text) { + SetWindowText(widget->window, utf16_t(text)); +} + +void Window::setStatusText(const string &text) { + SendMessage(window->status, SB_SETTEXT, 0, (LPARAM)(wchar_t*)utf16_t(text)); +} + +void Window::setMenuVisible(bool visible) { + SetMenu(widget->window, visible ? window->menu : 0); + resize(window->width, window->height); +} + +void Window::setStatusVisible(bool visible) { + ShowWindow(window->status, visible ? SW_SHOWNORMAL : SW_HIDE); + resize(window->width, window->height); +} + +Window::Window() { + window = new Window::Data; + window->defaultFont = 0; + window->brush = 0; +} + +void Window::resize(unsigned width, unsigned height) { + window->width = width; + window->height = height; + + SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + RECT rc; + GetClientRect(widget->window, &rc); + width += width - (rc.right - rc.left); + height += height - (rc.bottom - rc.top); + + if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { + GetClientRect(window->status, &rc); + height += rc.bottom - rc.top; + } + + SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + SetWindowPos(window->status, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); +} \ No newline at end of file diff --git a/snespurify/phoenix/windows/windows.cpp b/snespurify/phoenix/windows/windows.cpp new file mode 100755 index 00000000..697f8eec --- /dev/null +++ b/snespurify/phoenix/windows/windows.cpp @@ -0,0 +1,468 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +namespace phoenix { + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +OS::Data *OS::os = 0; +Window Window::None; + +static void OS_keyboardProc(HWND, UINT, WPARAM, LPARAM); +static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + +void OS::initialize() { + static bool initialized = false; + if(initialized == true) return; + initialized = true; + + InitCommonControls(); + CoInitialize(0); + + os = new OS::Data; + os->proportionalFont = Font_createFont("Tahoma", 8, false, false); + os->monospaceFont = Font_createFont("Courier New", 8, false, false); + + WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(2)); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = OS_windowProc; + wc.lpszClassName = L"phoenix_window"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Canvas_windowProc; + wc.lpszClassName = L"phoenix_canvas"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Label_windowProc; + wc.lpszClassName = L"phoenix_label"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Viewport_windowProc; + wc.lpszClassName = L"phoenix_viewport"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); +} + +bool OS::pending() { + MSG msg; + return PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE); +} + +void OS::run() { + while(pending()) { + MSG msg; + if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) { + OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +} + +void OS::main() { + MSG msg; + while(GetMessage(&msg, 0, 0, 0)) { + if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) { + OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +void OS::quit() { + PostQuitMessage(0); +} + +unsigned OS::desktopWidth() { + return GetSystemMetrics(SM_CXSCREEN); +} + +unsigned OS::desktopHeight() { + return GetSystemMetrics(SM_CYSCREEN); +} + +string OS::folderSelect(Window &parent, const string &path) { + wchar_t wfilename[PATH_MAX + 1] = L""; + BROWSEINFO bi; + bi.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + bi.pidlRoot = NULL; + bi.pszDisplayName = wfilename; + bi.lpszTitle = L""; + bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS; + bi.lpfn = NULL; + bi.lParam = 0; + bi.iImage = 0; + bool result = false; + LPITEMIDLIST pidl = SHBrowseForFolder(&bi); + if(pidl) { + if(SHGetPathFromIDList(pidl, wfilename)) { + result = true; + IMalloc *imalloc = 0; + if(SUCCEEDED(SHGetMalloc(&imalloc))) { + imalloc->Free(pidl); + imalloc->Release(); + } + } + } + if(result == false) return ""; + string name = utf8_t(wfilename); + name.transform("\\", "/"); + if(name.endswith("/") == false) name.append("/"); + return name; +} + +string OS::fileOpen(Window &parent, const string &filter, const string &path) { + string dir = path; + dir.replace("/", "\\"); + + string filterInfo; + lstring type; + type.split("\n", filter); + for(unsigned i = 0; i < type.size(); i++) { + lstring part; + part.split("\t", type[i]); + if(part.size() != 2) continue; + filterInfo.append(part[0]); + filterInfo.append(" ("); + filterInfo.append(part[1]); + filterInfo.append(")\t"); + part[1].replace(",", ";"); + filterInfo.append(part[1]); + filterInfo.append("\t"); + } + + utf16_t wfilter(filterInfo); + utf16_t wdir(dir); + wchar_t wfilename[PATH_MAX] = L""; + + wchar_t *p = wfilter; + while(*p != L'\0') { + if(*p == L'\t') *p = L'\0'; + p++; + } + + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + ofn.lpstrFilter = wfilter; + ofn.lpstrInitialDir = wdir; + ofn.lpstrFile = wfilename; + ofn.nMaxFile = PATH_MAX; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = L""; + + bool result = GetOpenFileName(&ofn); + if(result == false) return ""; + string name = utf8_t(wfilename); + name.transform("\\", "/"); + return name; +} + +string OS::fileSave(Window &parent, const string &filter, const string &path) { + string dir = path; + dir.replace("/", "\\"); + + string filterInfo; + lstring type; + type.split("\n", filter); + for(unsigned i = 0; i < type.size(); i++) { + lstring part; + part.split("\t", type[i]); + if(part.size() != 2) continue; + filterInfo.append(part[0]); + filterInfo.append(" ("); + filterInfo.append(part[1]); + filterInfo.append(")\t"); + part[1].replace(",", ";"); + filterInfo.append(part[1]); + filterInfo.append("\t"); + } + + utf16_t wfilter(filterInfo); + utf16_t wdir(dir); + wchar_t wfilename[PATH_MAX] = L""; + + wchar_t *p = wfilter; + while(*p != L'\0') { + if(*p == L'\t') *p = L'\0'; + p++; + } + + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + ofn.lpstrFilter = wfilter; + ofn.lpstrInitialDir = wdir; + ofn.lpstrFile = wfilename; + ofn.nMaxFile = PATH_MAX; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = L""; + + bool result = GetSaveFileName(&ofn); + if(result == false) return ""; + string name = utf8_t(wfilename); + name.transform("\\", "/"); + return name; +} + +static void OS_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch(msg) { + case WM_KEYDOWN: { + GUITHREADINFO info; + memset(&info, 0, sizeof(GUITHREADINFO)); + info.cbSize = sizeof(GUITHREADINFO); + GetGUIThreadInfo(GetCurrentThreadId(), &info); + Object *object_ptr = (Object*)GetWindowLongPtr(info.hwndFocus, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + ListBox &listBox = (ListBox&)*object_ptr; + if(wparam == VK_RETURN) { + if(listBox.onActivate) listBox.onActivate(); + } + } else if(dynamic_cast(object_ptr)) { + TextBox &textBox = (TextBox&)*object_ptr; + if(wparam == VK_RETURN) { + if(textBox.onActivate) textBox.onActivate(); + } + } + } + } + } +} + +static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + Object *object_ptr = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(!object_ptr || !dynamic_cast(object_ptr)) return DefWindowProc(hwnd, msg, wparam, lparam); + Window &window = (Window&)*object_ptr; + + switch(msg) { + case WM_CLOSE: { + if(window.onClose) { + if(window.onClose()) window.setVisible(false); + } else { + window.setVisible(false); + } + return TRUE; + } + + case WM_ERASEBKGND: { + if(window.window->brush == 0) break; + RECT rc; + GetClientRect(window.widget->window, &rc); + PAINTSTRUCT ps; + BeginPaint(window.widget->window, &ps); + FillRect(ps.hdc, &rc, window.window->brush); + EndPaint(window.widget->window, &ps); + return TRUE; + } + + case WM_CTLCOLORBTN: + case WM_CTLCOLORSTATIC: { + Object *object_ptr = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA); + if(object_ptr && window.window->brush) { + HDC hdc = (HDC)wparam; + SetBkColor((HDC)wparam, window.window->brushColor); + return (INT_PTR)window.window->brush; + } + } + + case WM_COMMAND: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control == 0) { + Object *object_ptr = (Object*)OS::findObject(id); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + MenuItem &menuItem = (MenuItem&)*object_ptr; + if(menuItem.onTick) menuItem.onTick(); + } else if(dynamic_cast(object_ptr)) { + MenuCheckItem &menuCheckItem = (MenuCheckItem&)*object_ptr; + menuCheckItem.setChecked(!menuCheckItem.checked()); + if(menuCheckItem.onTick) menuCheckItem.onTick(); + } else if(dynamic_cast(object_ptr)) { + MenuRadioItem &menuRadioItem = (MenuRadioItem&)*object_ptr; + if(menuRadioItem.checked() == false) { + menuRadioItem.setChecked(); + if(menuRadioItem.onTick) menuRadioItem.onTick(); + } + } + } + } else { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + Button &button = (Button&)*object_ptr; + if(button.onTick) button.onTick(); + } else if(dynamic_cast(object_ptr)) { + CheckBox &checkBox = (CheckBox&)*object_ptr; + checkBox.setChecked(!checkBox.checked()); + if(checkBox.onTick) checkBox.onTick(); + } else if(dynamic_cast(object_ptr)) { + ComboBox &comboBox = (ComboBox&)*object_ptr; + if(HIWORD(wparam) == CBN_SELCHANGE) { + if(comboBox.comboBox->selection != comboBox.selection()) { + comboBox.comboBox->selection = comboBox.selection(); + if(comboBox.onChange) comboBox.onChange(); + } + } + } else if(dynamic_cast(object_ptr)) { + EditBox &editBox = (EditBox&)*object_ptr; + if(HIWORD(wparam) == EN_CHANGE) { + if(editBox.object->locked == false && editBox.onChange) editBox.onChange(); + } + } else if(dynamic_cast(object_ptr)) { + RadioBox &radioBox = (RadioBox&)*object_ptr; + if(radioBox.checked() == false) { + radioBox.setChecked(); + if(radioBox.onTick) radioBox.onTick(); + } + } else if(dynamic_cast(object_ptr)) { + TextBox &textBox = (TextBox&)*object_ptr; + if(HIWORD(wparam) == EN_CHANGE) { + if(textBox.object->locked == false && textBox.onChange) textBox.onChange(); + } + } + } + } + } + + case WM_NOTIFY: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + ListBox &listBox = (ListBox&)*object_ptr; + LPNMHDR nmhdr = (LPNMHDR)lparam; + LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam; + + if(nmhdr->code == LVN_ITEMCHANGED && (nmlistview->uChanged & LVIF_STATE)) { + unsigned imagemask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1; + if(imagemask == 0 || imagemask == 1) { + if(listBox.object->locked == false && listBox.onTick) listBox.onTick(nmlistview->iItem); + } else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) { + listBox.listBox->lostFocus = true; + } else { + if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) { + if(listBox.onChange) listBox.onChange(); + } else if(listBox.listBox->lostFocus == false && listBox.selection() == false) { + if(listBox.onChange) listBox.onChange(); + } + listBox.listBox->lostFocus = false; + } + } else if(nmhdr->code == LVN_ITEMACTIVATE) { + if(listBox.onActivate) listBox.onActivate(); + } + } + } + } + } + + case WM_HSCROLL: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + HorizontalSlider &horizontalSlider = (HorizontalSlider&)*object_ptr; + if(horizontalSlider.horizontalSlider->position != horizontalSlider.position()) { + horizontalSlider.horizontalSlider->position = horizontalSlider.position(); + if(horizontalSlider.onChange) horizontalSlider.onChange(); + } + } + } + } + } + + case WM_VSCROLL: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + VerticalSlider &verticalSlider = (VerticalSlider&)*object_ptr; + if(verticalSlider.verticalSlider->position != verticalSlider.position()) { + verticalSlider.verticalSlider->position = verticalSlider.position(); + if(verticalSlider.onChange) verticalSlider.onChange(); + } + } + } + } + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +Object* OS::findObject(unsigned id) { + foreach(object, os->objects) { if(object->object->id == id) return object; } + return 0; +} + +} diff --git a/snespurify/phoenix/windows/windows.hpp b/snespurify/phoenix/windows/windows.hpp new file mode 100755 index 00000000..41807a94 --- /dev/null +++ b/snespurify/phoenix/windows/windows.hpp @@ -0,0 +1,288 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + struct Data; + Data *object; +private: + virtual void unused(); +}; + +struct Geometry { + unsigned x, y; + unsigned width, height; + inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const nall::string &name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + virtual bool enabled() = 0; + virtual void setEnabled(bool enabled = true) = 0; + Action(); +//private: + struct Data; + Data *action; +}; + +struct Menu : Action { + void create(Window &parent, const nall::string &text); + void create(Menu &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuSeparator : Action { + void create(Menu &parent); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(bool checked = true); +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const nall::string &text); + void create(MenuRadioItem &parent, const nall::string &text); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(); +}; + +struct Widget : Object { + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool focused(); + void setFocused(); + virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setDefaultFont(Font &font); + void setFont(Font &font); + Geometry geometry(); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const nall::string &text); + void setStatusText(const nall::string &text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + Window(); +//private: + struct Data; + Data *window; + static Window None; + void resize(unsigned width, unsigned height); +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(bool checked = true); +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void reset(); + void addItem(const nall::string &text); + unsigned selection(); + void setSelection(unsigned item); + ComboBox(); +//private: + struct Data; + Data *comboBox; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + nall::string getText(); + void setText(const nall::string &text); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + EditBox(); +//private: + struct Data; + Data *editBox; +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + HorizontalSlider(); +//private: + struct Data; + Data *horizontalSlider; +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setText(const nall::string &text); +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); + void reset(); + void resizeColumnsToContent(); + void addItem(const nall::string &text); + void setItem(unsigned row, const nall::string &text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + unsigned position(); + void setPosition(unsigned position); +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + bool checked(); + void setChecked(); + RadioBox(); +//private: + struct Data; + Data *radioBox; +}; + +struct TextBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); + nall::string text(); + void setText(const nall::string &text); + void setEditable(bool editable = true); +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + VerticalSlider(); +//private: + struct Data; + Data *verticalSlider; +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + static bool pending(); + static void run(); + static void main(); + static void quit(); + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static nall::string folderSelect(Window &parent, const nall::string &path = ""); + static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = ""); + static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = ""); +//private: + static void initialize(); + struct Data; + static Data *os; + static Object* findObject(unsigned id); + friend class Object; +}; + +}; diff --git a/snespurify/snespurify.cpp b/snespurify/snespurify.cpp new file mode 100755 index 00000000..13c55278 --- /dev/null +++ b/snespurify/snespurify.cpp @@ -0,0 +1,400 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +#include +using namespace phoenix; + +static const char applicationTitle[] = "snespurify v05"; + +struct Application : Window { + Font font; + Label pathLabel; + TextBox pathBox; + Button pathScan; + Button pathBrowse; + ListBox fileList; + Button selectAll; + Button unselectAll; + Button fixSelected; + + struct FileInfo { + string filename; + string problem; + string solution; + }; + linear_vector fileInfo; + lstring errors; + + void main(); + void enable(bool); + void scan(); + void scan(const string &pathname); + void analyze(const string &filename); + void repair(); + void createPatch(const string &filename); +} application; + +void Application::main() { + #if defined(PLATFORM_WIN) + font.create("Tahoma", 8); + #else + font.create("Sans", 8); + #endif + create(128, 128, 600, 360, applicationTitle); + setDefaultFont(font); + + unsigned x = 5, y = 5, width = 600, height = 25; + pathLabel.create(*this, x, y, 80, height, "Path to scan:"); + pathBox.create(*this, x + 85, y, 335, height); + pathScan.create(*this, x + 425, y, 80, height, "Scan"); + pathBrowse.create(*this, x + 510, y, 80, height, "Browse ..."); y += height + 5; + fileList.create(*this, x, y, 590, 290, "Filename\tProblem\tSolution"); y += 290 + 5; + selectAll.create(*this, x, y, 80, height, "Select All"); + unselectAll.create(*this, x + 85, y, 80, height, "Clear All"); + fixSelected.create(*this, 595 - 80, y, 80, height, "Correct"); y += height + 5; + fileList.setHeaderVisible(); + fileList.setCheckable(); + + onClose = []() { + OS::quit(); + return true; + }; + + pathBox.onActivate = pathScan.onTick = { &Application::scan, this }; + + pathBrowse.onTick = []() { + string pathname = OS::folderSelect(application); + if(pathname != "") application.pathBox.setText(pathname); + }; + + selectAll.onTick = []() { + unsigned count = application.fileInfo.size(); + for(unsigned i = 0; i < count; i++) application.fileList.setChecked(i, true); + }; + + unselectAll.onTick = []() { + unsigned count = application.fileInfo.size(); + for(unsigned i = 0; i < count; i++) application.fileList.setChecked(i, false); + }; + + fixSelected.onTick = { &Application::repair, this }; + + setVisible(); +} + +//don't allow actions to be taken while files are being scanned or fixed +void Application::enable(bool state) { + if(state == false) { + setTitle({ applicationTitle, " - working ..." }); + } else { + setTitle(applicationTitle); + } + + pathBox.setEnabled(state); + pathScan.setEnabled(state); + pathBrowse.setEnabled(state); + fileList.setEnabled(state); + selectAll.setEnabled(state); + unselectAll.setEnabled(state); + fixSelected.setEnabled(state); +} + +void Application::scan() { + fileInfo.reset(); + fileList.reset(); + + string pathname = pathBox.text(); + if(pathname == "") { + MessageWindow::information(application, "Please specify a directory to scan"); + return; + } + pathname.transform("\\", "/"); + if(pathname.endswith("/") == false) pathname.append("/"); + if(directory::exists(pathname) == false) { + MessageWindow::warning(application, "Specified directory does not exist"); + return; + } + + enable(false); + scan(pathname); + enable(true); + + if(fileInfo.size() == 0) { + MessageWindow::information(application, "All files are correct"); + return; + } + + unsigned counter = 0; + foreach(info, fileInfo) { + fileList.addItem({ notdir(info.filename), "\t", info.problem, "\t", info.solution }); + fileList.setChecked(counter++, true); + } + fileList.resizeColumnsToContent(); +} + +void Application::scan(const string &pathname) { + lstring files = directory::files(pathname); + foreach(file, files) { + OS::run(); + analyze({ pathname, file }); + } + + //recursion + lstring folders = directory::folders(pathname); + foreach(folder, folders) scan({ pathname, folder }); +} + +void Application::analyze(const string &filename) { + if(file::exists(filename) == false) return; + + if(filename.iendswith(".sfc") || filename.iendswith(".bs") || filename.iendswith(".st") + || filename.iendswith(".gb") || filename.iendswith(".gbc") || filename.iendswith(".sgb") + || filename.iendswith(".smc") || filename.iendswith(".swc") || filename.iendswith(".fig") || filename.iendswith(".ufo") + || filename.iendswith(".gd3") || filename.iendswith(".gd7") || filename.iendswith(".dx2") || filename.iendswith(".mgd") + || filename.iendswith(".mgh") || filename.iendswith(".048") || filename.iendswith(".058") || filename.iendswith(".068") + || filename.iendswith(".078") || filename.iendswith(".usa") || filename.iendswith(".eur") || filename.iendswith(".jap") + || filename.iendswith(".aus") || filename.iendswith(".bsx") + ) { + filemap map(filename, filemap::mode::read); + unsigned filesize = map.size(); + snes_information information(map.data(), filesize); + + //the ordering of rules is very important: + //patches need to be created prior to removal of headers + //headers need to be removed prior to renaming files (so header removal has correct filename) + //etc. + switch(information.type) { + case snes_information::TypeNormal: + case snes_information::TypeBsxSlotted: + case snes_information::TypeBsxBios: + case snes_information::TypeSufamiTurboBios: + case snes_information::TypeSuperGameBoy1Bios: + case snes_information::TypeSuperGameBoy2Bios: { + string ipsName = { nall::basename(filename), ".ips" }; + string upsName = { nall::basename(filename), ".ups" }; + if(file::exists(ipsName) == true && file::exists(upsName) == false) { + FileInfo info; + info.filename = filename; + info.problem = "Unsupported patch format"; + info.solution = "Create UPS patch"; + fileInfo.append(info); + } + + if((filesize & 0x7fff) == 512) { + FileInfo info; + info.filename = filename; + info.problem = "Copier header present"; + info.solution = "Remove copier header"; + fileInfo.append(info); + } + + if(filename.endswith(".sfc") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .sfc"; + fileInfo.append(info); + } + + break; + } + + case snes_information::TypeBsx: { + if((filesize & 0x7fff) == 512) { + FileInfo info; + info.filename = filename; + info.problem = "Copier header present"; + info.solution = "Remove copier header"; + fileInfo.append(info); + } + + if(filename.endswith(".bs") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .bs"; + fileInfo.append(info); + } + + break; + } + + case snes_information::TypeSufamiTurbo: { + if(filename.endswith(".st") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .st"; + fileInfo.append(info); + } + + break; + } + + case snes_information::TypeGameBoy: { + if(filename.endswith(".gb") == false && filename.endswith(".gbc") == false && filename.endswith(".sgb") == false) { + FileInfo info; + info.filename = filename; + info.problem = "Wrong file extension"; + info.solution = "Rename to .gb"; + fileInfo.append(info); + } + + break; + } + } + } +} + +void Application::repair() { + enable(false); + errors.reset(); + + for(unsigned n = 0; n < fileInfo.size(); n++) { + if(fileList.checked(n) == false) continue; + OS::run(); + + FileInfo &info = fileInfo[n]; + if(info.solution == "Create UPS patch") { + createPatch(info.filename); + } else if(info.solution == "Remove copier header") { + file fp; + if(fp.open(info.filename, file::mode::read)) { + unsigned size = fp.size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + if(fp.open(info.filename, file::mode::write)) { + fp.write(data + 512, size - 512); + fp.close(); + } + } + } else if(info.solution == "Rename to .sfc") { + rename(info.filename, string(nall::basename(info.filename), ".sfc")); + } else if(info.solution == "Rename to .bs") { + rename(info.filename, string(nall::basename(info.filename), ".bs")); + } else if(info.solution == "Rename to .st") { + rename(info.filename, string(nall::basename(info.filename), ".st")); + } else if(info.solution == "Rename to .gb") { + rename(info.filename, string(nall::basename(info.filename), ".gb")); + } + } + + if(errors.size() == 0) { + MessageWindow::information(application, "Selected problems have been corrected"); + } else { + string output; + for(unsigned i = 0; i < 3 && i < errors.size(); i++) output.append(string(errors[i], "\n")); + if(errors.size() > 3) output.append("\n(too many errors to show ...)"); + MessageWindow::information(application, { + "Selected problems have been corrected, but there were errors:\n\n", + output + }); + } + + fileInfo.reset(); + fileList.reset(); + enable(true); +} + +void Application::createPatch(const string &filename) { + string ipsName = { nall::basename(filename), ".ips" }; + string upsName = { nall::basename(filename), ".ups" }; + + file fp; + if(fp.open(filename, file::mode::read)) { + unsigned isize = fp.size(); + uint8_t *idata = new uint8_t[isize]; + fp.read(idata, isize); + fp.close(); + + fp.open(ipsName, file::mode::read); + unsigned psize = fp.size(); + uint8_t *pdata = new uint8_t[psize]; + fp.read(pdata, psize); + fp.close(); + + if(psize >= 8 + && pdata[0] == 'P' && pdata[1] == 'A' && pdata[2] == 'T' && pdata[3] == 'C' && pdata[4] == 'H' + && pdata[psize - 3] == 'E' && pdata[psize - 2] == 'O' && pdata[psize - 1] == 'F') { + unsigned osize = 0; + //no way to determine how big IPS output will be, allocate max size IPS patches support -- 16MB + uint8_t *odata = new uint8_t[16 * 1024 * 1024](); + memcpy(odata, idata, isize); + + unsigned offset = 5; + while(offset < psize - 3) { + unsigned addr; + addr = pdata[offset++] << 16; + addr |= pdata[offset++] << 8; + addr |= pdata[offset++] << 0; + + unsigned size; + size = pdata[offset++] << 8; + size |= pdata[offset++] << 0; + + if(size == 0) { + //RLE + size = pdata[offset++] << 8; + size |= pdata[offset++] << 0; + + for(unsigned n = addr; n < addr + size;) { + odata[n++] = pdata[offset]; + if(n > osize) osize = n; + } + offset++; + } else { + for(unsigned n = addr; n < addr + size;) { + odata[n++] = pdata[offset++]; + if(n > osize) osize = n; + } + } + } + + osize = max(isize, osize); + bool hasHeader = ((isize & 0x7fff) == 512); + + uint8_t *widata = idata; + unsigned wisize = isize; + if(hasHeader) { + //remove copier header for UPS patch creation + widata += 512; + wisize -= 512; + } + + uint8_t *wodata = odata; + unsigned wosize = osize; + if(hasHeader) { + //remove copier header for UPS patch creation + wodata += 512; + wosize -= 512; + } + + ups patcher; + if(patcher.create(widata, wisize, wodata, wosize, upsName) != ups::result::success) { + errors.append({ "Failed to create UPS patch: ", upsName, "\n" }); + } + + delete[] odata; + } else { + errors.append({ "IPS patch is invalid: ", ipsName, "\n" }); + } + + delete[] idata; + delete[] pdata; + } +} + +int main() { + application.main(); + OS::main(); + return 0; +} diff --git a/snespurify/sync.sh b/snespurify/sync.sh new file mode 100755 index 00000000..853434d2 --- /dev/null +++ b/snespurify/sync.sh @@ -0,0 +1,10 @@ +synchronize() { + if [ -d ../"$1" ]; then + test -d "$1" && rm -r "$1" + cp -r ../"$1" ./"$1" + fi +} + +synchronize "nall" +synchronize "phoenix" +rm -r phoenix/test*