From 67318297dd444a771225e211f851a4b0ea9453c3 Mon Sep 17 00:00:00 2001 From: byuu Date: Sun, 18 Jan 2009 10:21:22 +0000 Subject: [PATCH] Update to bsnes v039 release. Changelog: - Recovered ~10% speed loss from last release via S-CPU IRQ timing optimizations - Implemented O(1) binary-heap priority queue for event scheduling - Fixed a bug where BS-X slotted carts were never mapping SRAM - Fixed a bug where invalid controller input was always being allowed - Fixed all compilation warnings with GCC 4.3 and Visual C++ 9.0 - Added advanced options to control S-CPU ALU hardware delays - S-RTC and SPC7110 timers updated to handle time_t overflow (Y2k38) gracefully - Cheat codes can now have multiple codes per entry, and multiple lines per description - Rewrote config file parser; removed config/ class from emulator core - Windows: added 256x256 image to program icon set - Linux: fixed Xorg keysym mapping, key names should show correctly in all cases now - UI: updated video panel, added fullscreen-on-startup and NTSC merge fields options - UI: simplified audio panel - UI: boolean options on advanced panel can be toggled via double-click - Lots of code cleanup, especially for S-CPU IRQ handling and nall template library --- bsnes.exe | Bin 374784 -> 0 bytes demo_irqtest.smc | Bin 131584 -> 0 bytes demo_nmitest.smc | Bin 131584 -> 0 bytes irq.smc | Bin 131584 -> 0 bytes nmi.smc | Bin 131072 -> 0 bytes src/Makefile | 24 +- src/base.hpp | 7 +- src/cart/cart.cpp | 52 +- src/cart/cart.hpp | 56 +- src/cart/cart_bsc.cpp | 2 +- src/cart/cart_bsx.cpp | 2 +- src/cart/cart_file.cpp | 18 +- src/cart/cart_header.cpp | 60 +- src/cart/cart_load.cpp | 2 +- src/cart/cart_normal.cpp | 2 +- src/cart/cart_st.cpp | 2 +- src/cc.bat | 4 +- src/cheat/cheat.cpp | 491 ++++++------ src/cheat/cheat.hpp | 43 +- src/chip/bsx/bsx.cpp | 4 +- src/chip/bsx/bsx.hpp | 16 +- src/chip/bsx/bsx_base.cpp | 8 +- src/chip/bsx/bsx_cart.cpp | 8 +- src/chip/bsx/bsx_flash.cpp | 8 +- src/chip/cx4/cx4.cpp | 5 +- src/chip/cx4/cx4.hpp | 4 +- src/chip/cx4/cx4data.cpp | 2 +- src/chip/cx4/cx4fn.cpp | 2 +- src/chip/cx4/cx4oam.cpp | 2 +- src/chip/cx4/cx4ops.cpp | 2 +- src/chip/dsp1/dsp1.cpp | 8 +- src/chip/dsp1/dsp1.hpp | 4 +- src/chip/dsp1/dsp1emu.cpp | 2 +- src/chip/dsp2/dsp2.cpp | 7 +- src/chip/dsp2/dsp2.hpp | 12 +- src/chip/dsp2/dsp2_op.cpp | 2 +- src/chip/dsp3/dsp3.cpp | 5 +- src/chip/dsp3/dsp3.hpp | 4 +- src/chip/dsp3/dsp3emu.c | 2 +- src/chip/dsp4/dsp4.cpp | 5 +- src/chip/dsp4/dsp4.hpp | 4 +- src/chip/dsp4/dsp4emu.c | 2 +- src/chip/obc1/obc1.cpp | 2 + src/chip/sdd1/sdd1.cpp | 8 +- src/chip/sdd1/sdd1emu.cpp | 2 +- src/chip/spc7110/spc7110.cpp | 62 +- src/chip/spc7110/spc7110.hpp | 8 +- src/chip/srtc/srtc.cpp | 45 +- src/chip/st010/st010.cpp | 5 +- src/chip/st010/st010.hpp | 4 +- src/chip/st010/st010_op.cpp | 2 +- src/clean.bat | 2 +- src/config/config.cpp | 110 --- src/config/config.hpp | 38 - src/cpu/scpu/core/core.cpp | 24 +- src/cpu/scpu/core/op_misc.b | 6 +- src/cpu/scpu/core/op_misc.cpp | 9 +- src/cpu/scpu/core/op_pc.cpp | 3 + src/cpu/scpu/core/op_read.cpp | 3 + src/cpu/scpu/core/op_rmw.cpp | 3 + src/cpu/scpu/core/op_write.cpp | 3 + src/cpu/scpu/deltaqueue.cpp | 88 --- src/cpu/scpu/dma/dma.cpp | 8 +- src/cpu/scpu/dma/dma.hpp | 2 +- src/cpu/scpu/mmio/mmio.cpp | 1064 +++++++++++++-------------- src/cpu/scpu/mmio/mmio.hpp | 4 +- src/cpu/scpu/scpu.cpp | 12 +- src/cpu/scpu/scpu.hpp | 25 +- src/cpu/scpu/timing/event.cpp | 14 +- src/cpu/scpu/timing/irq.cpp | 101 +-- src/cpu/scpu/timing/joypad.cpp | 2 +- src/cpu/scpu/timing/timing.cpp | 54 +- src/cpu/scpu/timing/timing.hpp | 11 +- src/data/bsnes.ico | Bin 26694 -> 22071 bytes src/data/controller.h | 5 + src/interface.hpp | 11 +- src/lib/hiro/win/editbox.cpp | 45 ++ src/lib/hiro/win/editbox.hpp | 5 + src/lib/hiro/win/hiro.cpp | 3 +- src/lib/hiro/win/hiro.hpp | 2 + src/lib/libfilter/ntsc.hpp | 3 +- src/lib/nall/algorithm.hpp | 34 +- src/lib/nall/any.hpp | 108 ++- src/lib/nall/array.hpp | 139 ++-- src/lib/nall/base64.hpp | 136 ++-- src/lib/nall/bit.hpp | 65 +- src/lib/nall/config.hpp | 292 +++----- src/lib/nall/config.txt | 176 +++++ src/lib/nall/crc32.hpp | 114 ++- src/lib/nall/detect.hpp | 2 +- src/lib/nall/dictionary.hpp | 106 ++- src/lib/nall/endian.hpp | 2 +- src/lib/nall/file.hpp | 380 +++++----- src/lib/nall/filemap.hpp | 292 ++++---- src/lib/nall/function.hpp | 136 ++-- src/lib/nall/input.hpp | 292 ++++---- src/lib/nall/lzss.hpp | 122 ++- src/lib/nall/modulo.hpp | 39 - src/lib/nall/moduloarray.hpp | 36 + src/lib/nall/new.hpp | 10 +- src/lib/nall/platform.hpp | 4 +- src/lib/nall/priorityqueue.hpp | 94 +++ src/lib/nall/serial.hpp | 128 ++-- src/lib/nall/sort.hpp | 68 +- src/lib/nall/static.hpp | 24 +- src/lib/nall/stdint.hpp | 22 +- src/lib/nall/string.cpp | 2 +- src/lib/nall/string.hpp | 68 +- src/lib/nall/string/class.cpp | 2 +- src/lib/nall/string/compare.cpp | 2 +- src/lib/nall/string/convert.cpp | 2 +- src/lib/nall/string/match.cpp | 2 +- src/lib/nall/string/math.cpp | 2 +- src/lib/nall/string/replace.cpp | 2 +- src/lib/nall/string/split.cpp | 2 +- src/lib/nall/string/strl.cpp | 2 +- src/lib/nall/string/trim.cpp | 2 +- src/lib/nall/traits.hpp | 134 ++-- src/lib/nall/ups.hpp | 320 ++++---- src/lib/nall/utf8.hpp | 94 +-- src/lib/nall/utility.hpp | 45 +- src/lib/nall/varint.hpp | 150 ++-- src/lib/nall/vector.hpp | 270 +++---- src/lib/ruby/input/directinput.cpp | 1 + src/lib/sync.bat | 5 + src/lib/tool/opgen_switch.cpp | 11 +- src/memory/smemory/smemory.cpp | 4 +- src/ppu/bppu/bppu.cpp | 10 +- src/ppu/bppu/bppu.hpp | 17 +- src/ppu/bppu/bppu_mmio.cpp | 4 +- src/ppu/bppu/bppu_render_addsub.cpp | 18 +- src/ppu/bppu/bppu_render_bg.cpp | 24 +- src/ppu/bppu/bppu_render_line.cpp | 4 +- src/ppu/bppu/bppu_render_mode7.cpp | 2 +- src/ppu/bppu/bppu_render_oam.cpp | 22 +- src/ppu/counter.cpp | 14 +- src/ppu/counter.hpp | 28 +- src/ppu/ppu.cpp | 1 - src/ppu/ppu.hpp | 6 +- src/reader/reader.cpp | 3 +- src/smp/ssmp/core/op_misc.cpp | 3 + src/smp/ssmp/core/op_mov.cpp | 3 + src/smp/ssmp/core/op_pc.cpp | 3 + src/smp/ssmp/core/op_read.cpp | 3 + src/smp/ssmp/core/op_rmw.cpp | 3 + src/smp/ssmp/memory/memory.cpp | 4 +- src/smp/ssmp/memory/memory.hpp | 12 +- src/smp/ssmp/ssmp.cpp | 4 +- src/smp/ssmp/ssmp.hpp | 1 - src/smp/ssmp/timing/timing.cpp | 6 +- src/smp/ssmp/timing/timing.hpp | 4 +- src/snes/scheduler/scheduler.cpp | 102 +-- src/snes/snes.cpp | 48 +- src/snes/snes.hpp | 41 +- src/snes/tracer/tracer.cpp | 180 ++--- src/ui/base/main.cpp | 42 +- src/ui/base/textview.cpp | 2 +- src/ui/config.cpp | 491 ++++++------ src/ui/event/debugger.cpp | 10 +- src/ui/event/event.cpp | 145 ++-- src/ui/event/event.hpp | 18 +- src/ui/inputdevices.cpp | 106 +-- src/ui/inputui.cpp | 34 +- src/ui/interface.cpp | 23 +- src/ui/loader/bsxloader.cpp | 8 +- src/ui/loader/stloader.cpp | 10 +- src/ui/main.cpp | 33 +- src/ui/settings/advanced.cpp | 115 ++- src/ui/settings/advanced.hpp | 15 +- src/ui/settings/audiosettings.cpp | 44 +- src/ui/settings/cheateditor.cpp | 247 +++++-- src/ui/settings/cheateditor.hpp | 37 +- src/ui/settings/driverselect.cpp | 20 +- src/ui/settings/inputconfig.cpp | 16 +- src/ui/settings/inputconfig.hpp | 2 +- src/ui/settings/pathsettings.cpp | 30 +- src/ui/settings/settings.cpp | 6 +- src/ui/settings/videosettings.cpp | 121 ++- src/ui/settings/videosettings.hpp | 9 +- src/ui/status.cpp | 2 +- src/ui/ui.cpp | 46 +- test_hdma.smc | Bin 131072 -> 0 bytes test_hdmasync.smc | Bin 131072 -> 0 bytes test_hdmatiming.smc | Bin 131072 -> 0 bytes test_irq.smc | Bin 131072 -> 0 bytes test_irq4200.smc | Bin 131072 -> 0 bytes test_irq4209.smc | Bin 131072 -> 0 bytes test_irqb.smc | Bin 131072 -> 0 bytes test_nmi.smc | Bin 131072 -> 0 bytes 189 files changed, 4541 insertions(+), 4285 deletions(-) delete mode 100644 bsnes.exe delete mode 100644 demo_irqtest.smc delete mode 100644 demo_nmitest.smc delete mode 100644 irq.smc delete mode 100644 nmi.smc delete mode 100644 src/config/config.cpp delete mode 100644 src/config/config.hpp delete mode 100644 src/cpu/scpu/deltaqueue.cpp create mode 100644 src/lib/nall/config.txt delete mode 100644 src/lib/nall/modulo.hpp create mode 100644 src/lib/nall/moduloarray.hpp create mode 100644 src/lib/nall/priorityqueue.hpp delete mode 100644 test_hdma.smc delete mode 100644 test_hdmasync.smc delete mode 100644 test_hdmatiming.smc delete mode 100644 test_irq.smc delete mode 100644 test_irq4200.smc delete mode 100644 test_irq4209.smc delete mode 100644 test_irqb.smc delete mode 100644 test_nmi.smc diff --git a/bsnes.exe b/bsnes.exe deleted file mode 100644 index 848cfe095e3dd1bf159cf04654d2a2cc71de2dc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374784 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%0AXeC}z!J&f!G!iYw*t7-RTtMYtre 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 diff --git a/demo_irqtest.smc b/demo_irqtest.smc deleted file mode 100644 index 384ae3c3ef997ecb11f7134dd999165865da958b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131584 zcmeIyJ&0UY6ae7+X7*>SoQQ>;?`dHoiUSrl5r+XGf+8kcY$HsuMaooR3&F_-K_UB7 z%Yg7?g(qxjX|vtNCbe2f3X7z0+c4sI@12>=Y%rZgAm3y6?VWSZJ@4FaW;O)Z^DOYn z1f`8#S%G_-oX( z^WyMUvyvC@>@=%+aj?^@<;9z~$5qvY-Pfw-i*$Q2_8I+izl@LDPMKFzFsEmkqn8HV zNxbzY!+g)L)AF1C>7)GT-lO5sd)3kV)x-FEsd#u?{&>I4q02FJc`0;Z&~2ADN9Doj zXucfABNXun2cvo=#Dg~FZWyHGr2J|0$4WSDe|$5YF4okYRB)E-%yaFW>!#eSZ!d3I zS+vcWxN8<(U4$>po6W*&^YG&Fv*`Lf8fo&pag^boAuju~sq61AkWGkGXB~KEIQgz-vj4$Sm`f+(&e&333)!n;4hj4N+SEO(}W~!Tw)!$A%KTqwK zPWekyY&NZ?y_hf7Y-hWeI9_=&Q(sTpXZgP_OUJE>0-SbeRCUzesg`v}Dbi+8PFEzZ(0wwzhZnfyI_ zBJ*-?xsh6)&ossEyH)HFf9mz8ZC{%0sgGEyS(^XsVZHRR$j8ft^?&urHSz0uHqdP9 z3~|2E+~E94^Y-EM{O{J;2|QB*zlZKKHIHEk5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!CviA@I@F&&rJtuWgi9KmDX!T)kGO9YPVpbmk#D%0nS1l*&CCZEf_9Mw7Eku_;W&G@S%-cd@;b}w zUS2Ph^{ukLT{b)0p={=NLf9y-ZPjJjgzXz;^Ste@#HH%zK@m4@wky}$U|Q~0Mpyg! zN$i$R2B}ZK+v)3-qvJH}9}h-%%F*5OFn(`lhkM2QgCd2NV`zCMl=t(w2P;*vUXAkp zQag=f7kbuCi`1vU;9FQ3eyZMvJOUC#WOoS2qsOm5#SoXJR4rd^jksdwqm?7y=*k(qZdZk<}jGV!M0CoMWm zFFW(x2oj=sSNC7D-41AQk^g$$PJjRb0t5&UI2VD>AwQRM9ZG-z0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZ;C~1_dU(HBz4vsrc=GsRaoU!8m*+yrLKx4q@Mb&?uVw<{P{oKwfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF N5FkK+009C?;1}AR2h9Kg diff --git a/irq.smc b/irq.smc deleted file mode 100644 index 1559b6d9d3201eb20424c840d6b3100d4a729436..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131584 zcmeI$J!=#}7y#h+B7&(BwXt*8%4nh~1RDc#AR4$R#uWYpJ1s3NwzM#6EeArn1MfEo z`307W9|=~0H1+}4cZ>%YV_}gr$+Ix~&d2WT-gCDzvln4I&I0Fl)~DCn&YQuj)u{W? zeL6hTo!-gaeA)fUtyS@IvOB0&qx*0zVtBeVT(5^W>g`N3o$dANZN!uB)z;Ql3}lP48MWi0q?(vhL5;{kb|zb)6l)kEXMc9eg;uRgV3U^%v^Fv)uLGHG}h| z@VjOz@3HxLoWCxgod5v>1PC06z@41T|C-7tqxqlN{L#$=xdELJAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB=C*5LjHkU#%=IE#AFdt=xLBQZ3(Gs;c>kQxg%Lh<0p?7i}BQ z$12)r+IA|cxZG6nG2=|eE15PjU+TVOEbSLF|0!c>FZsDl-?Dru5Ylb2&miOh zH2(iInp6vE>;sPHCJZE27D*%D!0wrIXU}2z*g13Ai~C#s&9?V)Gh*6o#OkcS+TWku zs%N+By?kBk?QK=BBA)J6^Z9Hg*H(_N{fz#LGs6eXu-{B0>nCUH$@O}2qt2|Zv+L_< zdOxzk_Ts#J_E)Yi*VAYDHe7F}OC>p2xv&v+^E(^_2oNAZfB*pk1PGj-!2Q4W`sbth zm+k(OJE!-RbS6N6009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyaAE>$qd_$uj0S7>s`1^&)P>Hab|$~-n#Juf^@%|rXB!~LmwXdaq}=An6bd>)?I9}yZ6AV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ r009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkL{KNt7|VUd%< diff --git a/src/Makefile b/src/Makefile index 0e964a61..15079f6c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,7 +6,7 @@ prefix = /usr/local ################ ifneq ($(findstring gcc,$(compiler)),) # GCC family - flags = -O3 -fomit-frame-pointer $(if $(call streq,$(platform),x),-mtune=native,) -Ilib + flags = -O3 -fomit-frame-pointer -Ilib c = $(compiler) $(flags) cpp = $(subst cc,++,$(compiler)) $(flags) obj = o @@ -16,7 +16,7 @@ ifneq ($(findstring gcc,$(compiler)),) # GCC family mkdef = -D$1 mklib = -l$1 else ifeq ($(compiler),cl) # Visual C++ - flags = /nologo /wd4355 /wd4996 /O2 /EHsc /Ilib + flags = /nologo /wd4355 /wd4805 /wd4996 /Ox /GL /EHsc /Ilib c = cl $(flags) cpp = cl $(flags) obj = obj @@ -79,7 +79,7 @@ link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`) #################################### objects = main libco hiro ruby libfilter string \ - config reader cart cheat \ + reader cart cheat \ memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \ bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 @@ -98,11 +98,7 @@ rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c)) # Windows resource file ifeq ($(platform),win) - ifeq ($(compiler),cl) - objects += obj/bsnes.res - else ifneq ($(findstring gcc,$(compiler)),) - objects += obj/bsnesrc.$(obj) - endif + objects += obj/resource.$(obj) endif ################ @@ -127,9 +123,8 @@ all: build; ### main ### ############ -obj/main.$(obj): ui/main.cpp ui/* ui/base/* ui/event/* ui/loader/* ui/settings/* -obj/bsnes.res: ui/bsnes.rc; rc /r /foobj/bsnes.res ui/bsnes.rc -obj/bsnesrc.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/bsnesrc.$(obj) +obj/main.$(obj) : ui/main.cpp ui/* ui/base/* ui/event/* ui/loader/* ui/settings/* +obj/resource.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/resource.$(obj) ################# ### libraries ### @@ -139,16 +134,15 @@ obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* lib/ruby/video/* lib/ruby/audio/* $(call compile,$(rubydef) $(rubyflags)) obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/* lib/hiro/gtk/* lib/hiro/win/* $(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`)) -obj/libco.$(obj): lib/libco/libco.c lib/libco/* - $(call compile,-static) +obj/libco.$(obj): lib/libco/libco.c lib/libco/* + $(call compile,$(if $(call strne,$(compiler),cl),-static)) obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/* -obj/string.$(obj): lib/nall/string.cpp lib/nall/* +obj/string.$(obj): lib/nall/string.cpp lib/nall/* ################# ### utilities ### ################# -obj/config.$(obj): config/config.cpp config/* obj/reader.$(obj): reader/reader.cpp reader/* obj/cart.$(obj) : cart/cart.cpp cart/* obj/cheat.$(obj) : cheat/cheat.cpp cheat/* diff --git a/src/base.hpp b/src/base.hpp index e27c13b6..dfe6b493 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.038.05" +#define BSNES_VERSION "0.039" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define BUSCORE sBus @@ -24,15 +24,13 @@ #include #include #include -#include #include #include #include #include -#include +#include #include #include -#include #include #include #include @@ -47,6 +45,5 @@ typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; -typedef unsigned uint; #include "interface.hpp" diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp index e152baf2..6c69d59f 100644 --- a/src/cart/cart.cpp +++ b/src/cart/cart.cpp @@ -1,9 +1,12 @@ -#include <../base.hpp> -#define CART_CPP - -#include +#include <../base.hpp> +#include <../chip/chip.hpp> +#include <../reader/reader.hpp> +#define CART_CPP + +#include #include +#include "cart.hpp" #include "cart_load.cpp" #include "cart_normal.cpp" #include "cart_bsx.cpp" @@ -22,11 +25,11 @@ namespace memory { Cartridge cartridge; -const char* Cartridge::name() { return info.filename; } -Cartridge::CartridgeMode Cartridge::mode() { return info.mode; } -Cartridge::MemoryMapper Cartridge::mapper() { return info.mapper; } -Cartridge::Region Cartridge::region() { return info.region; } -bool Cartridge::loaded() { return cart.loaded; } +const char* Cartridge::name() const { return info.filename; } +Cartridge::CartridgeMode Cartridge::mode() const { return info.mode; } +Cartridge::MemoryMapper Cartridge::mapper() const { return info.mapper; } +Cartridge::Region Cartridge::region() const { return info.region; } +bool Cartridge::loaded() const { return cart.loaded; } void Cartridge::load_begin(CartridgeMode mode) { cart.rom = cart.ram = cart.rtc = 0; @@ -111,7 +114,9 @@ Cartridge::~Cartridge() { if(cart.loaded == true) unload(); } -// +//========== +//cartinfo_t +//========== void Cartridge::cartinfo_t::reset() { type = TypeUnknown; @@ -165,3 +170,30 @@ Cartridge::info_t& Cartridge::info_t::operator=(const Cartridge::cartinfo_t &sou return *this; } + +//======= +//utility +//======= + +string Cartridge::filepath(const char *filename, const char *pathname) { + //if no pathname, return filename as-is + string file(filename); + replace(file, "\\", "/"); + if(!pathname || !*pathname) return file; + + //ensure path ends with trailing '/' + string path(pathname); + replace(path, "\\", "/"); + if(!strend(path, "/")) strcat(path, "/"); + + //replace relative path with absolute path + if(strbegin(path, "./")) { + ltrim(path, "./"); + path = string() << snes.config.path.base << path; + } + + //remove folder part of filename + lstring part; + split(part, "/", file); + return path << part[count(part) - 1]; +} diff --git a/src/cart/cart.hpp b/src/cart/cart.hpp index 83e35ccb..5b6b91d1 100644 --- a/src/cart/cart.hpp +++ b/src/cart/cart.hpp @@ -29,11 +29,11 @@ public: Complement = 0x1c, //inverse checksum Checksum = 0x1e, ResetVector = 0x3c, - }; - - enum Region { - NTSC, - PAL, + }; + + enum Region { + NTSC, + PAL, }; enum MemoryMapper { @@ -55,28 +55,28 @@ public: DSP1HiROM, }; - const char* name(); - CartridgeMode mode(); - MemoryMapper mapper(); - Region region(); + const char* name() const; + CartridgeMode mode() const; + MemoryMapper mapper() const; + Region region() const; struct { bool loaded; char fn[PATH_MAX]; uint8 *rom, *ram, *rtc; - uint rom_size, ram_size, rtc_size; + unsigned rom_size, ram_size, rtc_size; } cart; struct { char fn[PATH_MAX]; uint8 *ram; - uint ram_size; + unsigned ram_size; } bs; struct { char fn[PATH_MAX]; uint8 *rom, *ram; - uint rom_size, ram_size; + unsigned rom_size, ram_size; } stA, stB; struct cartinfo_t { @@ -159,7 +159,7 @@ public: void unload_cart_bsc(); void unload_cart_st(); - bool loaded(); + bool loaded() const; void load_begin(CartridgeMode); void load_end(); bool unload(); @@ -167,29 +167,31 @@ public: void read_header(cartinfo_t &info, const uint8_t *data, unsigned size); unsigned find_header(const uint8_t *data, unsigned size); unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); - - enum CompressionMode { - CompressionNone, //always load without compression - CompressionInspect, //use file header inspection - CompressionAuto, //use file extension or file header inspection (configured by user) + + enum CompressionMode { + CompressionNone, //always load without compression + CompressionInspect, //use file header inspection + CompressionAuto, //use file extension or file header inspection (configured by user) }; - bool load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression = CompressionNone); - bool save_file(const char *fn, uint8 *data, uint size); + bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone); + bool save_file(const char *fn, uint8 *data, unsigned size); bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size); - char* modify_extension(char *filename, const char *extension); - char* get_base_filename(char *filename); - char* get_path_filename(char *filename, const char *path, const char *source, const char *extension); + char* modify_extension(char *filename, const char *extension); + char* get_base_filename(char *filename); + char* get_path_filename(char *filename, const char *path, const char *source, const char *extension); char* get_patch_filename(const char *source, const char *extension); - char* get_save_filename(const char *source, const char *extension); - char* get_cheat_filename(const char *source, const char *extension); + char* get_save_filename(const char *source, const char *extension); + char* get_cheat_filename(const char *source, const char *extension); + + static string filepath(const char *filename, const char *pathname); Cartridge(); ~Cartridge(); -private: +private: char patchfn[PATH_MAX]; char savefn[PATH_MAX]; - char rtcfn[PATH_MAX]; + char rtcfn[PATH_MAX]; char cheatfn[PATH_MAX]; }; diff --git a/src/cart/cart_bsc.cpp b/src/cart/cart_bsc.cpp index e1cb44c8..218be630 100644 --- a/src/cart/cart_bsc.cpp +++ b/src/cart/cart_bsc.cpp @@ -41,4 +41,4 @@ void Cartridge::unload_cart_bsc() { if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size); } -#endif //ifdef CART_CPP +#endif diff --git a/src/cart/cart_bsx.cpp b/src/cart/cart_bsx.cpp index 7836907f..91774236 100644 --- a/src/cart/cart_bsx.cpp +++ b/src/cart/cart_bsx.cpp @@ -46,4 +46,4 @@ void Cartridge::unload_cart_bsx() { save_file(get_save_filename(cart.fn, "psr"), bsxcart.psram.handle(), bsxcart.psram.size()); } -#endif //ifdef CART_CPP +#endif diff --git a/src/cart/cart_file.cpp b/src/cart/cart_file.cpp index 9ea88ffa..f55eaf69 100644 --- a/src/cart/cart_file.cpp +++ b/src/cart/cart_file.cpp @@ -54,28 +54,28 @@ char* Cartridge::get_base_filename(char *filename) { char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) { strcpy(filename, source); modify_extension(filename, extension); - strcpy(filename, config::filepath(filename, path)); + strcpy(filename, filepath(filename, path)); return filename; } char* Cartridge::get_patch_filename(const char *source, const char *extension) { - return get_path_filename(patchfn, config::path.patch, source, extension); + return get_path_filename(patchfn, snes.config.path.patch, source, extension); } char* Cartridge::get_save_filename(const char *source, const char *extension) { - return get_path_filename(savefn, config::path.save, source, extension); + return get_path_filename(savefn, snes.config.path.save, source, extension); } char* Cartridge::get_cheat_filename(const char *source, const char *extension) { - return get_path_filename(cheatfn, config::path.cheat, source, extension); + return get_path_filename(cheatfn, snes.config.path.cheat, source, extension); } -bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) { +bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) { if(file::exists(fn) == false) return false; Reader::Type filetype = Reader::Normal; if(compression == CompressionInspect) filetype = Reader::detect(fn, true); - if(compression == CompressionAuto) filetype = Reader::detect(fn, config::file.autodetect_type); + if(compression == CompressionAuto) filetype = Reader::detect(fn, snes.config.file.autodetect_type); switch(filetype) { default: case Reader::Normal: { @@ -125,7 +125,7 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t bool apply = false; if(result == ups::ok) apply = true; - if(config::file.bypass_patch_crc32 == true) { + if(snes.config.file.bypass_patch_crc32 == true) { if(result == ups::input_crc32_invalid) apply = true; if(result == ups::output_crc32_invalid) apply = true; } @@ -141,7 +141,7 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t return apply; } -bool Cartridge::save_file(const char *fn, uint8 *data, uint size) { +bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) { file fp; if(!fp.open(fn, file::mode_write)) return false; fp.write(data, size); @@ -149,4 +149,4 @@ bool Cartridge::save_file(const char *fn, uint8 *data, uint size) { return true; } -#endif //ifdef CART_CPP +#endif diff --git a/src/cart/cart_header.cpp b/src/cart/cart_header.cpp index b628e3d7..05e201dc 100644 --- a/src/cart/cart_header.cpp +++ b/src/cart/cart_header.cpp @@ -4,7 +4,10 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size info.reset(); unsigned index = find_header(data, size); + //======================= //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]; @@ -19,7 +22,10 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size } } + //========================= //detect Sufami Turbo carts + //========================= + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { info.type = TypeSufamiTurboBIOS; @@ -28,15 +34,18 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size } info.mapper = STROM; info.region = NTSC; //Sufami Turbo only released in Japan - return; + return; //RAM size handled internally by load_cart_st(); } - //standard cart - uint8 mapper = data[index + Mapper]; - uint8 rom_type = data[index + RomType]; - uint8 rom_size = data[index + RomSize]; - uint8 company = data[index + Company]; - uint8 region = data[index + CartRegion] & 0x7f; + //===================== + //detect standard carts + //===================== + + const uint8 mapper = data[index + Mapper]; + const uint8 rom_type = data[index + RomType]; + const uint8 rom_size = data[index + RomSize]; + const uint8 company = data[index + Company]; + const uint8 region = data[index + CartRegion] & 0x7f; //detect presence of BS-X flash cartridge connector (reads extended header information) if(data[index - 14] == 'Z') { @@ -55,25 +64,28 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size //BS-X base cart info.type = TypeBSXBIOS; info.mapper = BSXROM; + info.region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class } else { + //BS-X slotted cart info.type = TypeBSC; info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); } - return; - } + } else { + //standard cart + info.type = TypeNormal; - info.type = TypeNormal; - - if(index == 0x7fc0 && size >= 0x401000) { - info.mapper = ExLoROM; - } else if(index == 0x7fc0 && mapper == 0x32) { - info.mapper = ExLoROM; - } else if(index == 0x7fc0) { - info.mapper = LoROM; - } else if(index == 0xffc0) { - info.mapper = HiROM; - } else { //index == 0x40ffc0 - info.mapper = ExHiROM; + if(index == 0x7fc0 && size >= 0x401000) { + info.mapper = ExLoROM; + } else if(index == 0x7fc0 && mapper == 0x32) { + info.mapper = ExLoROM; + } else if(index == 0x7fc0) { + info.mapper = LoROM; + } else if(index == 0xffc0) { + info.mapper = HiROM; + } else { //index == 0x40ffc0 + info.mapper = ExHiROM; + } } if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { @@ -186,7 +198,7 @@ unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned ad uint16 complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset - uint8 mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + uint8 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. @@ -248,7 +260,7 @@ unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned ad 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 + 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++; @@ -258,4 +270,4 @@ unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned ad return score; } -#endif //ifdef CART_CPP +#endif diff --git a/src/cart/cart_load.cpp b/src/cart/cart_load.cpp index 75e4275b..4a4037ec 100644 --- a/src/cart/cart_load.cpp +++ b/src/cart/cart_load.cpp @@ -47,4 +47,4 @@ bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, ui return true; } -#endif //ifdef CART_CPP +#endif diff --git a/src/cart/cart_normal.cpp b/src/cart/cart_normal.cpp index 11073842..79861f3f 100644 --- a/src/cart/cart_normal.cpp +++ b/src/cart/cart_normal.cpp @@ -32,4 +32,4 @@ void Cartridge::unload_cart_normal() { if(cart.rtc) save_file(get_save_filename(cart.fn, "rtc"), cart.rtc, cart.rtc_size); } -#endif //ifdef CART_CPP +#endif diff --git a/src/cart/cart_st.cpp b/src/cart/cart_st.cpp index b32b5035..0c5fc7af 100644 --- a/src/cart/cart_st.cpp +++ b/src/cart/cart_st.cpp @@ -59,4 +59,4 @@ void Cartridge::unload_cart_st() { if(stB.ram) save_file(get_save_filename(stB.fn, "srm"), stB.ram, stB.ram_size); } -#endif //ifdef CART_CPP +#endif diff --git a/src/cc.bat b/src/cc.bat index 2efde1c3..9e270d97 100644 --- a/src/cc.bat +++ b/src/cc.bat @@ -1,3 +1,3 @@ -@make platform=win compiler=mingw32-gcc -::@make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true +@mingw32-make platform=win compiler=mingw32-gcc +::@mingw32-make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true @pause diff --git a/src/cheat/cheat.cpp b/src/cheat/cheat.cpp index 2dc25281..30da603b 100644 --- a/src/cheat/cheat.cpp +++ b/src/cheat/cheat.cpp @@ -1,13 +1,21 @@ #include <../base.hpp> +#include Cheat cheat; Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) { enabled = source.enabled; - addr = source.addr; - data = source.data; - code = source.code; - desc = source.desc; + code = source.code; + desc = source.desc; + count = source.count; + + addr.reset(); + data.reset(); + for(unsigned n = 0; n < count; n++) { + addr[n] = source.addr[n]; + data[n] = source.data[n]; + } + return *this; } @@ -16,14 +24,200 @@ bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) { return strcmp(desc, source.desc) < 0; } -/***** - * string <> binary code translation routines - * decode() "7e1234:56" -> 0x7e123456 - * encode() 0x7e123456 -> "7e1234:56" - *****/ +//parse item ("0123-4567+89AB-CDEF"), return cheat_t item +//return true if code is valid, false otherwise +bool Cheat::decode(const char *s, Cheat::cheat_t &item) const { + item.enabled = false; + item.count = 0; -bool Cheat::decode(const char *str, unsigned &addr, uint8 &data, type_t &type) { - string t = str; + lstring list; + split(list, "+", s); + + for(unsigned n = 0; n < list.size(); n++) { + unsigned addr; + uint8_t data; + type_t type; + if(decode(list[n], addr, data, type) == false) return false; + + item.addr[item.count] = addr; + item.data[item.count] = data; + item.count++; + } + + return true; +} + +//read() is used by MemBus::read() if Cheat::enabled(addr) returns true to look up cheat code. +//returns true if cheat code was found, false if it was not. +//when true, cheat code substitution value is stored in data. +bool Cheat::read(unsigned addr, uint8_t &data) const { + addr = mirror_address(addr); + for(unsigned i = 0; i < code.size(); i++) { + if(enabled(i) == false) continue; + + for(unsigned n = 0; n < code[i].count; n++) { + if(addr == mirror_address(code[i].addr[n])) { + data = code[i].data[n]; + return true; + } + } + } + + //code not found, or code is disabled + return false; +} + +//================================ +//cheat list manipulation routines +//================================ + +bool Cheat::add(bool enable, const char *code_, const char *desc_) { + cheat_t item; + if(decode(code_, item) == false) return false; + + unsigned i = code.size(); + code[i] = item; + code[i].enabled = enable; + code[i].desc = desc_; + code[i].code = code_; + encode_description(code[i].desc); + update(code[i]); + + update_cheat_status(); + return true; +} + +bool Cheat::edit(unsigned i, bool enable, const char *code_, const char *desc_) { + cheat_t item; + if(decode(code_, item) == false) return false; + + //disable current code and clear from code lookup table + code[i].enabled = false; + update(code[i]); + + code[i] = item; + code[i].enabled = enable; + code[i].desc = desc_; + code[i].code = code_; + encode_description(code[i].desc); + update(code[i]); + + update_cheat_status(); + return true; +} + +bool Cheat::remove(unsigned i) { + unsigned size = code.size(); + if(i >= size) return false; //also verifies size cannot be < 1 + + for(unsigned n = i; n < size - 1; n++) code[n] = code[n + 1]; + code.resize(size - 1); + + update_cheat_status(); + return true; +} + +bool Cheat::get(unsigned i, cheat_t &item) const { + if(i >= code.size()) return false; + + item = code[i]; + decode_description(item.desc); + return true; +} + +//============================== +//cheat status modifier routines +//============================== + +bool Cheat::enabled(unsigned i) const { + return (i < code.size() ? code[i].enabled : false); +} + +void Cheat::enable(unsigned i) { + if(i >= code.size()) return; + + code[i].enabled = true; + update(code[i]); + update_cheat_status(); +} + +void Cheat::disable(unsigned i) { + if(i >= code.size()) return; + + code[i].enabled = false; + update(code[i]); + update_cheat_status(); +} + +//=============================== +//cheat file load / save routines +// +//file format: +//"description", status, nnnn-nnnn[+nnnn-nnnn...]\r\n +//... +//=============================== + +bool Cheat::load(const char *fn) { + string data; + if(!fread(data, fn)) return false; + replace(data, "\r\n", "\n"); + qreplace(data, " ", ""); + + lstring line; + split(line, "\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + qsplit(part, ",", line[i]); + if(part.size() != 3) continue; + trim(part[0], "\""); + add(part[1] == "enabled", /* code = */ part[2], /* desc = */ part[0]); + } + + return true; +} + +bool Cheat::save(const char *fn) const { + file fp; + if(!fp.open(fn, file::mode_write)) return false; + for(unsigned i = 0; i < code.size(); i++) { + fp.print(string() + << "\"" << code[i].desc << "\", " + << (code[i].enabled ? "enabled, " : "disabled, ") + << code[i].code << "\r\n"); + } + fp.close(); + return true; +} + +void Cheat::sort() { + if(code.size() <= 1) return; //nothing to sort? + cheat_t *buffer = new cheat_t[code.size()]; + for(unsigned i = 0; i < code.size(); i++) buffer[i] = code[i]; + nall::sort(buffer, code.size()); + for(unsigned i = 0; i < code.size(); i++) code[i] = buffer[i]; + delete[] buffer; +} + +void Cheat::clear() { + cheat_system_enabled = false; + memset(mask, 0, 0x200000); + code.reset(); +} + +Cheat::Cheat() { + clear(); +} + +//================== +//internal functions +//================== + +//string <> binary code translation routines +//decode() "7e123456" -> 0x7e123456 +//encode() 0x7e123456 -> "7e123456" + +bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) const { + string t = s; strlower(t); #define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f')) @@ -51,18 +245,18 @@ bool Cheat::decode(const char *str, unsigned &addr, uint8 &data, type_t &type) { //8421 8421 8421 8421 8421 8421 //abcd efgh ijkl mnop qrst uvwx //ijkl qrst opab cduv wxef ghmn - addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22) | - (!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20) | - (!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18) | - (!!(r & 0x000008) << 17) | (!!(r & 0x000004) << 16) | - (!!(r & 0x800000) << 15) | (!!(r & 0x400000) << 14) | - (!!(r & 0x200000) << 13) | (!!(r & 0x100000) << 12) | - (!!(r & 0x000002) << 11) | (!!(r & 0x000001) << 10) | - (!!(r & 0x008000) << 9) | (!!(r & 0x004000) << 8) | - (!!(r & 0x080000) << 7) | (!!(r & 0x040000) << 6) | - (!!(r & 0x020000) << 5) | (!!(r & 0x010000) << 4) | - (!!(r & 0x000200) << 3) | (!!(r & 0x000100) << 2) | - (!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0); + addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22) + | (!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20) + | (!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18) + | (!!(r & 0x000008) << 17) | (!!(r & 0x000004) << 16) + | (!!(r & 0x800000) << 15) | (!!(r & 0x400000) << 14) + | (!!(r & 0x200000) << 13) | (!!(r & 0x100000) << 12) + | (!!(r & 0x000002) << 11) | (!!(r & 0x000001) << 10) + | (!!(r & 0x008000) << 9) | (!!(r & 0x004000) << 8) + | (!!(r & 0x080000) << 7) | (!!(r & 0x040000) << 6) + | (!!(r & 0x020000) << 5) | (!!(r & 0x010000) << 4) + | (!!(r & 0x000200) << 3) | (!!(r & 0x000100) << 2) + | (!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0); data = r >> 24; return true; } else { @@ -70,43 +264,53 @@ bool Cheat::decode(const char *str, unsigned &addr, uint8 &data, type_t &type) { } } -bool Cheat::encode(string &str, unsigned addr, uint8 data, type_t type) { +bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const { char t[16]; if(type == ProActionReplay) { - sprintf(t, "%.6x:%.2x", addr, data); - str = t; + sprintf(t, "%.6x%.2x", addr, data); + s = t; return true; } else if(type == GameGenie) { unsigned r = addr; - addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) | - (!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) | - (!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18) | - (!!(r & 0x000020) << 17) | (!!(r & 0x000010) << 16) | - (!!(r & 0x000200) << 15) | (!!(r & 0x000100) << 14) | - (!!(r & 0x800000) << 13) | (!!(r & 0x400000) << 12) | - (!!(r & 0x200000) << 11) | (!!(r & 0x100000) << 10) | - (!!(r & 0x000008) << 9) | (!!(r & 0x000004) << 8) | - (!!(r & 0x000002) << 7) | (!!(r & 0x000001) << 6) | - (!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4) | - (!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2) | - (!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0); + addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) + | (!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) + | (!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18) + | (!!(r & 0x000020) << 17) | (!!(r & 0x000010) << 16) + | (!!(r & 0x000200) << 15) | (!!(r & 0x000100) << 14) + | (!!(r & 0x800000) << 13) | (!!(r & 0x400000) << 12) + | (!!(r & 0x200000) << 11) | (!!(r & 0x100000) << 10) + | (!!(r & 0x000008) << 9) | (!!(r & 0x000004) << 8) + | (!!(r & 0x000002) << 7) | (!!(r & 0x000001) << 6) + | (!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4) + | (!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2) + | (!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0); sprintf(t, "%.2x%.2x-%.4x", data, addr >> 16, addr & 0xffff); strtr(t, "0123456789abcdef", "df4709156bc8a23e"); - str = t; + s = t; return true; } else { return false; } } -/***** - * address lookup table manipulation and mirroring - * mirror_address() 0x000000 -> 0x7e0000 - * set() enable specified address, mirror accordingly - * clear() disable specified address, mirror accordingly - *****/ +//update_cheat_status() will scan to see if any codes are +//enabled. if any are, make sure the cheat system is on. +//otherwise, turn cheat system off to speed up emulation. +void Cheat::update_cheat_status() { + for(unsigned i = 0; i < code.size(); i++) { + if(code[i].enabled) { + cheat_system_enabled = true; + return; + } + } + cheat_system_enabled = false; +} +//address lookup table manipulation and mirroring +//mirror_address() 0x000000 -> 0x7e0000 +//set() enable specified address, mirror accordingly +//clear() disable specified address, mirror accordingly unsigned Cheat::mirror_address(unsigned addr) const { if((addr & 0x40e000) != 0x0000) return addr; //8k WRAM mirror @@ -114,6 +318,14 @@ unsigned Cheat::mirror_address(unsigned addr) const { return (0x7e0000 + (addr & 0x1fff)); } +//updates mask[] table enabled bits; +//must be called after modifying item.enabled state. +void Cheat::update(const cheat_t &item) { + for(unsigned n = 0; n < item.count; n++) { + (item.enabled) ? set(item.addr[n]) : clear(item.addr[n]); + } +} + void Cheat::set(unsigned addr) { addr = mirror_address(addr); @@ -136,7 +348,7 @@ void Cheat::clear(unsigned addr) { //if there is more than one cheat code using the same address, //(eg with a different override value) then do not clear code //lookup table entry. - uint8 r; + uint8_t r; if(read(addr, r) == true) return; mask[addr >> 3] &= ~(1 << (addr & 7)); @@ -152,185 +364,16 @@ void Cheat::clear(unsigned addr) { } } -/***** - * read() is used by MemBus::read() if Cheat::enabled(addr) - * returns true to look up cheat code. - * returns true if cheat code was found, false if it was not. - * when true, cheat code substitution value is stored in data. - *****/ +//these two functions are used to safely store description text inside .cfg file format. -bool Cheat::read(unsigned addr, uint8 &data) const { - addr = mirror_address(addr); - for(unsigned i = 0; i < code.size(); i++) { - if(enabled(i) == false) continue; - if(addr == mirror_address(code[i].addr)) { - data = code[i].data; - return true; - } - } - //code not found, or code is disabled - return false; +string& Cheat::encode_description(string &desc) const { + replace(desc, "\"", "\\q"); + replace(desc, "\n", "\\n"); + return desc; } -/***** - * update_cheat_status() will scan to see if any codes are - * enabled. if any are, make sure the cheat system is on. - * otherwise, turn cheat system off to speed up emulation. - *****/ - -void Cheat::update_cheat_status() { - for(unsigned i = 0; i < code.size(); i++) { - if(code[i].enabled) { - cheat_system_enabled = true; - return; - } - } - cheat_system_enabled = false; -} - -/***** - * cheat list manipulation routines - *****/ - -bool Cheat::add(bool enable, const char *code_, const char *desc_) { - unsigned addr; - uint8 data; - type_t type; - if(decode(code_, addr, data, type) == false) return false; - - unsigned n = code.size(); - code[n].enabled = enable; - code[n].addr = addr; - code[n].data = data; - code[n].code = code_; - code[n].desc = desc_; - (enable) ? set(addr) : clear(addr); - - update_cheat_status(); - return true; -} - -bool Cheat::edit(unsigned n, bool enable, const char *code_, const char *desc_) { - unsigned addr; - uint8 data; - type_t type; - if(decode(code_, addr, data, type) == false) return false; - - //disable current code and clear from code lookup table - code[n].enabled = false; - clear(code[n].addr); - - //update code and enable in code lookup table - code[n].enabled = enable; - code[n].addr = addr; - code[n].data = data; - code[n].code = code_; - code[n].desc = desc_; - set(addr); - - update_cheat_status(); - return true; -} - -bool Cheat::remove(unsigned n) { - unsigned size = code.size(); - if(n >= size) return false; //also verifies size cannot be < 1 - - for(unsigned i = n; i < size - 1; i++) code[i] = code[i + 1]; - code.resize(size - 1); - - update_cheat_status(); - return true; -} - -bool Cheat::get(unsigned n, cheat_t &cheat) const { - if(n >= code.size()) return false; - - cheat = code[n]; - return true; -} - -/***** - * code status modifier routines - *****/ - -bool Cheat::enabled(unsigned n) const { - return (n < code.size()) ? code[n].enabled : false; -} - -void Cheat::enable(unsigned n) { - if(n >= code.size()) return; - - code[n].enabled = true; - set(code[n].addr); - update_cheat_status(); -} - -void Cheat::disable(unsigned n) { - if(n >= code.size()) return; - - code[n].enabled = false; - clear(code[n].addr); - update_cheat_status(); -} - -/***** - * cheat file manipulation routines - *****/ - -/* file format: */ -/* nnnn-nnnn = status, "description" \r\n */ -/* ... */ - -bool Cheat::load(const char *fn) { - string data; - if(!fread(data, fn)) return false; - replace(data, "\r\n", "\n"); - qreplace(data, "=", ","); - qreplace(data, " ", ""); - - lstring line; - split(line, "\n", data); - for(unsigned i = 0; i < ::count(line); i++) { - lstring part; - split(part, ",", line[i]); - if(::count(part) != 3) continue; - trim(part[2], "\""); - add(part[1] == "enabled", part[0], part[2]); - } - - return true; -} - -bool Cheat::save(const char *fn) const { - file fp; - if(!fp.open(fn, file::mode_write)) return false; - for(unsigned i = 0; i < code.size(); i++) { - fp.print(string() - << code[i].code << " = " - << (code[i].enabled ? "enabled" : "disabled") << ", " - << "\"" << code[i].desc << "\"" - << "\r\n"); - } - fp.close(); - return true; -} - -void Cheat::sort() { - if(code.size() <= 1) return; //nothing to sort? - cheat_t *buffer = new cheat_t[code.size()]; - for(unsigned i = 0; i < code.size(); i++) buffer[i] = code[i]; - nall::sort(buffer, code.size()); - for(unsigned i = 0; i < code.size(); i++) code[i] = buffer[i]; - delete[] buffer; -} - -void Cheat::clear() { - cheat_system_enabled = false; - memset(mask, 0, 0x200000); - code.reset(); -} - -Cheat::Cheat() { - clear(); +string& Cheat::decode_description(string &desc) const { + replace(desc, "\\q", "\""); + replace(desc, "\\n", "\n"); + return desc; } diff --git a/src/cheat/cheat.hpp b/src/cheat/cheat.hpp index ac8a543d..9cc92a40 100644 --- a/src/cheat/cheat.hpp +++ b/src/cheat/cheat.hpp @@ -6,32 +6,33 @@ public: }; struct cheat_t { - bool enabled; - unsigned addr; - uint8 data; - string code; - string desc; + bool enabled; + string code; + string desc; + + unsigned count; + array addr; + array data; cheat_t& operator=(const cheat_t&); bool operator<(const cheat_t&); }; - static bool decode(const char *str, unsigned &addr, uint8 &data, type_t &type); - static bool encode(string &str, unsigned addr, uint8 data, type_t type); + bool decode(const char *s, cheat_t &item) const; + bool read(unsigned addr, uint8_t &data) const; inline bool enabled() const { return cheat_system_enabled; } inline unsigned count() const { return code.size(); } - inline bool exists(unsigned addr) const { return bool(mask[addr >> 3] & 1 << (addr & 7)); } - - bool read(unsigned addr, uint8 &data) const; + inline bool exists(unsigned addr) const { return mask[addr >> 3] & 1 << (addr & 7); } bool add(bool enable, const char *code, const char *desc); - bool edit(unsigned n, bool enable, const char *code, const char *desc); - bool get(unsigned n, cheat_t &cheat) const; - bool remove(unsigned n); - bool enabled(unsigned n) const; - void enable(unsigned n); - void disable(unsigned n); + bool edit(unsigned i, bool enable, const char *code, const char *desc); + bool remove(unsigned i); + bool get(unsigned i, cheat_t &item) const; + + bool enabled(unsigned i) const; + void enable(unsigned i); + void disable(unsigned i); bool load(const char *fn); bool save(const char *fn) const; @@ -43,13 +44,21 @@ public: private: bool cheat_system_enabled; - uint8 mask[0x200000]; + uint8_t mask[0x200000]; vector code; + bool decode(const char *str, unsigned &addr, uint8_t &data, type_t &type) const; + bool encode(string &str, unsigned addr, uint8_t data, type_t type) const; + void update_cheat_status(); unsigned mirror_address(unsigned addr) const; + + void update(const cheat_t& item); void set(unsigned addr); void clear(unsigned addr); + + string& encode_description(string &desc) const; + string& decode_description(string &desc) const; }; extern Cheat cheat; diff --git a/src/chip/bsx/bsx.cpp b/src/chip/bsx/bsx.cpp index c4e16325..9d4de100 100644 --- a/src/chip/bsx/bsx.cpp +++ b/src/chip/bsx/bsx.cpp @@ -1,6 +1,8 @@ -#include <../base.hpp> +#include <../base.hpp> +#include <../cart/cart.hpp> #define BSX_CPP +#include "bsx.hpp" #include "bsx_base.cpp" #include "bsx_cart.cpp" #include "bsx_flash.cpp" diff --git a/src/chip/bsx/bsx.hpp b/src/chip/bsx/bsx.hpp index d1ae221a..ebab28a9 100644 --- a/src/chip/bsx/bsx.hpp +++ b/src/chip/bsx/bsx.hpp @@ -5,8 +5,8 @@ public: void power(); void reset(); - uint8 mmio_read(uint addr); - void mmio_write(uint addr, uint8 data); + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); private: struct { @@ -29,8 +29,8 @@ public: void power(); void reset(); - uint8 mmio_read(uint addr); - void mmio_write(uint addr, uint8 data); + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); MappedRAM sram; MappedRAM psram; @@ -56,13 +56,13 @@ public: void power(); void reset(); - uint size(); - uint8 read(uint addr); - void write(uint addr, uint8 data); + unsigned size(); + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); private: struct { - uint command; + unsigned command; uint8 write_old; uint8 write_new; diff --git a/src/chip/bsx/bsx_base.cpp b/src/chip/bsx/bsx_base.cpp index 090226b6..3e81ece7 100644 --- a/src/chip/bsx/bsx_base.cpp +++ b/src/chip/bsx/bsx_base.cpp @@ -15,7 +15,7 @@ void BSXBase::reset() { memset(®s, 0x00, sizeof regs); } -uint8 BSXBase::mmio_read(uint addr) { +uint8 BSXBase::mmio_read(unsigned addr) { addr &= 0xffff; switch(addr) { @@ -28,7 +28,7 @@ uint8 BSXBase::mmio_read(uint addr) { case 0x2190: return regs.r2190; case 0x2192: { - uint counter = regs.r2192_counter++; + unsigned counter = regs.r2192_counter++; if(regs.r2192_counter >= 18) regs.r2192_counter = 0; if(counter == 0) { @@ -73,7 +73,7 @@ uint8 BSXBase::mmio_read(uint addr) { return cpu.regs.mdr; } -void BSXBase::mmio_write(uint addr, uint8 data) { +void BSXBase::mmio_write(unsigned addr, uint8 data) { addr &= 0xffff; switch(addr) { @@ -134,4 +134,4 @@ void BSXBase::mmio_write(uint addr, uint8 data) { } } -#endif //ifdef BSX_CPP +#endif diff --git a/src/chip/bsx/bsx_cart.cpp b/src/chip/bsx/bsx_cart.cpp index 49585061..ed875cf8 100644 --- a/src/chip/bsx/bsx_cart.cpp +++ b/src/chip/bsx/bsx_cart.cpp @@ -12,7 +12,7 @@ void BSXCart::power() { } void BSXCart::reset() { - for(uint i = 0; i < 16; i++) regs.r[i] = 0x00; + for(unsigned i = 0; i < 16; i++) regs.r[i] = 0x00; regs.r[0x07] = 0x80; regs.r[0x08] = 0x80; @@ -57,7 +57,7 @@ void BSXCart::update_memory_map() { bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, psram); } -uint8 BSXCart::mmio_read(uint addr) { +uint8 BSXCart::mmio_read(unsigned addr) { if((addr & 0xf0ffff) == 0x005000) { //$[00-0f]:5000 MMIO uint8 n = (addr >> 16) & 15; return regs.r[n]; @@ -70,7 +70,7 @@ uint8 BSXCart::mmio_read(uint addr) { return 0x00; } -void BSXCart::mmio_write(uint addr, uint8 data) { +void BSXCart::mmio_write(unsigned addr, uint8 data) { if((addr & 0xf0ffff) == 0x005000) { //$[00-0f]:5000 MMIO uint8 n = (addr >> 16) & 15; regs.r[n] = data; @@ -96,4 +96,4 @@ BSXCart::~BSXCart() { delete[] psram_data; } -#endif //ifdef BSX_CPP +#endif diff --git a/src/chip/bsx/bsx_flash.cpp b/src/chip/bsx/bsx_flash.cpp index 43f925fd..781d2ea6 100644 --- a/src/chip/bsx/bsx_flash.cpp +++ b/src/chip/bsx/bsx_flash.cpp @@ -17,11 +17,11 @@ void BSXFlash::reset() { regs.write_enable = false; } -uint BSXFlash::size() { +unsigned BSXFlash::size() { return memory::bscram.size(); } -uint8 BSXFlash::read(uint addr) { +uint8 BSXFlash::read(unsigned addr) { if(addr == 0x0002) { if(regs.flash_enable) return 0x80; } @@ -48,7 +48,7 @@ uint8 BSXFlash::read(uint addr) { return memory::bscram.read(addr); } -void BSXFlash::write(uint addr, uint8 data) { +void BSXFlash::write(unsigned addr, uint8 data) { //there exist both read-only and read-write BS-X flash cartridges ... //unfortunately, the vendor info is not stored inside memory dumps //of BS-X flashcarts, so it is impossible to determine whether a @@ -110,4 +110,4 @@ void BSXFlash::write(uint addr, uint8 data) { } } -#endif //ifdef BSX_CPP +#endif diff --git a/src/chip/cx4/cx4.cpp b/src/chip/cx4/cx4.cpp index 5335e217..963d42cc 100644 --- a/src/chip/cx4/cx4.cpp +++ b/src/chip/cx4/cx4.cpp @@ -8,6 +8,7 @@ #include <../base.hpp> #define CX4_CPP +#include "cx4.hpp" #include "cx4data.cpp" #include "cx4fn.cpp" #include "cx4oam.cpp" @@ -78,7 +79,7 @@ uint16 dest, count; } } -void Cx4::write(uint addr, uint8 data) { +void Cx4::write(unsigned addr, uint8 data) { addr &= 0x1fff; if(addr < 0x0c00) { @@ -160,7 +161,7 @@ void Cx4::writel(uint16 addr, uint32 data) { write(addr + 2, data >> 16); } -uint8 Cx4::read(uint addr) { +uint8 Cx4::read(unsigned addr) { addr &= 0x1fff; if(addr < 0x0c00) { diff --git a/src/chip/cx4/cx4.hpp b/src/chip/cx4/cx4.hpp index d1133d43..c4cf9505 100644 --- a/src/chip/cx4/cx4.hpp +++ b/src/chip/cx4/cx4.hpp @@ -90,8 +90,8 @@ public: void power(); void reset(); - uint8 read (uint addr); - void write(uint addr, uint8 data); + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); }; extern Cx4 cx4; diff --git a/src/chip/cx4/cx4data.cpp b/src/chip/cx4/cx4data.cpp index 0308c833..426dfeeb 100644 --- a/src/chip/cx4/cx4data.cpp +++ b/src/chip/cx4/cx4data.cpp @@ -184,4 +184,4 @@ const int16 Cx4::CosTable[512] = { 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765 }; -#endif //ifdef CX4_CPP +#endif diff --git a/src/chip/cx4/cx4fn.cpp b/src/chip/cx4/cx4fn.cpp index ca0f8dd7..ed60bc8c 100644 --- a/src/chip/cx4/cx4fn.cpp +++ b/src/chip/cx4/cx4fn.cpp @@ -243,4 +243,4 @@ uint8 bit = 0x80; } } -#endif //ifdef CX4_CPP +#endif diff --git a/src/chip/cx4/cx4oam.cpp b/src/chip/cx4/cx4oam.cpp index 6173cb05..8214fbb1 100644 --- a/src/chip/cx4/cx4oam.cpp +++ b/src/chip/cx4/cx4oam.cpp @@ -220,4 +220,4 @@ uint16 mask2 = 0x3f3f; } } -#endif //ifdef CX4_CPP +#endif diff --git a/src/chip/cx4/cx4ops.cpp b/src/chip/cx4/cx4ops.cpp index 8cc509ee..f5373cc5 100644 --- a/src/chip/cx4/cx4ops.cpp +++ b/src/chip/cx4/cx4ops.cpp @@ -223,4 +223,4 @@ void Cx4::op89() { str(1, 0xffffff); } -#endif //ifdef CX4_CPP +#endif diff --git a/src/chip/dsp1/dsp1.cpp b/src/chip/dsp1/dsp1.cpp index de403abf..1e67af74 100644 --- a/src/chip/dsp1/dsp1.cpp +++ b/src/chip/dsp1/dsp1.cpp @@ -1,6 +1,8 @@ -#include <../base.hpp> +#include <../base.hpp> +#include <../cart/cart.hpp> #define DSP1_CPP +#include "dsp1.hpp" #include "dsp1emu.cpp" void DSP1::init() {} @@ -46,11 +48,11 @@ bool DSP1::addr_decode(uint16 addr) { return 0; } -uint8 DSP1::read(uint addr) { +uint8 DSP1::read(unsigned addr) { return (addr_decode(addr) == 0) ? dsp1.getDr() : dsp1.getSr(); } -void DSP1::write(uint addr, uint8 data) { +void DSP1::write(unsigned addr, uint8 data) { if(addr_decode(addr) == 0) { dsp1.setDr(data); } diff --git a/src/chip/dsp1/dsp1.hpp b/src/chip/dsp1/dsp1.hpp index 95c72a4b..5bbdc74d 100644 --- a/src/chip/dsp1/dsp1.hpp +++ b/src/chip/dsp1/dsp1.hpp @@ -11,8 +11,8 @@ public: void power(); void reset(); - uint8 read(uint addr); - void write(uint addr, uint8 data); + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); }; extern DSP1 dsp1; diff --git a/src/chip/dsp1/dsp1emu.cpp b/src/chip/dsp1/dsp1emu.cpp index 58dd2dea..0e6a29c9 100644 --- a/src/chip/dsp1/dsp1emu.cpp +++ b/src/chip/dsp1/dsp1emu.cpp @@ -1622,4 +1622,4 @@ const int16 Dsp1::SinTable[256] = { ////////////////////////////////////////////////////////////////// -#endif //ifdef DSP1_CPP +#endif diff --git a/src/chip/dsp2/dsp2.cpp b/src/chip/dsp2/dsp2.cpp index 2d36d657..de40c0e3 100644 --- a/src/chip/dsp2/dsp2.cpp +++ b/src/chip/dsp2/dsp2.cpp @@ -1,6 +1,7 @@ #include <../base.hpp> #define DSP2_CPP +#include "dsp2.hpp" #include "dsp2_op.cpp" void DSP2::init() {} @@ -29,8 +30,8 @@ void DSP2::reset() { status.op0dinlen = 0; } -uint8 DSP2::read(uint addr) { -uint8 r = 0xff; +uint8 DSP2::read(unsigned addr) { + uint8 r = 0xff; if(status.out_count) { r = status.output[status.out_index++]; status.out_index &= 511; @@ -41,7 +42,7 @@ uint8 r = 0xff; return r; } -void DSP2::write(uint addr, uint8 data) { +void DSP2::write(unsigned addr, uint8 data) { if(status.waiting_for_command) { status.command = data; status.in_index = 0; diff --git a/src/chip/dsp2/dsp2.hpp b/src/chip/dsp2/dsp2.hpp index cf125b23..e84c9391 100644 --- a/src/chip/dsp2/dsp2.hpp +++ b/src/chip/dsp2/dsp2.hpp @@ -1,10 +1,10 @@ class DSP2 : public Memory { public: struct { - bool waiting_for_command; - uint command; - uint in_count, in_index; - uint out_count, out_index; + bool waiting_for_command; + unsigned command; + unsigned in_count, in_index; + unsigned out_count, out_index; uint8 parameters[512]; uint8 output[512]; @@ -26,8 +26,8 @@ public: void power(); void reset(); - uint8 read(uint addr); - void write(uint addr, uint8 data); + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); DSP2(); ~DSP2(); diff --git a/src/chip/dsp2/dsp2_op.cpp b/src/chip/dsp2/dsp2_op.cpp index fec67bd6..dc6c7cc7 100644 --- a/src/chip/dsp2/dsp2_op.cpp +++ b/src/chip/dsp2/dsp2_op.cpp @@ -174,4 +174,4 @@ uint8 pixelarray[512]; } } -#endif //ifdef DSP2_CPP +#endif diff --git a/src/chip/dsp3/dsp3.cpp b/src/chip/dsp3/dsp3.cpp index ad88fe23..e6c7c7c1 100644 --- a/src/chip/dsp3/dsp3.cpp +++ b/src/chip/dsp3/dsp3.cpp @@ -1,6 +1,7 @@ #include <../base.hpp> #define DSP3_CPP +#include "dsp3.hpp" namespace DSP3i { #define bool8 uint8 #include "dsp3emu.c" @@ -21,13 +22,13 @@ void DSP3::reset() { DSP3i::DSP3_Reset(); } -uint8 DSP3::read(uint addr) { +uint8 DSP3::read(unsigned addr) { DSP3i::dsp3_address = addr & 0xffff; DSP3i::DSP3GetByte(); return DSP3i::dsp3_byte; } -void DSP3::write(uint addr, uint8 data) { +void DSP3::write(unsigned addr, uint8 data) { DSP3i::dsp3_address = addr & 0xffff; DSP3i::dsp3_byte = data; DSP3i::DSP3SetByte(); diff --git a/src/chip/dsp3/dsp3.hpp b/src/chip/dsp3/dsp3.hpp index b596963a..38696334 100644 --- a/src/chip/dsp3/dsp3.hpp +++ b/src/chip/dsp3/dsp3.hpp @@ -5,8 +5,8 @@ public: void power(); void reset(); - uint8 read (uint addr); - void write(uint addr, uint8 data); + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); }; extern DSP3 dsp3; diff --git a/src/chip/dsp3/dsp3emu.c b/src/chip/dsp3/dsp3emu.c index 7ce0d81e..21f82a6c 100644 --- a/src/chip/dsp3/dsp3emu.c +++ b/src/chip/dsp3/dsp3emu.c @@ -1143,4 +1143,4 @@ void InitDSP3() DSP3_Reset(); } -#endif //ifdef DSP3_CPP +#endif diff --git a/src/chip/dsp4/dsp4.cpp b/src/chip/dsp4/dsp4.cpp index 9e43f431..aed7caf5 100644 --- a/src/chip/dsp4/dsp4.cpp +++ b/src/chip/dsp4/dsp4.cpp @@ -1,6 +1,7 @@ #include <../base.hpp> #define DSP4_CPP +#include "dsp4.hpp" namespace DSP4i { inline uint16 READ_WORD(uint8 *addr) { return (addr[0]) + (addr[1] << 8); @@ -34,7 +35,7 @@ void DSP4::reset() { DSP4i::InitDSP4(); } -uint8 DSP4::read(uint addr) { +uint8 DSP4::read(unsigned addr) { addr &= 0xffff; if(addr < 0xc000) { DSP4i::dsp4_address = addr; @@ -44,7 +45,7 @@ uint8 DSP4::read(uint addr) { return 0x80; } -void DSP4::write(uint addr, uint8 data) { +void DSP4::write(unsigned addr, uint8 data) { addr &= 0xffff; if(addr < 0xc000) { DSP4i::dsp4_address = addr; diff --git a/src/chip/dsp4/dsp4.hpp b/src/chip/dsp4/dsp4.hpp index 75c19eca..17d6533a 100644 --- a/src/chip/dsp4/dsp4.hpp +++ b/src/chip/dsp4/dsp4.hpp @@ -5,8 +5,8 @@ public: void power(); void reset(); - uint8 read (uint addr); - void write(uint addr, uint8 data); + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); }; extern DSP4 dsp4; diff --git a/src/chip/dsp4/dsp4emu.c b/src/chip/dsp4/dsp4emu.c index 50785775..1b6ea272 100644 --- a/src/chip/dsp4/dsp4emu.c +++ b/src/chip/dsp4/dsp4emu.c @@ -2147,4 +2147,4 @@ void DSP4GetByte() } } -#endif //ifdef DSP4_CPP +#endif diff --git a/src/chip/obc1/obc1.cpp b/src/chip/obc1/obc1.cpp index eeb5a2ce..96069953 100644 --- a/src/chip/obc1/obc1.cpp +++ b/src/chip/obc1/obc1.cpp @@ -1,4 +1,6 @@ #include <../base.hpp> +#include <../cart/cart.hpp> +#include "obc1.hpp" void OBC1::init() {} void OBC1::enable() {} diff --git a/src/chip/sdd1/sdd1.cpp b/src/chip/sdd1/sdd1.cpp index e845f13b..8530b4bd 100644 --- a/src/chip/sdd1/sdd1.cpp +++ b/src/chip/sdd1/sdd1.cpp @@ -1,6 +1,8 @@ -#include <../base.hpp> +#include <../base.hpp> +#include <../cart/cart.hpp> #define SDD1_CPP +#include "sdd1.hpp" #include "sdd1emu.cpp" void SDD1::init() {} @@ -42,7 +44,7 @@ void SDD1::reset() { bus.map(Bus::MapDirect, 0xc0, 0xff, 0x0000, 0xffff, *this); } -uint8 SDD1::mmio_read(uint addr) { +uint8 SDD1::mmio_read(unsigned addr) { addr &= 0xffff; if((addr & 0x4380) == 0x4300) { @@ -59,7 +61,7 @@ uint8 SDD1::mmio_read(uint addr) { return cpu.regs.mdr; } -void SDD1::mmio_write(uint addr, uint8 data) { +void SDD1::mmio_write(unsigned addr, uint8 data) { addr &= 0xffff; if((addr & 0x4380) == 0x4300) { diff --git a/src/chip/sdd1/sdd1emu.cpp b/src/chip/sdd1/sdd1emu.cpp index 449082ab..2581ff98 100644 --- a/src/chip/sdd1/sdd1emu.cpp +++ b/src/chip/sdd1/sdd1emu.cpp @@ -448,4 +448,4 @@ SDD1emu::SDD1emu() : /////////////////////////////////////////////////////////// -#endif //ifdef SDD1_CPP +#endif diff --git a/src/chip/spc7110/spc7110.cpp b/src/chip/spc7110/spc7110.cpp index d6a84660..c33dda1b 100644 --- a/src/chip/spc7110/spc7110.cpp +++ b/src/chip/spc7110/spc7110.cpp @@ -1,6 +1,8 @@ #include <../base.hpp> +#include <../cart/cart.hpp> #define SPC7110_CPP +#include "spc7110.hpp" #include "decomp.cpp" const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -92,18 +94,30 @@ void SPC7110::set_data_pointer(unsigned addr) { r4811 = addr; r4812 = addr >> 8; void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; } void SPC7110::update_time(int offset) { - time_t rtc_time; - rtc_time = memory::cartrtc.read(16); - rtc_time |= memory::cartrtc.read(17) << 8; - rtc_time |= memory::cartrtc.read(18) << 16; - rtc_time |= memory::cartrtc.read(19) << 24; + time_t rtc_time + = (memory::cartrtc.read(16) << 0) + | (memory::cartrtc.read(17) << 8) + | (memory::cartrtc.read(18) << 16) + | (memory::cartrtc.read(19) << 24); + time_t current_time = time(0); + + //sizeof(time_t) is platform-dependent; though memory::cartrtc needs to be platform-agnostic. + //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by + //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow + //memory::cartrtc timestamp to remain valid for up to ~34 years from the last update, even if + //time_t overflows. calculation should be valid regardless of number representation, time_t size, + //or whether time_t is signed or unsigned. + time_t diff + = (current_time >= rtc_time) + ? (current_time - rtc_time) + : (std::numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > std::numeric_limits::max() / 2) diff = 0; //compensate for underflow bool update = true; - if(memory::cartrtc.read(13) & 1) update = false; //do not update if CR0 timer disable flag is set - if(memory::cartrtc.read(15) & 3) update = false; //do not update if CR2 timer disable flags are set + if(memory::cartrtc.read(13) & 1) update = false; //do not update if CR0 timer disable flag is set + if(memory::cartrtc.read(15) & 3) update = false; //do not update if CR2 timer disable flags are set - time_t current_time = time(0) - offset; - if(update && current_time > rtc_time) { + if(diff > 0 && update == true) { unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; @@ -114,9 +128,9 @@ void SPC7110::update_time(int offset) { day--; month--; - year += (year >= 90) ? 1900 : 2000; //range = 1990-2089 + year += (year >= 90) ? 1900 : 2000; //range = 1990-2089 - second += (unsigned)(current_time - rtc_time); + second += diff; while(second >= 60) { second -= 60; @@ -168,13 +182,13 @@ void SPC7110::update_time(int offset) { memory::cartrtc.write(12, weekday % 7); } - memory::cartrtc.write(16, current_time); - memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(16, current_time >> 0); + memory::cartrtc.write(17, current_time >> 8); memory::cartrtc.write(18, current_time >> 16); memory::cartrtc.write(19, current_time >> 24); } -uint8 SPC7110::mmio_read(uint addr) { +uint8 SPC7110::mmio_read(unsigned addr) { addr &= 0xffff; switch(addr) { @@ -215,7 +229,7 @@ uint8 SPC7110::mmio_read(uint addr) { unsigned addr = data_pointer(); unsigned adjust = data_adjust(); - if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend unsigned adjustaddr = addr; if(r4818 & 2) { @@ -226,7 +240,7 @@ uint8 SPC7110::mmio_read(uint addr) { uint8 data = memory::cartrom.read(datarom_addr(adjustaddr)); if(!(r4818 & 2)) { unsigned increment = (r4818 & 1) ? data_increment() : 1; - if(r4818 & 4) increment = (int16)increment; //16-bit sign extend + if(r4818 & 4) increment = (int16)increment; //16-bit sign extend if((r4818 & 16) == 0) { set_data_pointer(addr + increment); @@ -250,7 +264,7 @@ uint8 SPC7110::mmio_read(uint addr) { unsigned addr = data_pointer(); unsigned adjust = data_adjust(); - if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend uint8 data = memory::cartrom.read(datarom_addr(addr + adjust)); if((r4818 & 0x60) == 0x60) { @@ -322,7 +336,7 @@ uint8 SPC7110::mmio_read(uint addr) { return cpu.regs.mdr; } -void SPC7110::mmio_write(uint addr, uint8 data) { +void SPC7110::mmio_write(unsigned addr, uint8 data) { addr &= 0xffff; switch(addr) { @@ -373,11 +387,11 @@ void SPC7110::mmio_write(uint addr, uint8 data) { if((r4818 & 0x60) == 0x20) { unsigned increment = data_adjust() & 0xff; - if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend set_data_pointer(data_pointer() + increment); } else if((r4818 & 0x60) == 0x40) { unsigned increment = data_adjust(); - if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend set_data_pointer(data_pointer() + increment); } } break; @@ -390,11 +404,11 @@ void SPC7110::mmio_write(uint addr, uint8 data) { if((r4818 & 0x60) == 0x20) { unsigned increment = data_adjust() & 0xff; - if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend set_data_pointer(data_pointer() + increment); } else if((r4818 & 0x60) == 0x40) { unsigned increment = data_adjust(); - if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend set_data_pointer(data_pointer() + increment); } } break; @@ -615,7 +629,7 @@ void SPC7110::mmio_write(uint addr, uint8 data) { } } -uint8 SPC7110::read(uint addr) { +uint8 SPC7110::read(unsigned addr) { //$[00-0f|80-8f]:[8000-ffff], $[c0-cf]:[0000-ffff] mapped directly to memory::cartrom if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { @@ -646,7 +660,7 @@ uint8 SPC7110::read(uint addr) { return cpu.regs.mdr; } -void SPC7110::write(uint addr, uint8 data) { +void SPC7110::write(unsigned addr, uint8 data) { if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { //$[00|30]:[6000-7fff] if(r4830 & 0x80) memory::cartram.write(addr & 0x1fff, data); diff --git a/src/chip/spc7110/spc7110.hpp b/src/chip/spc7110/spc7110.hpp index 2c9b6bf1..45baefca 100644 --- a/src/chip/spc7110/spc7110.hpp +++ b/src/chip/spc7110/spc7110.hpp @@ -35,11 +35,11 @@ public: void update_time(int offset = 0); time_t create_time(); - uint8 mmio_read (uint addr); - void mmio_write(uint addr, uint8 data); + uint8 mmio_read (unsigned addr); + void mmio_write(unsigned addr, uint8 data); - uint8 read (uint addr); - void write(uint addr, uint8 data); + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); //spc7110decomp void decomp_init(); diff --git a/src/chip/srtc/srtc.cpp b/src/chip/srtc/srtc.cpp index 335cb965..f76ecf4a 100644 --- a/src/chip/srtc/srtc.cpp +++ b/src/chip/srtc/srtc.cpp @@ -1,4 +1,6 @@ #include <../base.hpp> +#include <../cart/cart.hpp> +#include "srtc.hpp" const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -21,14 +23,26 @@ void SRTC::reset() { } void SRTC::update_time() { - time_t rtc_time; - rtc_time = memory::cartrtc.read(16); - rtc_time |= memory::cartrtc.read(17) << 8; - rtc_time |= memory::cartrtc.read(18) << 16; - rtc_time |= memory::cartrtc.read(19) << 24; - + time_t rtc_time + = (memory::cartrtc.read(16) << 0) + | (memory::cartrtc.read(17) << 8) + | (memory::cartrtc.read(18) << 16) + | (memory::cartrtc.read(19) << 24); time_t current_time = time(0); - if(current_time > rtc_time) { + + //sizeof(time_t) is platform-dependent; though memory::cartrtc needs to be platform-agnostic. + //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by + //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow + //memory::cartrtc timestamp to remain valid for up to ~34 years from the last update, even if + //time_t overflows. calculation should be valid regardless of number representation, time_t size, + //or whether time_t is signed or unsigned. + time_t diff + = (current_time >= rtc_time) + ? (current_time - rtc_time) + : (std::numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > std::numeric_limits::max() / 2) diff = 0; //compensate for underflow + + if(diff > 0) { unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; @@ -41,8 +55,7 @@ void SRTC::update_time() { month--; year += 1000; - second += (unsigned)(current_time - rtc_time); - + second += diff; while(second >= 60) { second -= 60; @@ -94,8 +107,8 @@ void SRTC::update_time() { memory::cartrtc.write(12, weekday % 7); } - memory::cartrtc.write(16, current_time); - memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(16, current_time >> 0); + memory::cartrtc.write(17, current_time >> 8); memory::cartrtc.write(18, current_time >> 16); memory::cartrtc.write(19, current_time >> 24); } @@ -104,8 +117,8 @@ void SRTC::update_time() { //eg 0 = Sunday, 1 = Monday, ... 6 = Saturday //usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008 unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) { - unsigned y = 1900, m = 1; //epoch is 1900-01-01 - unsigned sum = 0; //number of days passed since epoch + unsigned y = 1900, m = 1; //epoch is 1900-01-01 + unsigned sum = 0; //number of days passed since epoch year = max(1900, year); month = max(1, min(12, month)); @@ -136,7 +149,7 @@ unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) { } sum += day - 1; - return (sum + 1) % 7; //1900-01-01 was a Monday + return (sum + 1) % 7; //1900-01-01 was a Monday } uint8 SRTC::mmio_read(unsigned addr) { @@ -164,7 +177,7 @@ void SRTC::mmio_write(unsigned addr, uint8 data) { addr &= 0xffff; if(addr == 0x2801) { - data &= 0x0f; //only the low four bits are used + data &= 0x0f; //only the low four bits are used if(data == 0x0d) { rtc_mode = RTCM_Read; @@ -177,7 +190,7 @@ void SRTC::mmio_write(unsigned addr, uint8 data) { return; } - if(data == 0x0f) return; //unknown behavior + if(data == 0x0f) return; //unknown behavior if(rtc_mode == RTCM_Write) { if(rtc_index >= 0 && rtc_index < 12) { diff --git a/src/chip/st010/st010.cpp b/src/chip/st010/st010.cpp index 1fa4a724..c0c8f906 100644 --- a/src/chip/st010/st010.cpp +++ b/src/chip/st010/st010.cpp @@ -1,6 +1,7 @@ #include <../base.hpp> #define ST010_CPP +#include "st010.hpp" #include "st010_data.hpp" #include "st010_op.cpp" @@ -62,11 +63,11 @@ void ST010::reset() { // -uint8 ST010::read(uint addr) { +uint8 ST010::read(unsigned addr) { return readb(addr); } -void ST010::write(uint addr, uint8 data) { +void ST010::write(unsigned addr, uint8 data) { writeb(addr, data); if((addr & 0xfff) == 0x0021 && (data & 0x80)) { diff --git a/src/chip/st010/st010.hpp b/src/chip/st010/st010.hpp index dbabdee5..d4445cea 100644 --- a/src/chip/st010/st010.hpp +++ b/src/chip/st010/st010.hpp @@ -5,8 +5,8 @@ public: void power(); void reset(); - uint8 read (uint addr); - void write(uint addr, uint8 data); + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); private: uint8 ram[0x1000]; diff --git a/src/chip/st010/st010_op.cpp b/src/chip/st010/st010_op.cpp index 1eb9c55c..77b4a9c1 100644 --- a/src/chip/st010/st010_op.cpp +++ b/src/chip/st010/st010_op.cpp @@ -258,4 +258,4 @@ int16 x1, y1; writew(0x0012, y1); } -#endif //ifdef ST010_CPP +#endif diff --git a/src/clean.bat b/src/clean.bat index 3a500869..1d563cce 100644 --- a/src/clean.bat +++ b/src/clean.bat @@ -1 +1 @@ -@make platform=win compiler=mingw32-gcc clean +@mingw32-make platform=win compiler=mingw32-gcc clean diff --git a/src/config/config.cpp b/src/config/config.cpp deleted file mode 100644 index dab0868b..00000000 --- a/src/config/config.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include <../base.hpp> - -namespace config { - -configuration& config() { - static configuration config; - return config; -} - -string filepath(const char *filename, const char *pathname) { - //if no pathname, return filename as-is - string file(filename); - replace(file, "\\", "/"); - if(!pathname || !*pathname) return file; - - //ensure path ends with trailing '/' - string path(pathname); - replace(path, "\\", "/"); - if(!strend(path, "/")) strcat(path, "/"); - - //replace relative path with absolute path - if(strbegin(path, "./")) { - ltrim(path, "./"); - path = string() << config::path.base << path; - } - - //remove folder part of filename - lstring part; - split(part, "/", file); - return path << part[count(part) - 1]; -} - -integral_setting File::autodetect_type(config(), "file.autodetect_type", - "Auto-detect file type by inspecting file header, rather than by file extension.\n" - "In other words, if a .zip file is renamed to .smc, it will still be correctly " - "identified as a .zip file. However, there is an infinitesimal (1:~500,000,000) " - "chance of a false detection when loading an uncompressed image file, if this " - "option is enabled.", - integral_setting::boolean, false); - -integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32", - "UPS patches contain CRC32s to validate that a patch was applied successfully.\n" - "By default, if this validation fails, said patch will not be applied. " - "Setting this option to true will bypass the validation, " - "which may or may not result in a working image. " - "Thus, enabling this option is strongly discouraged.", - integral_setting::boolean, false); - -string_setting Path::base("path.base", "Path that bsnes resides in", ""); -string_setting Path::user("path.user", "Path to user folder", ""); - -string_setting Path::rom(config(), "path.rom", - "Default path to look for ROM files in (\"\" = use default directory)", ""); -string_setting Path::save(config(), "path.save", - "Default path for all save RAM files (\"\" = use current directory)", ""); -string_setting Path::patch(config(), "path.patch", - "Default path for all UPS patch files (\"\" = use current directory)", ""); -string_setting Path::cheat(config(), "path.cheat", - "Default path for all cheat files (\"\" = use current directory)", ""); -string_setting Path::exportdata(config(), "path.export", - "Default path for all exported data files\n", ""); - -string_setting Path::bsx(config(), "path.bsx", "", ""); -string_setting Path::st(config(), "path.st", "", ""); - -integral_setting SNES::controller_port1(config(), "snes.controller_port1", - "Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceJoypad); -integral_setting SNES::controller_port2(config(), "snes.controller_port2", - "Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceJoypad); -integral_setting SNES::expansion_port(config(), "snes.expansion_port", - "Device attached to SNES expansion port\n" - "0 = None\n" - "1 = Satellaview BS-X", - integral_setting::decimal, ::SNES::ExpansionBSX); -integral_setting SNES::region(config(), "snes.region", - "SNES regional model\n" - "0 = Auto-detect based on cartridge\n" - "1 = NTSC\n" - "2 = PAL", - integral_setting::decimal, ::SNES::Autodetect); - -integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate", - "NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272); -integral_setting CPU::pal_clock_rate(config(), "cpu.pal_clock_rate", - "PAL S-CPU clock rate (in hz)", integral_setting::decimal, 21281370); -integral_setting CPU::wram_init_value(config(), "cpu.wram_init_value", - "Value to initialize 128k WRAM to upon power cycle.\n" - "Note that on real hardware, this value is undefined; meaning it can vary " - "per power-on, and per SNES unit. Such randomness is undesirable for an " - "emulator, so a static value is needed. There is also some form of pattern " - "to the randomness that has yet to be determined, which some games rely upon.\n" - "A value of 0x55 is safe for all known commercial software, and should be used. " - "However, some software written for SNES copiers, or backup units, relies on " - "WRAM being initialized to 0x00; which was a side-effect of the BIOS program " - "which executed on these copiers. Using 0x00 will therefore fix many homebrew " - "programs, but *will* break some poorly programmed commercial software titles, " - "which do not properly initialize WRAM upon power cycle.", - integral_setting::hex, 0x55); - -integral_setting SMP::ntsc_clock_rate(config(), "smp.ntsc_clock_rate", - "NTSC S-SMP clock rate (in hz)", integral_setting::decimal, 32041 * 768); -integral_setting SMP::pal_clock_rate(config(), "smp.pal_clock_rate", - "PAL S-SMP clock rate (in hz)", integral_setting::decimal, 32041 * 768); - -integral_setting Temp::alu_mul_delay(config(), "temp.alu_mul_delay", - "", integral_setting::decimal, 32); -integral_setting Temp::alu_div_delay(config(), "temp.alu_div_delay", - "", integral_setting::decimal, 64); - -} //namespace config diff --git a/src/config/config.hpp b/src/config/config.hpp deleted file mode 100644 index 1ff13109..00000000 --- a/src/config/config.hpp +++ /dev/null @@ -1,38 +0,0 @@ -namespace config { - -extern configuration& config(); - -string filepath(const char *filename, const char *pathname); - -extern struct File { - static integral_setting autodetect_type; - static integral_setting bypass_patch_crc32; -} file; - -extern struct Path { - static string_setting base, user; - static string_setting rom, save, patch, cheat, exportdata; - static string_setting bsx, st; -} path; - -extern struct SNES { - static integral_setting controller_port1; - static integral_setting controller_port2; - static integral_setting expansion_port; - static integral_setting region; -} snes; - -extern struct CPU { - static integral_setting ntsc_clock_rate, pal_clock_rate; - static integral_setting wram_init_value; -} cpu; - -extern struct SMP { - static integral_setting ntsc_clock_rate, pal_clock_rate; -} smp; - -extern struct Temp { - static integral_setting alu_mul_delay, alu_div_delay; -} temp; - -}; diff --git a/src/cpu/scpu/core/core.cpp b/src/cpu/scpu/core/core.cpp index 4ec0d0c4..b746d6bc 100644 --- a/src/cpu/scpu/core/core.cpp +++ b/src/cpu/scpu/core/core.cpp @@ -10,14 +10,14 @@ void sCPU::enter() { add_clocks(186); loop: - if(event.irq) { - event.irq = false; - if(status.nmi_pending == true) { + if(status.interrupt_pending) { + status.interrupt_pending = false; + if(status.nmi_pending) { status.nmi_pending = false; - event.irq_vector = (regs.e == false) ? 0xffea : 0xfffa; - } else if(status.irq_pending == true) { + status.interrupt_vector = (regs.e == false ? 0xffea : 0xfffa); + } else if(status.irq_pending) { status.irq_pending = false; - event.irq_vector = (regs.e == false) ? 0xffee : 0xfffe; + status.interrupt_vector = (regs.e == false ? 0xffee : 0xfffe); } op_irq(); } @@ -40,15 +40,15 @@ void sCPU::enter() { void sCPU::op_irq() { op_read(regs.pc.d); op_io(); - if(!regs.e)op_writestack(regs.pc.b); + if(!regs.e) op_writestack(regs.pc.b); op_writestack(regs.pc.h); op_writestack(regs.pc.l); op_writestack(regs.e ? (regs.p & ~0x10) : regs.p); - rd.l = op_read(event.irq_vector + 0); + rd.l = op_read(status.interrupt_vector + 0); regs.pc.b = 0x00; regs.p.i = 1; regs.p.d = 0; - rd.h = op_read(event.irq_vector + 1); + rd.h = op_read(status.interrupt_vector + 1); regs.pc.w = rd.w; } @@ -57,11 +57,11 @@ void sCPU::op_irq() { //this affects the following opcodes: // clc, cld, cli, clv, sec, sed, sei, // tax, tay, txa, txy, tya, tyx, -// tcd, tcs, tdc, tsc, tsx, tcs, +// tcd, tcs, tdc, tsc, tsx, txs, // inc, inx, iny, dec, dex, dey, // asl, lsr, rol, ror, nop, xce. alwaysinline void sCPU::op_io_irq() { - if(event.irq) { + if(status.interrupt_pending) { //IRQ pending, modify I/O cycle to bus read cycle, do not increment PC op_read(regs.pc.d); } else { @@ -87,4 +87,4 @@ alwaysinline void sCPU::op_io_cond6(uint16 addr) { } } -#endif //ifdef SCPU_CPP +#endif diff --git a/src/cpu/scpu/core/op_misc.b b/src/cpu/scpu/core/op_misc.b index 0e53d147..491b14af 100644 --- a/src/cpu/scpu/core/op_misc.b +++ b/src/cpu/scpu/core/op_misc.b @@ -62,9 +62,9 @@ stp(0xdb) { } wai(0xcb) { -//last_cycle() will clear event.wai once an NMI / IRQ edge is reached -1:event.wai = true; - while(event.wai) { +//last_cycle() will clear status.wai_lock once an NMI / IRQ edge is reached +1:status.wai_lock = true; + while(status.wai_lock) { last_cycle(); op_io(); } diff --git a/src/cpu/scpu/core/op_misc.cpp b/src/cpu/scpu/core/op_misc.cpp index 98bc7567..ef1fc98c 100644 --- a/src/cpu/scpu/core/op_misc.cpp +++ b/src/cpu/scpu/core/op_misc.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + //nop case 0xea: { last_cycle(); @@ -103,9 +105,9 @@ case 0xdb: { //wai case 0xcb: { - //last_cycle() will clear event.wai once an NMI / IRQ edge is reached - event.wai = true; - while(event.wai) { + //last_cycle() will clear status.wai_lock once an NMI / IRQ edge is reached + status.wai_lock = true; + while(status.wai_lock) { last_cycle(); op_io(); } @@ -534,3 +536,4 @@ case 0x62: { if(regs.e) regs.s.h = 0x01; } break; +#endif diff --git a/src/cpu/scpu/core/op_pc.cpp b/src/cpu/scpu/core/op_pc.cpp index 761bb228..9588206a 100644 --- a/src/cpu/scpu/core/op_pc.cpp +++ b/src/cpu/scpu/core/op_pc.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + //bcc case 0x90: { if(!!regs.p.c) last_cycle(); @@ -274,3 +276,4 @@ case 0x6b: { if(regs.e) regs.s.h = 0x01; } break; +#endif diff --git a/src/cpu/scpu/core/op_read.cpp b/src/cpu/scpu/core/op_read.cpp index 76f8ce72..a486b5f5 100644 --- a/src/cpu/scpu/core/op_read.cpp +++ b/src/cpu/scpu/core/op_read.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + //adc_const case 0x69: { if(regs.p.m) last_cycle(); @@ -1649,3 +1651,4 @@ case 0x89: { regs.p.z = ((rd.w & regs.a.w) == 0); } break; +#endif diff --git a/src/cpu/scpu/core/op_rmw.cpp b/src/cpu/scpu/core/op_rmw.cpp index f04702a4..6d37dd0b 100644 --- a/src/cpu/scpu/core/op_rmw.cpp +++ b/src/cpu/scpu/core/op_rmw.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + //inc case 0x1a: { last_cycle(); @@ -568,3 +570,4 @@ case 0x76: { op_writedp(dp + regs.x.w, rd.l); } break; +#endif diff --git a/src/cpu/scpu/core/op_write.cpp b/src/cpu/scpu/core/op_write.cpp index 438e6767..d2362e62 100644 --- a/src/cpu/scpu/core/op_write.cpp +++ b/src/cpu/scpu/core/op_write.cpp @@ -1,3 +1,5 @@ +#ifdef SCPU_CPP + //sta_addr case 0x8d: { aa.l = op_readpc(); @@ -288,3 +290,4 @@ case 0x93: { op_writedbr(aa.w + regs.y.w + 1, regs.a.h); } break; +#endif diff --git a/src/cpu/scpu/deltaqueue.cpp b/src/cpu/scpu/deltaqueue.cpp deleted file mode 100644 index 6fd321e1..00000000 --- a/src/cpu/scpu/deltaqueue.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifdef SCPU_CPP - -//delta queue scheduler is used to time multiple events with the following properties: -//O(1) test (tick) -//O(log n) insert (enqueue) -//O(log n) remove (dequeue) -// -//notes: -//implementation uses binary min-heap array; -//tick() uses 2's-complement signed math to avoid overflow errors; -//enqueue() takes relative timestamps, which are converted to absolute times internally. - -class deltaqueue { - enum { queuesize = 64 }; //maximum number of events that can be queued at the same time. - -public: - alwaysinline void tick(unsigned ticks) { - counter += ticks; - - while(heapsize) { - if((signed)(counter - heap[0].counter) < 0) break; //if(counter < heap[0].counter) break; - unsigned event = heap[0].event; - dequeue(); - cpu.queue_event(event); - } - } - - void enqueue(unsigned event, unsigned ticks) { - unsigned child = heapsize++; - - heap[child].event = event; - heap[child].counter = counter + ticks; - - while(child) { - unsigned parent = (child - 1) >> 1; - if(heap[child].counter >= heap[parent].counter) break; - swap(parent, child); - child = parent; - } - } - - void dequeue() { - heap[0].counter = heap[--heapsize].counter; - heap[0].event = heap[heapsize].event; - heap[heapsize].counter = ~0; - - unsigned parent = 0; - while(true) { - unsigned child = (parent << 1) + 1; - if(heap[child + 1].counter < heap[child].counter) child++; - if(heap[parent].counter <= heap[child].counter) break; - swap(parent, child); - parent = child; - } - } - - void reset() { - counter = 0; - heapsize = 0; - for(unsigned i = 0; i < queuesize << 1; i++) heap[i].counter = ~0; - } - - deltaqueue() { - reset(); - } - -public: - unsigned counter; - unsigned heapsize; - - struct { - unsigned counter; - unsigned event; - } heap[queuesize << 1]; - - void swap(unsigned x, unsigned y) { - unsigned counter = heap[x].counter; - unsigned event = heap[x].event; - - heap[x].counter = heap[y].counter; - heap[x].event = heap[y].event; - - heap[y].counter = counter; - heap[y].event = event; - } -}; - -#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/dma/dma.cpp b/src/cpu/scpu/dma/dma.cpp index a5888bee..de73d563 100644 --- a/src/cpu/scpu/dma/dma.cpp +++ b/src/cpu/scpu/dma/dma.cpp @@ -1,6 +1,6 @@ #ifdef SCPU_CPP -void sCPU::dma_add_clocks(uint clocks) { +void sCPU::dma_add_clocks(unsigned clocks) { status.dma_clocks += clocks; add_clocks(clocks); } @@ -122,7 +122,7 @@ void sCPU::dma_run() { } status.irq_lock = true; - delta.enqueue(EventIrqLockRelease, 2); + event.enqueue(2, EventIrqLockRelease); } /***** @@ -205,7 +205,7 @@ void sCPU::hdma_run() { } status.irq_lock = true; - delta.enqueue(EventIrqLockRelease, 2); + event.enqueue(2, EventIrqLockRelease); } void sCPU::hdma_init_reset() { @@ -227,7 +227,7 @@ void sCPU::hdma_init() { } status.irq_lock = true; - delta.enqueue(EventIrqLockRelease, 2); + event.enqueue(2, EventIrqLockRelease); } /***** diff --git a/src/cpu/scpu/dma/dma.hpp b/src/cpu/scpu/dma/dma.hpp index 3a7af05e..79102f05 100644 --- a/src/cpu/scpu/dma/dma.hpp +++ b/src/cpu/scpu/dma/dma.hpp @@ -45,7 +45,7 @@ bool hdma_do_transfer; } channel[8]; - void dma_add_clocks(uint clocks); + void dma_add_clocks(unsigned clocks); bool dma_addr_valid(uint32 abus); uint8 dma_read(uint32 abus); void dma_transfer(bool direction, uint8 bbus, uint32 abus); diff --git a/src/cpu/scpu/mmio/mmio.cpp b/src/cpu/scpu/mmio/mmio.cpp index 5fc033ad..7441cb26 100644 --- a/src/cpu/scpu/mmio/mmio.cpp +++ b/src/cpu/scpu/mmio/mmio.cpp @@ -1,538 +1,534 @@ #ifdef SCPU_CPP -uint8 sCPU::pio() { return status.pio; } -bool sCPU::joylatch() { return status.joypad_strobe_latch; } - -//WMDATA -uint8 sCPU::mmio_r2180() { - uint8 r = bus.read(0x7e0000 | status.wram_addr); - status.wram_addr = (status.wram_addr + 1) & 0x01ffff; - return r; -} - -//WMDATA -void sCPU::mmio_w2180(uint8 data) { - bus.write(0x7e0000 | status.wram_addr, data); - status.wram_addr = (status.wram_addr + 1) & 0x01ffff; -} - -//WMADDL -void sCPU::mmio_w2181(uint8 data) { - status.wram_addr = (status.wram_addr & 0xffff00) | (data); - status.wram_addr &= 0x01ffff; -} - -//WMADDM -void sCPU::mmio_w2182(uint8 data) { - status.wram_addr = (status.wram_addr & 0xff00ff) | (data << 8); - status.wram_addr &= 0x01ffff; -} - -//WMADDH -void sCPU::mmio_w2183(uint8 data) { - status.wram_addr = (status.wram_addr & 0x00ffff) | (data << 16); - status.wram_addr &= 0x01ffff; -} - -//JOYSER0 -//bit 0 is shared between JOYSER0 and JOYSER1, therefore -//strobing $4016.d0 affects both controller port latches. -//$4017 bit 0 writes are ignored. -void sCPU::mmio_w4016(uint8 data) { - status.joypad_strobe_latch = !!(data & 1); - - if(status.joypad_strobe_latch == 1) { - snes.input.poll(); - } -} - -//JOYSER0 -//7-2 = MDR -//1-0 = Joypad serial data -// -//TODO: test whether strobe latch of zero returns -//realtime or buffered status of joypadN.b -uint8 sCPU::mmio_r4016() { - uint8 r = regs.mdr & 0xfc; - r |= snes.input.port_read(0) & 3; - return r; -} - -//JOYSER1 -//7-5 = MDR -//4-2 = Always 1 (pins are connected to GND) -//1-0 = Joypad serial data -uint8 sCPU::mmio_r4017() { - uint8 r = (regs.mdr & 0xe0) | 0x1c; - r |= snes.input.port_read(1) & 3; - return r; -} - -//NMITIMEN -void sCPU::mmio_w4200(uint8 data) { - status.auto_joypad_poll = !!(data & 0x01); - nmitimen_update(data); -} - -//WRIO -void sCPU::mmio_w4201(uint8 data) { - if((status.pio & 0x80) && !(data & 0x80)) { - ppu.latch_counters(); - } - status.pio = data; -} - -//WRMPYA -void sCPU::mmio_w4202(uint8 data) { - status.mul_a = data; -} - -//WRMPYB -void sCPU::mmio_w4203(uint8 data) { - status.mul_b = data; - status.r4216 = status.mul_a * status.mul_b; - - status.alu_lock = true; - delta.enqueue(EventAluLockRelease, temp_.alu_mul_delay); -} - -//WRDIVL -void sCPU::mmio_w4204(uint8 data) { - status.div_a = (status.div_a & 0xff00) | (data); -} - -//WRDIVH -void sCPU::mmio_w4205(uint8 data) { - status.div_a = (status.div_a & 0x00ff) | (data << 8); -} - -//WRDIVB -void sCPU::mmio_w4206(uint8 data) { - status.div_b = data; - status.r4214 = (status.div_b) ? status.div_a / status.div_b : 0xffff; - status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a; - - status.alu_lock = true; - delta.enqueue(EventAluLockRelease, temp_.alu_div_delay); -} - -//HTIMEL -void sCPU::mmio_w4207(uint8 data) { - status.hirq_pos = (status.hirq_pos & ~0xff) | (data); - status.hirq_pos &= 0x01ff; - hvtime_update(0x4207); -} - -//HTIMEH -void sCPU::mmio_w4208(uint8 data) { - status.hirq_pos = (status.hirq_pos & 0xff) | (data << 8); - status.hirq_pos &= 0x01ff; - hvtime_update(0x4208); -} - -//VTIMEL -void sCPU::mmio_w4209(uint8 data) { - status.virq_pos = (status.virq_pos & ~0xff) | (data); - status.virq_pos &= 0x01ff; - hvtime_update(0x4209); -} - -//VTIMEH -void sCPU::mmio_w420a(uint8 data) { - status.virq_pos = (status.virq_pos & 0xff) | (data << 8); - status.virq_pos &= 0x01ff; - hvtime_update(0x420a); -} - -//DMAEN -void sCPU::mmio_w420b(uint8 data) { - for(unsigned i = 0; i < 8; i++) { - channel[i].dma_enabled = data & (1 << i); - } - if(data) status.dma_pending = true; -} - -//HDMAEN -void sCPU::mmio_w420c(uint8 data) { - for(unsigned i = 0; i < 8; i++) { - channel[i].hdma_enabled = data & (1 << i); - } -} - -//MEMSEL -void sCPU::mmio_w420d(uint8 data) { - bus.set_speed(data & 1); -} - -//RDNMI -//7 = NMI acknowledge -//6-4 = MDR -//3-0 = CPU (5a22) version -uint8 sCPU::mmio_r4210() { - uint8 r = (regs.mdr & 0x70); - r |= (uint8)(rdnmi()) << 7; - r |= (cpu_version & 0x0f); - return r; -} - -//TIMEUP -//7 = IRQ acknowledge -//6-0 = MDR -uint8 sCPU::mmio_r4211() { - uint8 r = (regs.mdr & 0x7f); - r |= (uint8)(timeup()) << 7; - return r; -} - -//HVBJOY -//7 = VBLANK acknowledge -//6 = HBLANK acknowledge -//5-1 = MDR -//0 = JOYPAD acknowledge -uint8 sCPU::mmio_r4212() { - uint8 r = (regs.mdr & 0x3e); - uint16 vs = ppu.overscan() == false ? 225 : 240; - - //auto joypad polling - if(ppu.vcounter() >= vs && ppu.vcounter() <= (vs + 2))r |= 0x01; - - //hblank - if(ppu.hcounter() <= 2 || ppu.hcounter() >= 1096)r |= 0x40; - - //vblank - if(ppu.vcounter() >= vs)r |= 0x80; - - return r; -} - -//RDIO -uint8 sCPU::mmio_r4213() { - return status.pio; -} - -//RDDIVL -uint8 sCPU::mmio_r4214() { - if(status.alu_lock) return 0; - return status.r4214; -} - -//RDDIVH -uint8 sCPU::mmio_r4215() { - if(status.alu_lock) return 0; - return status.r4214 >> 8; -} - -//RDMPYL -uint8 sCPU::mmio_r4216() { - if(status.alu_lock) return 0; - return status.r4216; -} - -//RDMPYH -uint8 sCPU::mmio_r4217() { - if(status.alu_lock) return 0; - return status.r4216 >> 8; -} - -//TODO: handle reads during joypad polling (v=225-227) -uint8 sCPU::mmio_r4218() { return status.joy1l; } //JOY1L -uint8 sCPU::mmio_r4219() { return status.joy1h; } //JOY1H -uint8 sCPU::mmio_r421a() { return status.joy2l; } //JOY2L -uint8 sCPU::mmio_r421b() { return status.joy2h; } //JOY2H -uint8 sCPU::mmio_r421c() { return status.joy3l; } //JOY3L -uint8 sCPU::mmio_r421d() { return status.joy3h; } //JOY3H -uint8 sCPU::mmio_r421e() { return status.joy4l; } //JOY4L -uint8 sCPU::mmio_r421f() { return status.joy4h; } //JOY4H - -//DMAPx -uint8 sCPU::mmio_r43x0(uint8 i) { - return channel[i].dmap; -} - -//BBADx -uint8 sCPU::mmio_r43x1(uint8 i) { - return channel[i].destaddr; -} - -//A1TxL -uint8 sCPU::mmio_r43x2(uint8 i) { - return channel[i].srcaddr; -} - -//A1TxH -uint8 sCPU::mmio_r43x3(uint8 i) { - return channel[i].srcaddr >> 8; -} - -//A1Bx -uint8 sCPU::mmio_r43x4(uint8 i) { - return channel[i].srcbank; -} - -//DASxL -//union { uint16 xfersize; uint16 hdma_iaddr; }; -uint8 sCPU::mmio_r43x5(uint8 i) { - return channel[i].xfersize; -} - -//DASxH -//union { uint16 xfersize; uint16 hdma_iaddr; }; -uint8 sCPU::mmio_r43x6(uint8 i) { - return channel[i].xfersize >> 8; -} - -//DASBx -uint8 sCPU::mmio_r43x7(uint8 i) { - return channel[i].hdma_ibank; -} - -//A2AxL -uint8 sCPU::mmio_r43x8(uint8 i) { - return channel[i].hdma_addr; -} - -//A2AxH -uint8 sCPU::mmio_r43x9(uint8 i) { - return channel[i].hdma_addr >> 8; -} - -//NTRLx -uint8 sCPU::mmio_r43xa(uint8 i) { - return channel[i].hdma_line_counter; -} - -//??? -uint8 sCPU::mmio_r43xb(uint8 i) { - return channel[i].unknown; -} - -//DMAPx -void sCPU::mmio_w43x0(uint8 i, uint8 data) { - channel[i].dmap = data; - channel[i].direction = !!(data & 0x80); - channel[i].hdma_indirect = !!(data & 0x40); - channel[i].reversexfer = !!(data & 0x10); - channel[i].fixedxfer = !!(data & 0x08); - channel[i].xfermode = data & 7; -} - -//DDBADx -void sCPU::mmio_w43x1(uint8 i, uint8 data) { - channel[i].destaddr = data; -} - -//A1TxL -void sCPU::mmio_w43x2(uint8 i, uint8 data) { - channel[i].srcaddr = (channel[i].srcaddr & 0xff00) | (data); -} - -//A1TxH -void sCPU::mmio_w43x3(uint8 i, uint8 data) { - channel[i].srcaddr = (channel[i].srcaddr & 0x00ff) | (data << 8); -} - -//A1Bx -void sCPU::mmio_w43x4(uint8 i, uint8 data) { - channel[i].srcbank = data; -} - -//DASxL -//union { uint16 xfersize; uint16 hdma_iaddr; }; -void sCPU::mmio_w43x5(uint8 i, uint8 data) { - channel[i].xfersize = (channel[i].xfersize & 0xff00) | (data); -} - -//DASxH -//union { uint16 xfersize; uint16 hdma_iaddr; }; -void sCPU::mmio_w43x6(uint8 i, uint8 data) { - channel[i].xfersize = (channel[i].xfersize & 0x00ff) | (data << 8); -} - -//DASBx -void sCPU::mmio_w43x7(uint8 i, uint8 data) { - channel[i].hdma_ibank = data; -} - -//A2AxL -void sCPU::mmio_w43x8(uint8 i, uint8 data) { - channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data); -} - -//A2AxH -void sCPU::mmio_w43x9(uint8 i, uint8 data) { - channel[i].hdma_addr = (channel[i].hdma_addr & 0x00ff) | (data << 8); -} - -//NTRLx -void sCPU::mmio_w43xa(uint8 i, uint8 data) { - channel[i].hdma_line_counter = data; -} - -//??? -void sCPU::mmio_w43xb(uint8 i, uint8 data) { - channel[i].unknown = data; -} - -void sCPU::mmio_power() { -} - -void sCPU::mmio_reset() { - //$2181-$2183 - status.wram_addr = 0x000000; - - //$4016-$4017 - status.joypad_strobe_latch = 0; - status.joypad1_bits = ~0; - status.joypad2_bits = ~0; - - //$4200 - status.nmi_enabled = false; - status.hirq_enabled = false; - status.virq_enabled = false; - status.auto_joypad_poll = false; - - //$4201 - status.pio = 0xff; - - //$4202-$4203 - status.mul_a = 0xff; - status.mul_b = 0xff; - - //$4204-$4206 - status.div_a = 0xffff; - status.div_b = 0xff; - - //$4207-$420a - status.hirq_pos = 0x01ff; - status.virq_pos = 0x01ff; - - //$4214-$4217 - status.r4214 = 0x0000; - status.r4216 = 0x0000; - - //$4218-$421f - status.joy1l = 0x00; - status.joy1h = 0x00; - status.joy2l = 0x00; - status.joy2h = 0x00; - status.joy3l = 0x00; - status.joy3h = 0x00; - status.joy4l = 0x00; - status.joy4h = 0x00; -} - -uint8 sCPU::mmio_read(uint addr) { - addr &= 0xffff; - - //APU - if((addr & 0xffc0) == 0x2140) { //$2140-$217f - scheduler.sync_cpusmp(); - return smp.port_read(addr & 3); - } - - //DMA - if((addr & 0xff80) == 0x4300) { //$4300-$437f - uint i = (addr >> 4) & 7; - switch(addr & 0xf) { - case 0x0: return mmio_r43x0(i); - case 0x1: return mmio_r43x1(i); - case 0x2: return mmio_r43x2(i); - case 0x3: return mmio_r43x3(i); - case 0x4: return mmio_r43x4(i); - case 0x5: return mmio_r43x5(i); - case 0x6: return mmio_r43x6(i); - case 0x7: return mmio_r43x7(i); - case 0x8: return mmio_r43x8(i); - case 0x9: return mmio_r43x9(i); - case 0xa: return mmio_r43xa(i); - case 0xb: return mmio_r43xb(i); - case 0xc: return regs.mdr; //unmapped - case 0xd: return regs.mdr; //unmapped - case 0xe: return regs.mdr; //unmapped - case 0xf: return mmio_r43xb(i); //mirror of $43xb - } - } - - switch(addr) { - case 0x2180: return mmio_r2180(); - case 0x4016: return mmio_r4016(); - case 0x4017: return mmio_r4017(); - case 0x4210: return mmio_r4210(); - case 0x4211: return mmio_r4211(); - case 0x4212: return mmio_r4212(); - case 0x4213: return mmio_r4213(); - case 0x4214: return mmio_r4214(); - case 0x4215: return mmio_r4215(); - case 0x4216: return mmio_r4216(); - case 0x4217: return mmio_r4217(); - case 0x4218: return mmio_r4218(); - case 0x4219: return mmio_r4219(); - case 0x421a: return mmio_r421a(); - case 0x421b: return mmio_r421b(); - case 0x421c: return mmio_r421c(); - case 0x421d: return mmio_r421d(); - case 0x421e: return mmio_r421e(); - case 0x421f: return mmio_r421f(); - } - - return regs.mdr; -} - -void sCPU::mmio_write(uint addr, uint8 data) { - addr &= 0xffff; - - //APU - if((addr & 0xffc0) == 0x2140) { //$2140-$217f - scheduler.sync_cpusmp(); - port_write(addr & 3, data); - return; - } - - //DMA - if((addr & 0xff80) == 0x4300) { //$4300-$437f - uint i = (addr >> 4) & 7; - switch(addr & 0xf) { - case 0x0: mmio_w43x0(i, data); return; - case 0x1: mmio_w43x1(i, data); return; - case 0x2: mmio_w43x2(i, data); return; - case 0x3: mmio_w43x3(i, data); return; - case 0x4: mmio_w43x4(i, data); return; - case 0x5: mmio_w43x5(i, data); return; - case 0x6: mmio_w43x6(i, data); return; - case 0x7: mmio_w43x7(i, data); return; - case 0x8: mmio_w43x8(i, data); return; - case 0x9: mmio_w43x9(i, data); return; - case 0xa: mmio_w43xa(i, data); return; - case 0xb: mmio_w43xb(i, data); return; - case 0xc: return; //unmapped - case 0xd: return; //unmapped - case 0xe: return; //unmapped - case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb - } - } - - switch(addr) { - case 0x2180: mmio_w2180(data); return; - case 0x2181: mmio_w2181(data); return; - case 0x2182: mmio_w2182(data); return; - case 0x2183: mmio_w2183(data); return; - case 0x4016: mmio_w4016(data); return; - case 0x4017: return; //unmapped - case 0x4200: mmio_w4200(data); return; - case 0x4201: mmio_w4201(data); return; - case 0x4202: mmio_w4202(data); return; - case 0x4203: mmio_w4203(data); return; - case 0x4204: mmio_w4204(data); return; - case 0x4205: mmio_w4205(data); return; - case 0x4206: mmio_w4206(data); return; - case 0x4207: mmio_w4207(data); return; - case 0x4208: mmio_w4208(data); return; - case 0x4209: mmio_w4209(data); return; - case 0x420a: mmio_w420a(data); return; - case 0x420b: mmio_w420b(data); return; - case 0x420c: mmio_w420c(data); return; - case 0x420d: mmio_w420d(data); return; - } -} +uint8 sCPU::pio() { return status.pio; } +bool sCPU::joylatch() { return status.joypad_strobe_latch; } + +//WMDATA +uint8 sCPU::mmio_r2180() { + uint8 r = bus.read(0x7e0000 | status.wram_addr); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; + return r; +} + +//WMDATA +void sCPU::mmio_w2180(uint8 data) { + bus.write(0x7e0000 | status.wram_addr, data); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; +} + +//WMADDL +void sCPU::mmio_w2181(uint8 data) { + status.wram_addr = (status.wram_addr & 0xffff00) | (data); + status.wram_addr &= 0x01ffff; +} + +//WMADDM +void sCPU::mmio_w2182(uint8 data) { + status.wram_addr = (status.wram_addr & 0xff00ff) | (data << 8); + status.wram_addr &= 0x01ffff; +} + +//WMADDH +void sCPU::mmio_w2183(uint8 data) { + status.wram_addr = (status.wram_addr & 0x00ffff) | (data << 16); + status.wram_addr &= 0x01ffff; +} + +//JOYSER0 +//bit 0 is shared between JOYSER0 and JOYSER1, therefore +//strobing $4016.d0 affects both controller port latches. +//$4017 bit 0 writes are ignored. +void sCPU::mmio_w4016(uint8 data) { + status.joypad_strobe_latch = !!(data & 1); + + if(status.joypad_strobe_latch == 1) { + snes.input.poll(); + } +} + +//JOYSER0 +//7-2 = MDR +//1-0 = Joypad serial data +// +//TODO: test whether strobe latch of zero returns +//realtime or buffered status of joypadN.b +uint8 sCPU::mmio_r4016() { + uint8 r = regs.mdr & 0xfc; + r |= snes.input.port_read(0) & 3; + return r; +} + +//JOYSER1 +//7-5 = MDR +//4-2 = Always 1 (pins are connected to GND) +//1-0 = Joypad serial data +uint8 sCPU::mmio_r4017() { + uint8 r = (regs.mdr & 0xe0) | 0x1c; + r |= snes.input.port_read(1) & 3; + return r; +} + +//NMITIMEN +void sCPU::mmio_w4200(uint8 data) { + status.auto_joypad_poll = !!(data & 0x01); + nmitimen_update(data); +} + +//WRIO +void sCPU::mmio_w4201(uint8 data) { + if((status.pio & 0x80) && !(data & 0x80)) { + ppu.latch_counters(); + } + status.pio = data; +} + +//WRMPYA +void sCPU::mmio_w4202(uint8 data) { + status.mul_a = data; +} + +//WRMPYB +void sCPU::mmio_w4203(uint8 data) { + status.mul_b = data; + status.r4216 = status.mul_a * status.mul_b; + + status.alu_lock = true; + event.enqueue(snes.config.cpu.alu_mul_delay, EventAluLockRelease); +} + +//WRDIVL +void sCPU::mmio_w4204(uint8 data) { + status.div_a = (status.div_a & 0xff00) | (data); +} + +//WRDIVH +void sCPU::mmio_w4205(uint8 data) { + status.div_a = (status.div_a & 0x00ff) | (data << 8); +} + +//WRDIVB +void sCPU::mmio_w4206(uint8 data) { + status.div_b = data; + status.r4214 = (status.div_b) ? status.div_a / status.div_b : 0xffff; + status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a; + + status.alu_lock = true; + event.enqueue(snes.config.cpu.alu_div_delay, EventAluLockRelease); +} + +//HTIMEL +void sCPU::mmio_w4207(uint8 data) { + status.hirq_pos = (status.hirq_pos & ~0xff) | (data); + status.hirq_pos &= 0x01ff; +} + +//HTIMEH +void sCPU::mmio_w4208(uint8 data) { + status.hirq_pos = (status.hirq_pos & 0xff) | (data << 8); + status.hirq_pos &= 0x01ff; +} + +//VTIMEL +void sCPU::mmio_w4209(uint8 data) { + status.virq_pos = (status.virq_pos & ~0xff) | (data); + status.virq_pos &= 0x01ff; +} + +//VTIMEH +void sCPU::mmio_w420a(uint8 data) { + status.virq_pos = (status.virq_pos & 0xff) | (data << 8); + status.virq_pos &= 0x01ff; +} + +//DMAEN +void sCPU::mmio_w420b(uint8 data) { + for(unsigned i = 0; i < 8; i++) { + channel[i].dma_enabled = data & (1 << i); + } + if(data) status.dma_pending = true; +} + +//HDMAEN +void sCPU::mmio_w420c(uint8 data) { + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_enabled = data & (1 << i); + } +} + +//MEMSEL +void sCPU::mmio_w420d(uint8 data) { + bus.set_speed(data & 1); +} + +//RDNMI +//7 = NMI acknowledge +//6-4 = MDR +//3-0 = CPU (5a22) version +uint8 sCPU::mmio_r4210() { + uint8 r = (regs.mdr & 0x70); + r |= (uint8)(rdnmi()) << 7; + r |= (cpu_version & 0x0f); + return r; +} + +//TIMEUP +//7 = IRQ acknowledge +//6-0 = MDR +uint8 sCPU::mmio_r4211() { + uint8 r = (regs.mdr & 0x7f); + r |= (uint8)(timeup()) << 7; + return r; +} + +//HVBJOY +//7 = VBLANK acknowledge +//6 = HBLANK acknowledge +//5-1 = MDR +//0 = JOYPAD acknowledge +uint8 sCPU::mmio_r4212() { + uint8 r = (regs.mdr & 0x3e); + uint16 vs = ppu.overscan() == false ? 225 : 240; + + //auto joypad polling + if(ppu.vcounter() >= vs && ppu.vcounter() <= (vs + 2))r |= 0x01; + + //hblank + if(ppu.hcounter() <= 2 || ppu.hcounter() >= 1096)r |= 0x40; + + //vblank + if(ppu.vcounter() >= vs)r |= 0x80; + + return r; +} + +//RDIO +uint8 sCPU::mmio_r4213() { + return status.pio; +} + +//RDDIVL +uint8 sCPU::mmio_r4214() { + if(status.alu_lock) return 0; + return status.r4214; +} + +//RDDIVH +uint8 sCPU::mmio_r4215() { + if(status.alu_lock) return 0; + return status.r4214 >> 8; +} + +//RDMPYL +uint8 sCPU::mmio_r4216() { + if(status.alu_lock) return 0; + return status.r4216; +} + +//RDMPYH +uint8 sCPU::mmio_r4217() { + if(status.alu_lock) return 0; + return status.r4216 >> 8; +} + +//TODO: handle reads during joypad polling (v=225-227) +uint8 sCPU::mmio_r4218() { return status.joy1l; } //JOY1L +uint8 sCPU::mmio_r4219() { return status.joy1h; } //JOY1H +uint8 sCPU::mmio_r421a() { return status.joy2l; } //JOY2L +uint8 sCPU::mmio_r421b() { return status.joy2h; } //JOY2H +uint8 sCPU::mmio_r421c() { return status.joy3l; } //JOY3L +uint8 sCPU::mmio_r421d() { return status.joy3h; } //JOY3H +uint8 sCPU::mmio_r421e() { return status.joy4l; } //JOY4L +uint8 sCPU::mmio_r421f() { return status.joy4h; } //JOY4H + +//DMAPx +uint8 sCPU::mmio_r43x0(uint8 i) { + return channel[i].dmap; +} + +//BBADx +uint8 sCPU::mmio_r43x1(uint8 i) { + return channel[i].destaddr; +} + +//A1TxL +uint8 sCPU::mmio_r43x2(uint8 i) { + return channel[i].srcaddr; +} + +//A1TxH +uint8 sCPU::mmio_r43x3(uint8 i) { + return channel[i].srcaddr >> 8; +} + +//A1Bx +uint8 sCPU::mmio_r43x4(uint8 i) { + return channel[i].srcbank; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 sCPU::mmio_r43x5(uint8 i) { + return channel[i].xfersize; +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 sCPU::mmio_r43x6(uint8 i) { + return channel[i].xfersize >> 8; +} + +//DASBx +uint8 sCPU::mmio_r43x7(uint8 i) { + return channel[i].hdma_ibank; +} + +//A2AxL +uint8 sCPU::mmio_r43x8(uint8 i) { + return channel[i].hdma_addr; +} + +//A2AxH +uint8 sCPU::mmio_r43x9(uint8 i) { + return channel[i].hdma_addr >> 8; +} + +//NTRLx +uint8 sCPU::mmio_r43xa(uint8 i) { + return channel[i].hdma_line_counter; +} + +//??? +uint8 sCPU::mmio_r43xb(uint8 i) { + return channel[i].unknown; +} + +//DMAPx +void sCPU::mmio_w43x0(uint8 i, uint8 data) { + channel[i].dmap = data; + channel[i].direction = !!(data & 0x80); + channel[i].hdma_indirect = !!(data & 0x40); + channel[i].reversexfer = !!(data & 0x10); + channel[i].fixedxfer = !!(data & 0x08); + channel[i].xfermode = data & 7; +} + +//DDBADx +void sCPU::mmio_w43x1(uint8 i, uint8 data) { + channel[i].destaddr = data; +} + +//A1TxL +void sCPU::mmio_w43x2(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0xff00) | (data); +} + +//A1TxH +void sCPU::mmio_w43x3(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0x00ff) | (data << 8); +} + +//A1Bx +void sCPU::mmio_w43x4(uint8 i, uint8 data) { + channel[i].srcbank = data; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void sCPU::mmio_w43x5(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0xff00) | (data); +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void sCPU::mmio_w43x6(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0x00ff) | (data << 8); +} + +//DASBx +void sCPU::mmio_w43x7(uint8 i, uint8 data) { + channel[i].hdma_ibank = data; +} + +//A2AxL +void sCPU::mmio_w43x8(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data); +} + +//A2AxH +void sCPU::mmio_w43x9(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0x00ff) | (data << 8); +} + +//NTRLx +void sCPU::mmio_w43xa(uint8 i, uint8 data) { + channel[i].hdma_line_counter = data; +} + +//??? +void sCPU::mmio_w43xb(uint8 i, uint8 data) { + channel[i].unknown = data; +} + +void sCPU::mmio_power() { +} + +void sCPU::mmio_reset() { + //$2181-$2183 + status.wram_addr = 0x000000; + + //$4016-$4017 + status.joypad_strobe_latch = 0; + status.joypad1_bits = ~0; + status.joypad2_bits = ~0; + + //$4200 + status.nmi_enabled = false; + status.hirq_enabled = false; + status.virq_enabled = false; + status.auto_joypad_poll = false; + + //$4201 + status.pio = 0xff; + + //$4202-$4203 + status.mul_a = 0xff; + status.mul_b = 0xff; + + //$4204-$4206 + status.div_a = 0xffff; + status.div_b = 0xff; + + //$4207-$420a + status.hirq_pos = 0x01ff; + status.virq_pos = 0x01ff; + + //$4214-$4217 + status.r4214 = 0x0000; + status.r4216 = 0x0000; + + //$4218-$421f + status.joy1l = 0x00; + status.joy1h = 0x00; + status.joy2l = 0x00; + status.joy2h = 0x00; + status.joy3l = 0x00; + status.joy3h = 0x00; + status.joy4l = 0x00; + status.joy4h = 0x00; +} + +uint8 sCPU::mmio_read(unsigned addr) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + scheduler.sync_cpusmp(); + return smp.port_read(addr & 3); + } + + //DMA + if((addr & 0xff80) == 0x4300) { //$4300-$437f + unsigned i = (addr >> 4) & 7; + switch(addr & 0xf) { + case 0x0: return mmio_r43x0(i); + case 0x1: return mmio_r43x1(i); + case 0x2: return mmio_r43x2(i); + case 0x3: return mmio_r43x3(i); + case 0x4: return mmio_r43x4(i); + case 0x5: return mmio_r43x5(i); + case 0x6: return mmio_r43x6(i); + case 0x7: return mmio_r43x7(i); + case 0x8: return mmio_r43x8(i); + case 0x9: return mmio_r43x9(i); + case 0xa: return mmio_r43xa(i); + case 0xb: return mmio_r43xb(i); + case 0xc: return regs.mdr; //unmapped + case 0xd: return regs.mdr; //unmapped + case 0xe: return regs.mdr; //unmapped + case 0xf: return mmio_r43xb(i); //mirror of $43xb + } + } + + switch(addr) { + case 0x2180: return mmio_r2180(); + case 0x4016: return mmio_r4016(); + case 0x4017: return mmio_r4017(); + case 0x4210: return mmio_r4210(); + case 0x4211: return mmio_r4211(); + case 0x4212: return mmio_r4212(); + case 0x4213: return mmio_r4213(); + case 0x4214: return mmio_r4214(); + case 0x4215: return mmio_r4215(); + case 0x4216: return mmio_r4216(); + case 0x4217: return mmio_r4217(); + case 0x4218: return mmio_r4218(); + case 0x4219: return mmio_r4219(); + case 0x421a: return mmio_r421a(); + case 0x421b: return mmio_r421b(); + case 0x421c: return mmio_r421c(); + case 0x421d: return mmio_r421d(); + case 0x421e: return mmio_r421e(); + case 0x421f: return mmio_r421f(); + } + + return regs.mdr; +} + +void sCPU::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + scheduler.sync_cpusmp(); + port_write(addr & 3, data); + return; + } + + //DMA + if((addr & 0xff80) == 0x4300) { //$4300-$437f + unsigned i = (addr >> 4) & 7; + switch(addr & 0xf) { + case 0x0: mmio_w43x0(i, data); return; + case 0x1: mmio_w43x1(i, data); return; + case 0x2: mmio_w43x2(i, data); return; + case 0x3: mmio_w43x3(i, data); return; + case 0x4: mmio_w43x4(i, data); return; + case 0x5: mmio_w43x5(i, data); return; + case 0x6: mmio_w43x6(i, data); return; + case 0x7: mmio_w43x7(i, data); return; + case 0x8: mmio_w43x8(i, data); return; + case 0x9: mmio_w43x9(i, data); return; + case 0xa: mmio_w43xa(i, data); return; + case 0xb: mmio_w43xb(i, data); return; + case 0xc: return; //unmapped + case 0xd: return; //unmapped + case 0xe: return; //unmapped + case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb + } + } + + switch(addr) { + case 0x2180: mmio_w2180(data); return; + case 0x2181: mmio_w2181(data); return; + case 0x2182: mmio_w2182(data); return; + case 0x2183: mmio_w2183(data); return; + case 0x4016: mmio_w4016(data); return; + case 0x4017: return; //unmapped + case 0x4200: mmio_w4200(data); return; + case 0x4201: mmio_w4201(data); return; + case 0x4202: mmio_w4202(data); return; + case 0x4203: mmio_w4203(data); return; + case 0x4204: mmio_w4204(data); return; + case 0x4205: mmio_w4205(data); return; + case 0x4206: mmio_w4206(data); return; + case 0x4207: mmio_w4207(data); return; + case 0x4208: mmio_w4208(data); return; + case 0x4209: mmio_w4209(data); return; + case 0x420a: mmio_w420a(data); return; + case 0x420b: mmio_w420b(data); return; + case 0x420c: mmio_w420c(data); return; + case 0x420d: mmio_w420d(data); return; + } +} #endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/mmio/mmio.hpp b/src/cpu/scpu/mmio/mmio.hpp index 242bba11..839d0a52 100644 --- a/src/cpu/scpu/mmio/mmio.hpp +++ b/src/cpu/scpu/mmio/mmio.hpp @@ -1,7 +1,7 @@ void mmio_power(); void mmio_reset(); - uint8 mmio_read(uint addr); - void mmio_write(uint addr, uint8 data); + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); uint8 pio(); bool joylatch(); diff --git a/src/cpu/scpu/scpu.cpp b/src/cpu/scpu/scpu.cpp index 62118eea..870171c6 100644 --- a/src/cpu/scpu/scpu.cpp +++ b/src/cpu/scpu/scpu.cpp @@ -1,8 +1,8 @@ -#include <../base.hpp> +#include <../base.hpp> #define SCPU_CPP -#include "deltaqueue.cpp" -deltaqueue delta; +#include +priority_queue event(512, bind(&sCPU::queue_event, &cpu)); #include "core/core.cpp" #include "dma/dma.cpp" @@ -36,9 +36,9 @@ void sCPU::reset() { regs.e = 1; regs.mdr = 0x00; - event.wai = false; - event.irq = false; - event.irq_vector = 0xfffc; //reset vector address + status.wai_lock = false; + status.interrupt_pending = false; + status.interrupt_vector = 0xfffc; //reset vector address mmio_reset(); dma_reset(); diff --git a/src/cpu/scpu/scpu.hpp b/src/cpu/scpu/scpu.hpp index 9ed4d6dd..3be415fd 100644 --- a/src/cpu/scpu/scpu.hpp +++ b/src/cpu/scpu/scpu.hpp @@ -8,25 +8,17 @@ public: #include "mmio/mmio.hpp" #include "timing/timing.hpp" - struct { - bool wai; - bool irq; - uint16 irq_vector; - unsigned cycle_edge; - } event; - enum DmaState { DmaInactive, DmaRun, DmaCpuSync }; - struct { - unsigned alu_mul_delay; - unsigned alu_div_delay; - } temp_; - struct { //core uint8 opcode; bool in_opcode; + bool wai_lock; + bool interrupt_pending; + uint16 interrupt_vector; + unsigned clock_count; unsigned line_clocks; @@ -39,16 +31,15 @@ public: bool nmi_line; bool nmi_transition; bool nmi_pending; - unsigned nmi_hold; + bool nmi_hold; - uint16 virq_trigger_pos, hirq_trigger_pos; bool irq_valid; bool irq_line; bool irq_transition; bool irq_pending; - unsigned irq_hold; + bool irq_hold; - //dma + //DMA unsigned dma_counter; unsigned dma_clocks; bool dma_pending; @@ -56,7 +47,7 @@ public: bool hdma_mode; //0 = init, 1 = run DmaState dma_state; - //mmio + //MMIO //$2181-$2183 uint32 wram_addr; diff --git a/src/cpu/scpu/timing/event.cpp b/src/cpu/scpu/timing/event.cpp index 437cbfd0..82de9247 100644 --- a/src/cpu/scpu/timing/event.cpp +++ b/src/cpu/scpu/timing/event.cpp @@ -2,26 +2,34 @@ void sCPU::queue_event(unsigned id) { switch(id) { + //interrupts triggered during (H)DMA do not trigger immediately after case EventIrqLockRelease: { status.irq_lock = false; } break; + //ALU multiplication / division results are not immediately calculated; + //the exact formula for the calculations are unknown, but this lock at least + //allows emulation to avoid returning to fully computed results too soon. case EventAluLockRelease: { status.alu_lock = false; } break; + //S-CPU WRAM consists of two 64kbyte DRAM chips, which must be refreshed + //once per scanline to avoid memory decay. case EventDramRefresh: { add_clocks(40); } break; + //HDMA init routine; occurs once per frame case EventHdmaInit: { - event.cycle_edge |= EventFlagHdmaInit; + cycle_edge_state |= EventFlagHdmaInit; } break; + //HDMA run routine; occurs once per scanline case EventHdmaRun: { - event.cycle_edge |= EventFlagHdmaRun; + cycle_edge_state |= EventFlagHdmaRun; } break; } } -#endif //ifdef SCPU_CPP +#endif diff --git a/src/cpu/scpu/timing/irq.cpp b/src/cpu/scpu/timing/irq.cpp index 7830e78b..5b4a5598 100644 --- a/src/cpu/scpu/timing/irq.cpp +++ b/src/cpu/scpu/timing/irq.cpp @@ -1,65 +1,47 @@ #ifdef SCPU_CPP -void sCPU::update_interrupts() { - unsigned vtime = status.virq_pos; - unsigned htime = status.hirq_enabled ? status.hirq_pos : 0; - unsigned vlimit = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; - - //an IRQ for the very last dot of a field cannot trigger an IRQ - if((vtime == (vlimit - 1) && htime == 339 && ppu.interlace() == false) - || (vtime == vlimit && htime == 339) - ) { - vtime = 0x03ff; - htime = 0x03ff; - } - - status.virq_trigger_pos = vtime; - status.hirq_trigger_pos = 4 * (status.hirq_enabled ? htime + 1 : 0); -} - -alwaysinline void sCPU::poll_interrupts() { - uint16_t vpos, hpos; - +//called once every four clock cycles; +//as NMI steps by scanlines (divisible by 4) and IRQ by PPU 4-cycle dots. +// +//ppu.(vh)counter(n) returns the value of said counters n-clocks before current time; +//it is used to emulate hardware communication delay between opcode and interrupt units. +void sCPU::poll_interrupts() { //NMI hold if(status.nmi_hold) { - status.nmi_hold -= 2; - if(status.nmi_hold == 0) { - if(status.nmi_enabled == true) status.nmi_transition = true; - } + status.nmi_hold = false; + if(status.nmi_enabled) status.nmi_transition = true; } //NMI test - vpos = ppu.vcounter(2); - hpos = ppu.hcounter(2); - bool nmi_valid = (vpos >= (!ppu.overscan() ? 225 : 240)); - if(status.nmi_valid == false && nmi_valid == true) { + bool nmi_valid = (ppu.vcounter(2) >= (!ppu.overscan() ? 225 : 240)); + if(!status.nmi_valid && nmi_valid) { //0->1 edge sensitive transition status.nmi_line = true; - status.nmi_hold = 4; - } else if(status.nmi_valid == true && nmi_valid == false) { + status.nmi_hold = true; //hold /NMI for four cycles + } else if(status.nmi_valid && !nmi_valid) { //1->0 edge sensitive transition status.nmi_line = false; } status.nmi_valid = nmi_valid; //IRQ hold - if(status.irq_hold) status.irq_hold -= 2; - if(status.irq_line == true && status.irq_hold == 0) { - if(status.virq_enabled == true || status.hirq_enabled == true) status.irq_transition = true; + status.irq_hold = false; + if(status.irq_line) { + if(status.virq_enabled || status.hirq_enabled) status.irq_transition = true; } //IRQ test - vpos = ppu.vcounter(10); - hpos = ppu.hcounter(10); - bool irq_valid = (status.virq_enabled == true || status.hirq_enabled == true); - if(irq_valid == true) { - if(status.virq_enabled == true && vpos != status.virq_trigger_pos) irq_valid = false; - if(status.hirq_enabled == true && hpos != status.hirq_trigger_pos) irq_valid = false; + bool irq_valid = (status.virq_enabled || status.hirq_enabled); + if(irq_valid) { + if((status.virq_enabled && ppu.vcounter(10) != (status.virq_pos)) + || (status.hirq_enabled && ppu.hcounter(10) != (status.hirq_pos + 1) * 4) + || (status.virq_pos && ppu.vcounter(6) == 0) //IRQs cannot trigger on last dot of field + ) irq_valid = false; } - if(status.irq_valid == false && irq_valid == true) { + if(!status.irq_valid && irq_valid) { //0->1 edge sensitive transition status.irq_line = true; - status.irq_hold = 4; + status.irq_hold = true; //hold /IRQ for four cycles } status.irq_valid = irq_valid; } @@ -68,37 +50,32 @@ void sCPU::nmitimen_update(uint8 data) { bool nmi_enabled = status.nmi_enabled; bool virq_enabled = status.virq_enabled; bool hirq_enabled = status.hirq_enabled; - status.nmi_enabled = !!(data & 0x80); - status.virq_enabled = !!(data & 0x20); - status.hirq_enabled = !!(data & 0x10); + status.nmi_enabled = data & 0x80; + status.virq_enabled = data & 0x20; + status.hirq_enabled = data & 0x10; //0->1 edge sensitive transition - if(nmi_enabled == false && status.nmi_enabled == true && status.nmi_line == true) { + if(!nmi_enabled && status.nmi_enabled && status.nmi_line) { status.nmi_transition = true; } //?->1 level sensitive transition - if(status.virq_enabled == true && status.hirq_enabled == false && status.irq_line == true) { + if(status.virq_enabled && !status.hirq_enabled && status.irq_line) { status.irq_transition = true; } - if(status.virq_enabled == false && status.hirq_enabled == false) { + if(!status.virq_enabled && !status.hirq_enabled) { status.irq_line = false; status.irq_transition = false; } - update_interrupts(); status.irq_lock = true; - delta.enqueue(EventIrqLockRelease, 2); -} - -void sCPU::hvtime_update(uint16 addr) { - update_interrupts(); + event.enqueue(2, EventIrqLockRelease); } bool sCPU::rdnmi() { bool result = status.nmi_line; - if(status.nmi_hold == 0) { + if(!status.nmi_hold) { status.nmi_line = false; } return result; @@ -106,25 +83,25 @@ bool sCPU::rdnmi() { bool sCPU::timeup() { bool result = status.irq_line; - if(status.irq_hold == 0) { + if(!status.irq_hold) { status.irq_line = false; status.irq_transition = false; } return result; } -alwaysinline bool sCPU::nmi_test() { - if(status.nmi_transition == false) return false; +bool sCPU::nmi_test() { + if(!status.nmi_transition) return false; status.nmi_transition = false; - event.wai = false; + status.wai_lock = false; return true; } -alwaysinline bool sCPU::irq_test() { - if(status.irq_transition == false) return false; +bool sCPU::irq_test() { + if(!status.irq_transition) return false; status.irq_transition = false; - event.wai = false; + status.wai_lock = false; return !regs.p.i; } -#endif //ifdef SCPU_CPP +#endif diff --git a/src/cpu/scpu/timing/joypad.cpp b/src/cpu/scpu/timing/joypad.cpp index 34b9e772..c754c4dc 100644 --- a/src/cpu/scpu/timing/joypad.cpp +++ b/src/cpu/scpu/timing/joypad.cpp @@ -25,4 +25,4 @@ void sCPU::run_auto_joypad_poll() { status.joy4h = joy4 >> 8; } -#endif //ifdef SCPU_CPP +#endif diff --git a/src/cpu/scpu/timing/timing.cpp b/src/cpu/scpu/timing/timing.cpp index c47db9a9..c43ed5fd 100644 --- a/src/cpu/scpu/timing/timing.cpp +++ b/src/cpu/scpu/timing/timing.cpp @@ -9,14 +9,16 @@ unsigned sCPU::dma_counter() { } void sCPU::add_clocks(unsigned clocks) { - delta.tick(clocks); + event.tick(clocks); unsigned ticks = clocks >> 1; while(ticks--) { ppu.tick(); - snes.input.tick(); - poll_interrupts(); + if((ppu.hcounter() & 2) == 0) { + snes.input.tick(); + } else { + poll_interrupts(); + } } - scheduler.addclocks_cpu(clocks); } @@ -26,20 +28,18 @@ void sCPU::scanline() { if(ppu.vcounter() == 0) { //hdma init triggers once every frame - delta.enqueue(EventHdmaInit, cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter()); + event.enqueue(cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter(), EventHdmaInit); } //dram refresh occurs once every scanline if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter(); - delta.enqueue(EventDramRefresh, status.dram_refresh_position); + event.enqueue(status.dram_refresh_position, EventDramRefresh); //hdma triggers once every visible scanline if(ppu.vcounter() <= (ppu.overscan() == false ? 224 : 239)) { - delta.enqueue(EventHdmaRun, 1104); + event.enqueue(1104, EventHdmaRun); } - update_interrupts(); - if(status.auto_joypad_poll == true && ppu.vcounter() == (ppu.overscan() == false ? 227 : 242)) { snes.input.poll(); run_auto_joypad_poll(); @@ -56,8 +56,8 @@ void sCPU::precycle_edge() { //used to test for H/DMA, which can trigger on the edge of every opcode cycle. void sCPU::cycle_edge() { - while(event.cycle_edge) { - switch(event.cycle_edge & -event.cycle_edge) { //switch on lowest bit (flag) set + while(cycle_edge_state) { + switch(bit::lowest(cycle_edge_state)) { case EventFlagHdmaInit: { hdma_init_reset(); if(hdma_enabled_channels()) { @@ -74,7 +74,7 @@ void sCPU::cycle_edge() { } break; } - event.cycle_edge &= event.cycle_edge - 1; //clear lowest bit set + cycle_edge_state = bit::clear_lowest(cycle_edge_state); } //H/DMA pending && DMA inactive? @@ -119,43 +119,39 @@ void sCPU::cycle_edge() { //status.irq_lock is used to simulate hardware delay before interrupts can //trigger during certain events (immediately after DMA, writes to $4200, etc) void sCPU::last_cycle() { - if(status.irq_lock) return; + if(!status.irq_lock) { + status.nmi_pending |= nmi_test(); + status.irq_pending |= irq_test(); - status.nmi_pending |= nmi_test(); - status.irq_pending |= irq_test(); - - event.irq = (status.nmi_pending || status.irq_pending); + status.interrupt_pending = (status.nmi_pending || status.irq_pending); + } } void sCPU::timing_power() { } void sCPU::timing_reset() { - delta.reset(); - - temp_.alu_mul_delay = config::temp.alu_mul_delay; - temp_.alu_div_delay = config::temp.alu_div_delay; + event.reset(); status.clock_count = 0; status.line_clocks = ppu.lineclocks(); status.irq_lock = false; status.alu_lock = false; - status.dram_refresh_position = (cpu_version == 1) ? 530 : 538; + status.dram_refresh_position = (cpu_version == 1 ? 530 : 538); + event.enqueue(status.dram_refresh_position, EventDramRefresh); status.nmi_valid = false; status.nmi_line = false; status.nmi_transition = false; status.nmi_pending = false; - status.nmi_hold = 0; + status.nmi_hold = false; status.irq_valid = false; status.irq_line = false; status.irq_transition = false; status.irq_pending = false; - status.irq_hold = 0; - - update_interrupts(); + status.irq_hold = false; status.dma_counter = 0; status.dma_clocks = 0; @@ -164,9 +160,7 @@ void sCPU::timing_reset() { status.hdma_mode = 0; status.dma_state = DmaInactive; - event.cycle_edge = 0; + cycle_edge_state = 0; } -#undef ntsc_color_burst_phase_shift_scanline - -#endif //ifdef SCPU_CPP +#endif diff --git a/src/cpu/scpu/timing/timing.hpp b/src/cpu/scpu/timing/timing.hpp index b46d2ddb..092e7f07 100644 --- a/src/cpu/scpu/timing/timing.hpp +++ b/src/cpu/scpu/timing/timing.hpp @@ -10,6 +10,7 @@ EventFlagHdmaInit = 1 << 0, EventFlagHdmaRun = 1 << 1, }; + unsigned cycle_edge_state; //timing.cpp unsigned dma_counter(); @@ -25,18 +26,16 @@ void timing_reset(); //irq.cpp - void update_interrupts(); - void poll_interrupts(); + alwaysinline void poll_interrupts(); void nmitimen_update(uint8 data); - void hvtime_update(uint16 addr); bool rdnmi(); bool timeup(); - bool nmi_test(); - bool irq_test(); + alwaysinline bool nmi_test(); + alwaysinline bool irq_test(); //joypad.cpp void run_auto_joypad_poll(); //event.cpp - void queue_event(unsigned); //deltaqueue callback function + void queue_event(unsigned); //priorityqueue callback function diff --git a/src/data/bsnes.ico b/src/data/bsnes.ico index 646d8c5fc336018029ef26b4dc17f77007302b37..54acded45d3d1e9b458bfab3c24feeb328e231d9 100644 GIT binary patch literal 22071 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 z zZ~6IK-@pF*DEgQAuLS<%zo`9CF~d}El)r)o^$!Ap%KERAg+}uP{u95z|0IT1DvPKm z4GY{m0Uwo=Xn7!w0DrH4Do5Cna%3IA9uNq)0JsD=12_yY1S|&pIX~*SA=CrqCw~k5 zkS-9R(wI9Kw+sLCpkpwNwj8+W0(OJ$2+|s0 z1iBo^Isb|LXAL;tn#OrLe^hSuf+3aGUA#nQdtd;z3bYJUIyH7~*`s5I?Avu}jHdiMC4Z(aAWgy-&c8?jt}?)RlKV{lXjwi+310}< zoj4nK_x}v%>E(s=TYeGk$!vfh7H}8k`5lZsIlfs+U%llt%H{unYDhadSNc_D0DoHp zCQ$CE@4x1M^xNk;;89i@Z<_AUAJE6a_d36dH<}M{HU(}&-{L&udn200Q1jhd;M|9{ zqaSEXXj~sO{jYd{Hz(jUVAAg(zt?uYgtbOUu%q^YzdWE3_5I(#5X5o*S>C`64}P2j z5Co{fJ32j4E?mTCGZ=yw1sf0jF%4tRwC z1_5J$NzNd@`1A+$*)zESXEuN~;1D1fa0Flm_~ALp%)CGIM}ME+>>H=T&~p5R{%PF* znvs9-8}M2Lye9sF+uvzRG5;XV?`92U$o#=}GP8Y+Lwfn|8uvJ^&sq%l7ui$N+Lq{4T<$Z?T`>S9YKZ>3W8%q5E9%{;)7EVB5cQu zPwu|6(}pMOg^||tSyDbr;-qA9M+Fg?HxHE*L~e2oKaBYloAx;sdW?cTlM?a;yIAzJ z1|CI#0tbqEj*hl>#3$|OoV}Hu#pJf?x;;-zY z;gCNQApI-0r~D8Pwy*u5ed@f27%kFujybRf#eR(k*h9}P*8wWNV;kkr0JhlxpU(LH zyB=ox|0BfRgW4Rr%$)@GI{{%+Yhl>;NB1}j0OyQ zjkG<(1?RYL{(=MAHr#*2HrNybTm*b~f4(+E39q_tXg=hRw!@kAjlTB-?Ea2zu%!yP z1$YiX_h+Zt+czcn;LBJCcGd$z0cn6MfCGSef5$c&2kfEyPv5llm9WY4tG}E7U&Vv` z=>YX#vGXlX!GD$KkA{Qo&4BOL`DcDjAiwkvKWgWD@gZMVz*pP+XWOkHrM{QP|1}Kq zR{|6Ornke7@O~aJ|Nol*-%Ss;H~+%eVy^ZHUzrD=z;wQ z3ZnEGj3f@2r{XZalbbMfRLEi&L2yC{X%_U8piZI(!N4{QgfcKvVLJl_4FypUwn>yA z&l#rR6`hQu+Y&;6seKCNFEdy8_`m){M9?EGEqdTFKR_@tJZy(Ne*EPO{k!x}okHjE zPXSYFxT!VFFfu-_``xr?VA{Dkkaqf7bSgcKsgdR^t=OQqY+3TB^PPD3hnD3?D4+~b z2Pj4=*u9Xp^O6X4A{6qu4|oE&j!aOEMf_+&Fjk`Tgof3t-z|dpZva#C@agl?YcqKY zue*)Tf6?)FYW%+oY0m+s(oM-R{!*-6XFQipeK6;;t?MH=*RCC~hIB>K(oW79CT-k? zO}!_Rcb~nGM~D@YiU^&Foy7qCU^iem%hT2p(oLCi=i zY7_0c_!+iQAMBfokIM7r_}M*Yxhx>v4Zxqq#wa^qJj5pg(7Asx+>O{VJuRwV-owTY z>CpQQGsp3onQ_1M4r%w#ust#!!>#qL_+R_pcb*F!)6lyY0bP+?e|t^$wY>i6Z}1C& orimcq=pHZZvkre+J;NsJC(A?-;{XbLNB3QO0K-0?(xB=7AElyM(EtDd literal 26694 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 aez*Hu&a@eE7g-F}1I1~Q;?3#5AM#(>6E*w* diff --git a/src/data/controller.h b/src/data/controller.h index 1cbe2108..90b12068 100644 --- a/src/data/controller.h +++ b/src/data/controller.h @@ -1,3 +1,7 @@ +#if defined(_MSC_VER) +//TODO: Visual C++ cannot handle strings > 65536 bytes. Need to find workaround. +static char enc_controller[65536 * 2] = { 0 }; +#else static char enc_controller[] = { "_v8B8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw" "AfB_AfAB8AHwAfAB8AHwAeD9AP396OjomJiZAHBwcG5ubmpqAGpYWFhLS0tBAEFB" @@ -1032,3 +1036,4 @@ static char enc_controller[] = { "AfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P9G" "8kbyRvJG8kbyRvJG8kby_0byqPAB8AHwAfAB8AHwAfAPAfAB8AHwATA" }; +#endif diff --git a/src/interface.hpp b/src/interface.hpp index d6193203..09b75fff 100644 --- a/src/interface.hpp +++ b/src/interface.hpp @@ -1,24 +1,20 @@ -#include "reader/reader.hpp" #include "cheat/cheat.hpp" -#include "config/config.hpp" #include "memory/memory.hpp" #include "memory/smemory/smemory.hpp" -#include "cart/cart.hpp" - #include "cpu/cpu.hpp" #include "cpu/scpu/scpu.hpp" +#include "ppu/ppu.hpp" +#include "ppu/bppu/bppu.hpp" + #include "smp/smp.hpp" #include "smp/ssmp/ssmp.hpp" #include "dsp/dsp.hpp" #include "dsp/sdsp/sdsp.hpp" -#include "ppu/ppu.hpp" -#include "ppu/bppu/bppu.hpp" - extern BUSCORE bus; extern CPUCORE cpu; extern SMPCORE smp; @@ -26,4 +22,3 @@ extern DSPCORE dsp; extern PPUCORE ppu; #include "snes/snes.hpp" -#include "chip/chip.hpp" diff --git a/src/lib/hiro/win/editbox.cpp b/src/lib/hiro/win/editbox.cpp index da7373d3..6949e7bc 100644 --- a/src/lib/hiro/win/editbox.cpp +++ b/src/lib/hiro/win/editbox.cpp @@ -8,6 +8,8 @@ void pEditbox::create(unsigned style, unsigned width, unsigned height, const cha (style & Editbox::HorizontalScrollNever ) ? 0 : ES_AUTOHSCROLL; + autovscroll = (vscroll == ES_AUTOVSCROLL); + hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD | WS_VISIBLE | vscroll | hscroll | (multiline == true ? ES_MULTILINE | ES_WANTRETURN : WS_TABSTOP) | @@ -16,6 +18,12 @@ void pEditbox::create(unsigned style, unsigned width, unsigned height, const cha phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0); SendMessage(hwnd, WM_SETFONT, (WPARAM)phiro().default_font, 0); set_text(text); + update(); +} + +void pEditbox::resize(unsigned width, unsigned height) { + pFormControl::resize(width, height); + update(); } void pEditbox::set_text(const char *text) { @@ -23,6 +31,7 @@ void pEditbox::set_text(const char *text) { replace(temp, "\r", ""); replace(temp, "\n", "\r\n"); SetWindowText(hwnd, utf16(temp)); + update(); } unsigned pEditbox::get_text(char *text, unsigned length) { @@ -36,4 +45,40 @@ unsigned pEditbox::get_text(char *text, unsigned length) { } pEditbox::pEditbox(Editbox &self_) : pFormControl(self_), self(self_) { + autovscroll = false; +} + +//======== +//internal +//======== + +//WS_VSCROLL always shows the scrollbar; +//ES_AUTOVSCROLL never shows the scrollbar but allows unlimited text; +//no style disallows more text than what fits in the window. +// +//below routine simulates the effect of allowing unlimited text, but only +//showing the scrollbar when it is needed. not sure how to simulate this effect +//for horizontal scrolling at this time. +// +//below routine should be called whenever the font, text or window size is +//modified. +void pEditbox::update() { + if(autovscroll == true) { + //determine how many lines of text this control allows + RECT rect; + SendMessage(hwnd, EM_GETRECT, 0, (LPARAM)&rect); + unsigned height = rect.bottom - rect.top; + + //determine the height of a single line of text + HDC hdc = GetDC(hwnd); + SelectObject(hdc, phiro().default_font); + DrawText(hdc, L"byuu", -1, &rect, DT_CALCRECT); + ReleaseDC(hwnd, hdc); + unsigned lineheight = rect.bottom - rect.top; + + //only show the scrollbar when there are more lines of text + //than the control can show at once. + unsigned linecount = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0); + ShowScrollBar(hwnd, SB_VERT, linecount > (height / lineheight)); + } } diff --git a/src/lib/hiro/win/editbox.hpp b/src/lib/hiro/win/editbox.hpp index 075d47f8..512091fb 100644 --- a/src/lib/hiro/win/editbox.hpp +++ b/src/lib/hiro/win/editbox.hpp @@ -2,8 +2,13 @@ class pEditbox : public pFormControl { public: Editbox &self; void create(unsigned style, unsigned width, unsigned height, const char *text = ""); + void resize(unsigned width, unsigned height); unsigned get_text(char *text, unsigned length = -1U); void set_text(const char *text = ""); pEditbox(Editbox&); + +//private: + bool autovscroll; + void update(); }; diff --git a/src/lib/hiro/win/hiro.cpp b/src/lib/hiro/win/hiro.cpp index 2a361409..6a28643e 100644 --- a/src/lib/hiro/win/hiro.cpp +++ b/src/lib/hiro/win/hiro.cpp @@ -232,7 +232,7 @@ pHiro& phiro() { HFONT pHiro::create_font(const char *name, unsigned size) { return CreateFont( - -(size * 96.0 / 72.0 + 0.5), //96 = DPI + -(size * 96.0 / 72.0 + 0.5), //96 = DPI 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, utf16(name) ); @@ -375,6 +375,7 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { case Widget::EditboxType: { Editbox &editbox = (Editbox&)*widget; if(HIWORD(wparam) == EN_CHANGE) { + editbox.p.update(); //called to dynamically display vertical scrollbar if needed if(editbox.on_change) editbox.on_change(event_t(event_t::Change, 0, &editbox)); } } break; diff --git a/src/lib/hiro/win/hiro.hpp b/src/lib/hiro/win/hiro.hpp index 16ab6d6c..7461d695 100644 --- a/src/lib/hiro/win/hiro.hpp +++ b/src/lib/hiro/win/hiro.hpp @@ -13,12 +13,14 @@ #define NOMINMAX #define _NO_OLDNAMES +#define mkdir _mkdir #define UNICODE #include #include #include #include #include +#undef mkdir #include using nall::min; diff --git a/src/lib/libfilter/ntsc.hpp b/src/lib/libfilter/ntsc.hpp index 953f4885..496e791e 100644 --- a/src/lib/libfilter/ntsc.hpp +++ b/src/lib/libfilter/ntsc.hpp @@ -3,6 +3,7 @@ class NTSCFilter : public Filter { public: void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); + void adjust(float hue, float saturation, float contrast, float brightness, float sharpness, bool merge_fields); NTSCFilter(); ~NTSCFilter(); @@ -10,8 +11,6 @@ public: private: struct snes_ntsc_t *ntsc; int burst, burst_toggle; - - void adjust(float hue, float saturation, float contrast, float brightness, float sharpness, bool merge_fields); }; extern NTSCFilter filter_ntsc; diff --git a/src/lib/nall/algorithm.hpp b/src/lib/nall/algorithm.hpp index bce2f70a..98b39528 100644 --- a/src/lib/nall/algorithm.hpp +++ b/src/lib/nall/algorithm.hpp @@ -1,27 +1,23 @@ #ifndef NALL_ALGORITHM_HPP #define NALL_ALGORITHM_HPP -namespace nall { - #undef min #undef max -template -T min(const T& t, const U& u) { - return t < u ? t : u; +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; + } + + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } } -template -T max(const T& t, const U& u) { - return t > u ? t : u; -} - -//pseudo-random number generator -inline unsigned prng() { - static unsigned n = 0; - return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); -} - -} //namespace nall - -#endif //ifndef NALL_ALGORITHM_HPP +#endif diff --git a/src/lib/nall/any.hpp b/src/lib/nall/any.hpp index a4417e00..9689af13 100644 --- a/src/lib/nall/any.hpp +++ b/src/lib/nall/any.hpp @@ -6,71 +6,69 @@ #include namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } -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< + is_array::value, + typename remove_extent::type>::type*, + T + >::type auto_t; - template any& operator=(const T& value_) { - typedef typename static_if< - is_array::value, - typename 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_); + } - 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; } - return *this; - } + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } - any() : container(0) {} - template any(const T& value_) : container(0) { operator=(value_); } + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; -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 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 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 remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } -template T any_cast(any &value) { - typedef typename 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 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; + } } -template T any_cast(const any &value) { - typedef const typename 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; -} - -} //namespace nall - -#endif //ifndef NALL_ANY_HPP +#endif diff --git a/src/lib/nall/array.hpp b/src/lib/nall/array.hpp index 0b733d2f..d2f1947d 100644 --- a/src/lib/nall/array.hpp +++ b/src/lib/nall/array.hpp @@ -3,92 +3,87 @@ #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; -//dynamic vector array -//neither constructor nor destructor is ever invoked; -//this this should only be used for POD objects. + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } -template class array { -protected: - T *pool; - unsigned poolsize, buffersize; + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } - unsigned findsize(unsigned size) const { - if(size <= 0x100) return 0x100; - if(size <= 0x400) return 0x400; - if(size <= 0x1000) return 0x1000; - if(size <= 0x4000) return 0x4000; - if(size <= 0x10000) return 0x10000; - if(size <= 0x40000) return 0x40000; - if(size <= 0x100000) return 0x100000; + void reserve(unsigned newsize) { + if(newsize == poolsize) return; - return (size + 0x100000) & ~0xfffff; - } + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } -public: - unsigned size() const { return buffersize; } - unsigned capacity() const { return poolsize; } + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } - void reset() { - if(pool) free(pool); - pool = 0; - poolsize = 0; - buffersize = 0; - } + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } - void reserve(unsigned newsize) { - if(newsize == poolsize) return; + void add(const T data) { + operator[](buffersize) = data; + } - pool = (T*)realloc(pool, newsize * sizeof(T)); - poolsize = newsize; - buffersize = min(buffersize, newsize); - } + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } - void resize(unsigned newsize) { - if(newsize > poolsize) reserve(findsize(newsize)); - buffersize = newsize; - } + array() { + pool = 0; + poolsize = 0; + buffersize = 0; + } - T* get(unsigned minsize = 0) { - if(minsize > buffersize) resize(minsize); - if(minsize > buffersize) throw "array[] out of bounds"; - return pool; - } + ~array() { reset(); } - void add(const T data) { operator[](buffersize) = data; } - void clear() { memset(pool, 0, buffersize * sizeof(T)); } + array(const array &source) : pool(0) { + operator=(source); + } - array() { - pool = 0; - poolsize = 0; - buffersize = 0; - } + 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() { reset(); } + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } - array& operator=(const array &source) { - if(pool) free(pool); - buffersize = source.buffersize; - poolsize = source.poolsize; - pool = (T*)realloc(pool, sizeof(T) * poolsize); //allocate entire pool size, - memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects - return *this; - } + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[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]; - } -}; - -} //namespace nall - -#endif //ifndef NALL_ARRAY_HPP +#endif diff --git a/src/lib/nall/base64.hpp b/src/lib/nall/base64.hpp index a4f3d07e..8b107b20 100644 --- a/src/lib/nall/base64.hpp +++ b/src/lib/nall/base64.hpp @@ -1,94 +1,92 @@ #ifndef NALL_BASE64_HPP #define NALL_BASE64_HPP +#include + #include #include -#include - namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new(zeromemory) char[inlength * 8 / 6 + 6]; -class base64 { -public: - static bool encode(char *&output, const uint8_t* input, unsigned inlength) { - output = new(zeromemory) 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; - 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 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; + } - case 2: { - uint8_t prev = dec(output[o]); - output[o++] = enc(prev + (input[i] >> 6)); - output[o++] = enc(input[i] & 63); - } break; + i++; } - i++; + return true; } - return true; - } + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new(zeromemory) uint8_t[inlength]; - static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { - unsigned inlength = strlen(input), infix = 0; - output = new(zeromemory) uint8_t[inlength]; + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); - unsigned i = 0, o = 0; - while(i < inlength) { - uint8_t x = dec(input[i]); + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; - switch(i++ & 3) { - case 0: { - output[o] = x << 2; - } break; + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } 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 2: { - output[o++] |= x >> 2; - output[o] = (x & 3) << 6; - } break; - - case 3: { - output[o++] |= x; - } break; + case 3: { + output[o++] |= x; + } break; + } } + + outlength = o; + return true; } - outlength = o; - return true; - } + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } -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; + } + }; +} - 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; - } -}; - -} //namespace nall - -#endif //ifndef NALL_BASE64_HPP +#endif diff --git a/src/lib/nall/bit.hpp b/src/lib/nall/bit.hpp index d6c0cbbc..169fc144 100644 --- a/src/lib/nall/bit.hpp +++ b/src/lib/nall/bit.hpp @@ -2,27 +2,50 @@ #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 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; + } + } } -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 nall - -#endif //ifndef NALL_BIT_HPP +#endif diff --git a/src/lib/nall/config.hpp b/src/lib/nall/config.hpp index 686336e9..0dde4a15 100644 --- a/src/lib/nall/config.hpp +++ b/src/lib/nall/config.hpp @@ -1,178 +1,114 @@ -#ifndef NALL_CONFIG_HPP -#define NALL_CONFIG_HPP - -#include -#include -#include -#include - -namespace nall { - -class setting; - -class configuration { -public: - array list; - - bool load(const char *fn); - bool save(const char *fn) const; - void add(setting *setting_) { list.add(setting_); } -}; - -class setting { -public: - enum setting_type { - integral_type, - string_type, - } type; - - const char *name; - const char *description; - - virtual void set(const char *input) = 0; - virtual void get(string &output) const = 0; - virtual void get_default(string &output) const = 0; -}; - -class integral_setting : public setting { -public: - enum integral_type { - boolean, - decimal, - hex, - } type; - - intmax_t value; - intmax_t default_value; - - void set(const char *input) { - if(type == boolean) { value = !strcmp(input, "true"); } - if(type == decimal) { value = strdec(input); } - if(type == hex) { value = strhex(input); } - } - - void get(string &output) const { - if(type == boolean) { output = value ? "true" : "false"; } - if(type == decimal) { output = strdec(value); } - if(type == hex) { output = string() << "0x" << strhex(value); } - } - - void get_default(string &output) const { - if(type == boolean) { output = default_value ? "true" : "false"; } - if(type == decimal) { output = strdec(default_value); } - if(type == hex) { output = string() << "0x" << strhex(default_value); } - } - - operator intmax_t() const { return value; } - integral_setting& operator=(intmax_t value_) { value = value_; return *this; } - - integral_setting(const char *name_, const char *description_, integral_type type_, intmax_t value_) { - initialize(name_, description_, type_, value_); - } - - integral_setting(configuration &parent, const char *name_, const char *description_, integral_type type_, intmax_t value_) { - initialize(name_, description_, type_, value_); - parent.add(this); - } - -private: - void initialize(const char *name_, const char *description_, integral_type type_, intmax_t value_) { - setting::type = setting::integral_type; - name = name_; - description = description_; - type = type_; - value = default_value = value_; - } -}; - -class string_setting : public setting { -public: - string value; - string default_value; - - void set(const char *input) { value = input; trim(value(), "\""); } - void get(string &output) const { output = string() << "\"" << value << "\""; } - void get_default(string &output) const { output = string() << "\"" << default_value << "\""; } - - operator const char*() const { return value; } - string_setting& operator=(const char *value_) { value = value_; return *this; } - bool operator==(const char *value_) const { return value == value_; } - bool operator!=(const char *value_) const { return value != value_; } - - string_setting(const char *name_, const char *description_, const char *value_) { - initialize(name_, description_, value_); - } - - string_setting(configuration &parent, const char *name_, const char *description_, const char *value_) { - initialize(name_, description_, value_); - parent.add(this); - } - -private: - void initialize(const char *name_, const char *description_, const char *value_) { - setting::type = setting::string_type; - name = name_; - description = description_; - value = default_value = value_; - } -}; - -inline bool configuration::load(const char *fn) { - //load the config file into memory - string data; - if(!fread(data, fn)) return false; - - //split the file into lines - replace(data, "\r\n", "\n"); - qreplace(data, "\t", ""); - qreplace(data, " ", ""); - - lstring line, part, subpart; - split(line, "\n", data); - - for(unsigned i = 0; i < count(line); i++) { - if(strlen(line[i]) == 0) continue; - if(strbegin(line[i], "#")) continue; - - qsplit(part, "=", line[i]); - for(unsigned l = 0; l < list.size(); l++) { - if(part[0] == list[l]->name) { - list[l]->set(part[1]); - break; - } - } - } - - return true; -} - -inline bool configuration::save(const char *fn) const { - file fp; - if(!fp.open(fn, file::mode_write)) return false; - - for(unsigned i = 0; i < list.size(); i++) { - string data; - lstring line, part, subpart; - strcpy(data, list[i]->description); - replace(data, "\r\n", "\n"); - split(line, "\n", data); - - string temp; - for(unsigned l = 0; l < count(line); l++) { - if(line[l] != "") fp.print(string() << "# " << line[l] << "\r\n"); - } - - string default_, value_; - list[i]->get_default(default_); - fp.print(string() << "# (default = " << default_ << ")\r\n"); - list[i]->get(value_); - fp.print(string() << list[i]->name << " = " << value_ << "\r\n\r\n"); - } - - fp.close(); - return true; -} - -} //namespace nall - -#endif //ifndef NALL_CONFIG_HPP +#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_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() { + switch(type) { + case boolean_t: return (*(bool*)data == false ? "false" : "true"); + case signed_t: return string() << (int)*(signed*)data; + case unsigned_t: return string() << (int)*(unsigned*)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 = s; break; + case unsigned_t: *(unsigned*)data = s; break; + case string_t: trim(s, "\""); *(string*)data = s; break; + } + } + }; + 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_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + bool load(const char *filename) { + string data; + if(fread(data, filename) == true) { + replace(data, "\r", ""); + lstring line; + split(line, "\n", data); + + for(unsigned i = 0; i < count(line); i++) { + int position = qstrpos(line[i], "#"); + if(position >= 0) line[i][position] = 0; + if(qstrpos(line[i], " = ") < 0) continue; + + lstring part; + qsplit(part, " = ", line[i]); + trim(part[0]); + trim(part[1]); + + 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; + } + } + + bool save(const char *filename) { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < list.size(); i++) { + fp.print(string() << list[i].name << " = " << list[i].get() << " # " << list[i].desc << "\r\n"); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/src/lib/nall/config.txt b/src/lib/nall/config.txt new file mode 100644 index 00000000..a59bc1c9 --- /dev/null +++ b/src/lib/nall/config.txt @@ -0,0 +1,176 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include +#include + +namespace nall { + class setting; + + class configuration { + public: + array list; + + bool load(const char *fn); + bool save(const char *fn) const; + void add(setting *setting_) { list.add(setting_); } + }; + + class setting { + public: + enum setting_type { + integral_type, + string_type, + } type; + + const char *name; + const char *description; + + virtual void set(const char *input) = 0; + virtual void get(string &output) const = 0; + virtual void get_default(string &output) const = 0; + }; + + class integral_setting : public setting { + public: + enum integral_type { + boolean, + decimal, + hex, + } type; + + intmax_t value; + intmax_t default_value; + + void set(const char *input) { + if(type == boolean) { value = !strcmp(input, "true"); } + if(type == decimal) { value = strdec(input); } + if(type == hex) { value = strhex(input); } + } + + void get(string &output) const { + if(type == boolean) { output = value ? "true" : "false"; } + if(type == decimal) { output = strdec(value); } + if(type == hex) { output = string() << "0x" << strhex(value); } + } + + void get_default(string &output) const { + if(type == boolean) { output = default_value ? "true" : "false"; } + if(type == decimal) { output = strdec(default_value); } + if(type == hex) { output = string() << "0x" << strhex(default_value); } + } + + operator intmax_t() const { return value; } + integral_setting& operator=(intmax_t value_) { value = value_; return *this; } + + integral_setting(const char *name_, const char *description_, integral_type type_, intmax_t value_) { + initialize(name_, description_, type_, value_); + } + + integral_setting(configuration &parent, const char *name_, const char *description_, integral_type type_, intmax_t value_) { + initialize(name_, description_, type_, value_); + parent.add(this); + } + + private: + void initialize(const char *name_, const char *description_, integral_type type_, intmax_t value_) { + setting::type = setting::integral_type; + name = name_; + description = description_; + type = type_; + value = default_value = value_; + } + }; + + class string_setting : public setting { + public: + string value; + string default_value; + + void set(const char *input) { value = input; trim(value(), "\""); } + void get(string &output) const { output = string() << "\"" << value << "\""; } + void get_default(string &output) const { output = string() << "\"" << default_value << "\""; } + + operator const char*() const { return value; } + string_setting& operator=(const char *value_) { value = value_; return *this; } + bool operator==(const char *value_) const { return value == value_; } + bool operator!=(const char *value_) const { return value != value_; } + + string_setting(const char *name_, const char *description_, const char *value_) { + initialize(name_, description_, value_); + } + + string_setting(configuration &parent, const char *name_, const char *description_, const char *value_) { + initialize(name_, description_, value_); + parent.add(this); + } + + private: + void initialize(const char *name_, const char *description_, const char *value_) { + setting::type = setting::string_type; + name = name_; + description = description_; + value = default_value = value_; + } + }; + + inline bool configuration::load(const char *fn) { + //load the config file into memory + string data; + if(!fread(data, fn)) return false; + + //split the file into lines + replace(data, "\r\n", "\n"); + qreplace(data, "\t", ""); + qreplace(data, " ", ""); + + lstring line, part, subpart; + split(line, "\n", data); + + for(unsigned i = 0; i < count(line); i++) { + if(strlen(line[i]) == 0) continue; + if(strbegin(line[i], "#")) continue; + + qsplit(part, "=", line[i]); + for(unsigned l = 0; l < list.size(); l++) { + if(part[0] == list[l]->name) { + list[l]->set(part[1]); + break; + } + } + } + + return true; + } + + inline bool configuration::save(const char *fn) const { + file fp; + if(!fp.open(fn, file::mode_write)) return false; + + for(unsigned i = 0; i < list.size(); i++) { + string data; + lstring line, part, subpart; + strcpy(data, list[i]->description); + replace(data, "\r\n", "\n"); + split(line, "\n", data); + + string temp; + for(unsigned l = 0; l < count(line); l++) { + if(line[l] != "") fp.print(string() << "# " << line[l] << "\r\n"); + } + + string default_, value_; + list[i]->get_default(default_); + fp.print(string() << "# (default = " << default_ << ")\r\n"); + list[i]->get(value_); + fp.print(string() << list[i]->name << " = " << value_ << "\r\n\r\n"); + } + + fp.close(); + return true; + } +} + +#endif diff --git a/src/lib/nall/crc32.hpp b/src/lib/nall/crc32.hpp index 3e848a6e..ad36fbf6 100644 --- a/src/lib/nall/crc32.hpp +++ b/src/lib/nall/crc32.hpp @@ -4,65 +4,63 @@ #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 + }; -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]); + 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; } - return ~crc32; } -} //namespace nall - -#endif //ifndef NALL_CRC32_HPP +#endif diff --git a/src/lib/nall/detect.hpp b/src/lib/nall/detect.hpp index c196237f..98c017e5 100644 --- a/src/lib/nall/detect.hpp +++ b/src/lib/nall/detect.hpp @@ -27,4 +27,4 @@ #define ARCH_MSB #endif -#endif //ifndef NALL_DETECT_HPP +#endif diff --git a/src/lib/nall/dictionary.hpp b/src/lib/nall/dictionary.hpp index b5c7ad75..02edddc4 100644 --- a/src/lib/nall/dictionary.hpp +++ b/src/lib/nall/dictionary.hpp @@ -6,70 +6,68 @@ #include namespace nall { - -class dictionary : noncopyable { -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, "{{")) { - int pos = strpos(input, "}}"); - if(pos >= 0) { - string temp = substr(input, pos + 2); - return temp; + class dictionary : noncopyable { + 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, "{{")) { + int pos = strpos(input, "}}"); + if(pos >= 0) { + string temp = substr(input, pos + 2); + return temp; + } + } + + return input; } - return input; - } + bool import(const char *filename) { + string data; + if(fread(data, filename) == false) return false; + ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + replace(data, "\r", ""); - bool import(const char *filename) { - string data; - if(fread(data, filename) == false) return false; - ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists - replace(data, "\r", ""); + lstring line; + split(line, "\n", data); + for(unsigned i = 0; i < count(line); i++) { + lstring part; + //format: "Input" = "Output" + qsplit(part, "=", line[i]); + if(count(part) != 2) continue; - lstring line; - split(line, "\n", data); - for(unsigned i = 0; i < count(line); i++) { - lstring part; - //format: "Input" = "Output" - qsplit(part, "=", line[i]); - if(count(part) != 2) continue; + //remove whitespace + trim(part[0]); + trim(part[1]); - //remove whitespace - trim(part[0]); - trim(part[1]); + //remove quotes + trim_once(part[0], "\""); + trim_once(part[1], "\""); - //remove quotes - trim_once(part[0], "\""); - trim_once(part[1], "\""); + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } - unsigned n = index_input.size(); - index_input[n] = part[0]; - index_output[n] = part[1]; + return true; } - return true; - } + void reset() { + index_input.reset(); + index_output.reset(); + } - void reset() { - index_input.reset(); - index_output.reset(); - } + ~dictionary() { + reset(); + } - ~dictionary() { - reset(); - } + protected: + lstring index_input; + lstring index_output; + }; +} -protected: - lstring index_input; - lstring index_output; -}; - -} //namespace nall - -#endif //ifndef NALL_DICTIONARY_HPP +#endif diff --git a/src/lib/nall/endian.hpp b/src/lib/nall/endian.hpp index ccbd209c..40d15633 100644 --- a/src/lib/nall/endian.hpp +++ b/src/lib/nall/endian.hpp @@ -35,4 +35,4 @@ #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h #endif -#endif //ifndef NALL_ENDIAN_HPP +#endif diff --git a/src/lib/nall/file.hpp b/src/lib/nall/file.hpp index a5fc013f..596b74a5 100644 --- a/src/lib/nall/file.hpp +++ b/src/lib/nall/file.hpp @@ -9,212 +9,210 @@ #include namespace nall { + class file : noncopyable { + public: + enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; + enum SeekMode { seek_absolute, seek_relative }; -class file : noncopyable { -public: - enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; - enum SeekMode { seek_absolute, seek_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++); - } - - void print(const char *string) { - if(!string) return; - while(*string) write(*string++); - } - - void flush() { - buffer_flush(); - fflush(fp); - } - - void seek(int offset, SeekMode mode = seek_absolute) { - if(!fp) return; //file not open - buffer_flush(); - - uintmax_t req_offset = file_offset; - switch(mode) { - case seek_absolute: req_offset = offset; break; - case seek_relative: req_offset += offset; break; + 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]; } - 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); + 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; } } - file_offset = req_offset; - } + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } - int offset() { - if(!fp) return -1; //file not open - return file_offset; - } + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } - int size() { - if(!fp) return -1; //file not open - return file_size; - } + void print(const char *string) { + if(!string) return; + while(*string) write(*string++); + } - bool end() { - if(!fp) return true; //file not open - return file_offset >= file_size; - } + void flush() { + buffer_flush(); + fflush(fp); + } - static bool exists(const char *fn) { - #if !defined(_WIN32) - FILE *fp = fopen(fn, "rb"); - #else - FILE *fp = _wfopen(utf16(fn), L"rb"); - #endif - if(fp) { - fclose(fp); + void seek(int offset, SeekMode mode = seek_absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(mode) { + case seek_absolute: req_offset = offset; break; + case seek_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 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(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + bool open() { + return fp; + } + + bool open(const char *fn, FileMode 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(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16(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; } - return false; - } - bool open() { - return fp; - } - - bool open(const char *fn, FileMode 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(fn), L"rb"); break; - case mode_write: fp = _wfopen(utf16(fn), L"wb+"); break; - case mode_readwrite: fp = _wfopen(utf16(fn), L"rb+"); break; - case mode_writeread: fp = _wfopen(utf16(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(); - } - -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; - FileMode file_mode; - - void buffer_sync() { - if(!fp) return; //file not open - if(buffer_offset != (file_offset & ~buffer_mask)) { + void close() { + if(!fp) return; buffer_flush(); - buffer_offset = file_offset & ~buffer_mask; + 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(); + } + + 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; + FileMode 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 = fread(buffer, 1, length, fp); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; } - } + }; +} - 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; - } -}; - -} //namespace nall - -#endif //ifndef NALL_FILE_HPP +#endif diff --git a/src/lib/nall/filemap.hpp b/src/lib/nall/filemap.hpp index bc73c49f..c73efad4 100644 --- a/src/lib/nall/filemap.hpp +++ b/src/lib/nall/filemap.hpp @@ -17,176 +17,174 @@ #endif namespace nall { + class filemap { + public: + enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread }; -class filemap { -public: - enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread }; + bool open(const char *filename, filemode mode) { return p_open(filename, mode); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* handle() { return p_handle; } + const uint8_t* handle() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + ~filemap() { p_dtor(); } - bool open(const char *filename, filemode mode) { return p_open(filename, mode); } - void close() { return p_close(); } - unsigned size() const { return p_size; } - uint8_t* handle() { return p_handle; } - const uint8_t* handle() const { return p_handle; } - filemap() : p_size(0), p_handle(0) { p_ctor(); } - ~filemap() { p_dtor(); } + private: + unsigned p_size; + uint8_t *p_handle; -private: - unsigned p_size; - uint8_t *p_handle; + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= - #if defined(_WIN32) - // - // MapViewOfFile - // + HANDLE p_filehandle, p_maphandle; - HANDLE p_filehandle, p_maphandle; + bool p_open(const char *filename, filemode mode) { + int desired_access, creation_disposition, flprotect, map_access; - bool p_open(const char *filename, filemode 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; + } - 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(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; } - p_filehandle = CreateFileW(utf16(filename), desired_access, FILE_SHARE_READ, NULL, - creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); - if(p_filehandle == INVALID_HANDLE_VALUE) return false; + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } - p_size = GetFileSize(p_filehandle, NULL); + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } - p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); - if(p_maphandle == INVALID_HANDLE_VALUE) { - CloseHandle(p_filehandle); + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { p_filehandle = INVALID_HANDLE_VALUE; - return false; + p_maphandle = INVALID_HANDLE_VALUE; } - 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; + void p_dtor() { + close(); } - if(p_maphandle != INVALID_HANDLE_VALUE) { - CloseHandle(p_maphandle); - p_maphandle = INVALID_HANDLE_VALUE; + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open(const char *filename, filemode 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); + 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; } - if(p_filehandle != INVALID_HANDLE_VALUE) { - CloseHandle(p_filehandle); - p_filehandle = INVALID_HANDLE_VALUE; - } - } + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } - void p_ctor() { - p_filehandle = INVALID_HANDLE_VALUE; - p_maphandle = INVALID_HANDLE_VALUE; - } - - void p_dtor() { - close(); - } - - #else - // - // mmap - // - - int p_fd; - - bool p_open(const char *filename, filemode 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; + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } } - p_fd = ::open(filename, open_flags); - 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); + void p_ctor() { p_fd = -1; } - } - void p_ctor() { - p_fd = -1; - } + void p_dtor() { + p_close(); + } - void p_dtor() { - p_close(); - } + #endif + }; +} - #endif -}; - -} //namespace nall - -#endif //ifndef NALL_FILEMAP_HPP +#endif diff --git a/src/lib/nall/function.hpp b/src/lib/nall/function.hpp index 2e10b6ea..9e90cb1d 100644 --- a/src/lib/nall/function.hpp +++ b/src/lib/nall/function.hpp @@ -8,7 +8,7 @@ #define TN typename namespace nall { -template class function; + template class function; } //parameters = 0 @@ -102,85 +102,83 @@ template class function; //function implementation template class namespace nall { + template + class function { + private: + struct base1 { virtual void func1(PL) {} }; + struct base2 { virtual void func2(PL) {} }; + struct derived : base1, virtual base2 {}; -template -class function { -private: - struct base1 { virtual void func1(PL) {} }; - struct base2 { virtual void func2(PL) {} }; - struct derived : base1, virtual base2 {}; - - struct data_t { - R (*fn_call)(const data_t& cat(PL)); - union { - R (*fn_global)(PL); - struct { - R (derived::*fn_member)(PL); - void *object; + struct data_t { + R (*fn_call)(const data_t& cat(PL)); + union { + R (*fn_global)(PL); + struct { + R (derived::*fn_member)(PL); + void *object; + }; }; - }; - } data; + } data; - static R fn_call_global(const data_t &d cat(PL)) { - return d.fn_global(CL); + static R fn_call_global(const data_t &d cat(PL)) { + return d.fn_global(CL); + } + + template + static R fn_call_member(const data_t &d cat(PL)) { + return (((C*)d.object)->*((R (C::*&)(PL))d.fn_member))(CL); + } + + public: + R operator()(PL) const { return data.fn_call(data cat(CL)); } + operator bool() const { return data.fn_call; } + + function() { data.fn_call = 0; } + + function(R (*fn)(PL)) { + data.fn_call = &fn_call_global; + data.fn_global = fn; + } + + template + function(R (C::*fn)(PL), C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + template + function(R (C::*fn)(PL) const, C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = (R (C::*&)(PL))fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + function &operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } + function(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); } + }; + + template + function bind(R (*fn)(PL)) { + return function(fn); } - template - static R fn_call_member(const data_t &d cat(PL)) { - return (((C*)d.object)->*((R (C::*&)(PL))d.fn_member))(CL); + template + function bind(R (C::*fn)(PL), C *obj) { + return function(fn, obj); } -public: - R operator()(PL) const { return data.fn_call(data cat(CL)); } - operator bool() const { return data.fn_call; } - - function() { data.fn_call = 0; } - - function(R (*fn)(PL)) { - data.fn_call = &fn_call_global; - data.fn_global = fn; - } - - template - function(R (C::*fn)(PL), C *obj) { - data.fn_call = &fn_call_member; - (R (C::*&)(PL))data.fn_member = fn; - assert(sizeof data.fn_member >= sizeof fn); - data.object = obj; - } - - template - function(R (C::*fn)(PL) const, C *obj) { - data.fn_call = &fn_call_member; - (R (C::*&)(PL))data.fn_member = (R (C::*&)(PL))fn; - assert(sizeof data.fn_member >= sizeof fn); - data.object = obj; - } - - function &operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } - function(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); } -}; - -template -function bind(R (*fn)(PL)) { - return function(fn); + template + function bind(R (C::*fn)(PL) const, C *obj) { + return function(fn, obj); + } } -template -function bind(R (C::*fn)(PL), C *obj) { - return function(fn, obj); -} - -template -function bind(R (C::*fn)(PL) const, C *obj) { - return function(fn, obj); -} - -} //namespace nall - #undef cat #undef TL #undef PL #undef CL -#endif //ifndef NALL_FUNCTION_HPP +#endif diff --git a/src/lib/nall/input.hpp b/src/lib/nall/input.hpp index 4c147ca5..e6dd4741 100644 --- a/src/lib/nall/input.hpp +++ b/src/lib/nall/input.hpp @@ -8,167 +8,165 @@ #include namespace nall { - -struct keyboard { - enum { - none, - escape, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, - print_screen, scroll_lock, pause, tilde, - num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_0, - dash, equal, backspace, - insert, delete_, home, end, page_up, page_down, - 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, - lbracket, rbracket, backslash, semicolon, apostrophe, comma, period, slash, - pad_1, pad_2, pad_3, pad_4, pad_5, pad_6, pad_7, pad_8, pad_9, pad_0, - point, enter, add, subtract, multiply, divide, - num_lock, caps_lock, - up, down, left, right, - tab, return_, spacebar, - lctrl, rctrl, lalt, ralt, lshift, rshift, lsuper, rsuper, menu, - limit, - }; -}; - -struct mouse { - enum { buttons = 8 }; - - enum { - none = keyboard::limit, - x, y, z, - button, - limit = button + buttons, - }; -}; - -template struct joypad { - enum { axes = 8 }; - enum { buttons = 96 }; - - enum { - none = joypad::limit, - up, down, left, right, - axis, - button = axis + axes, - limit = button + buttons, - }; -}; - -template<> struct joypad<-1> { - enum { count = 16 }; - enum { axes = 8 }; - enum { buttons = 96 }; - - enum { - none, - up, down, left, right, - axis, - button = axis + axes, - length = button + buttons - none, //number of syms per joypad - limit = mouse::limit, + struct keyboard { + enum { + none, + escape, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, + print_screen, scroll_lock, pause, tilde, + num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_0, + dash, equal, backspace, + insert, delete_, home, end, page_up, page_down, + 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, + lbracket, rbracket, backslash, semicolon, apostrophe, comma, period, slash, + pad_1, pad_2, pad_3, pad_4, pad_5, pad_6, pad_7, pad_8, pad_9, pad_0, + point, enter, add, subtract, multiply, divide, + num_lock, caps_lock, + up, down, left, right, + tab, return_, spacebar, + lctrl, rctrl, lalt, ralt, lshift, rshift, lsuper, rsuper, menu, + limit, + }; }; - static uint16_t index(unsigned joypad_number, unsigned joypad_enum) { - if(joypad_number >= count) return keyboard::none; - return limit + joypad_number * length + joypad_enum; - } -}; + struct mouse { + enum { buttons = 8 }; -enum { input_limit = joypad::count - 1>::limit }; + enum { + none = keyboard::limit, + x, y, z, + button, + limit = button + buttons, + }; + }; -static const char sym_table[][64] = { - //keyboard - "none", - "escape", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", - "print_screen", "scroll_lock", "pause", "tilde", - "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "num_7", "num_8", "num_9", "num_0", - "dash", "equal", "backspace", - "insert", "delete", "home", "end", "page_up", "page_down", - "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", - "lbracket", "rbracket", "backslash", "semicolon", "apostrophe", "comma", "period", "slash", - "pad_1", "pad_2", "pad_3", "pad_4", "pad_5", "pad_6", "pad_7", "pad_8", "pad_9", "pad_0", - "point", "enter", "add", "subtract", "multiply", "divide", - "num_lock", "caps_lock", - "up", "down", "left", "right", - "tab", "return", "spacebar", - "lctrl", "rctrl", "lalt", "ralt", "lshift", "rshift", "lsuper", "rsuper", "menu", - "keyboard.limit", + template struct joypad { + enum { axes = 8 }; + enum { buttons = 96 }; - //mouse - "mouse.x", "mouse.y", "mouse.z", - "mouse.button00", "mouse.button01", "mouse.button02", "mouse.button03", - "mouse.button04", "mouse.button05", "mouse.button06", "mouse.button07", - "mouse.limit", -}; + enum { + none = joypad::limit, + up, down, left, right, + axis, + button = axis + axes, + limit = button + buttons, + }; + }; -static const char* input_find(uint16_t key) { - if(key < mouse::limit) return sym_table[key]; + template<> struct joypad<-1> { + enum { count = 16 }; + enum { axes = 8 }; + enum { buttons = 96 }; - static char buffer[64]; - for(unsigned j = 0; j < 16; j++) { - if(key == joypad<>::index(j, joypad<>::up)) { sprintf(buffer, "joypad%.2d.up", j); return buffer; } - if(key == joypad<>::index(j, joypad<>::down)) { sprintf(buffer, "joypad%.2d.down", j); return buffer; } - if(key == joypad<>::index(j, joypad<>::left)) { sprintf(buffer, "joypad%.2d.left", j); return buffer; } - if(key == joypad<>::index(j, joypad<>::right)) { sprintf(buffer, "joypad%.2d.right", j); return buffer; } + enum { + none, + up, down, left, right, + axis, + button = axis + axes, + length = button + buttons - none, //number of syms per joypad + limit = mouse::limit, + }; - if(key >= joypad<>::index(j, joypad<>::axis + 0) - && key < joypad<>::index(j, joypad<>::axis + joypad<>::axes)) { - sprintf(buffer, "joypad%.2d.axis%.2d", j, key - joypad<>::index(j, joypad<>::axis)); - return buffer; + static uint16_t index(unsigned joypad_number, unsigned joypad_enum) { + if(joypad_number >= count) return keyboard::none; + return limit + joypad_number * length + joypad_enum; + } + }; + + enum { input_limit = joypad::count - 1>::limit }; + + static const char sym_table[][64] = { + //keyboard + "none", + "escape", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", + "print_screen", "scroll_lock", "pause", "tilde", + "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "num_7", "num_8", "num_9", "num_0", + "dash", "equal", "backspace", + "insert", "delete", "home", "end", "page_up", "page_down", + "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", + "lbracket", "rbracket", "backslash", "semicolon", "apostrophe", "comma", "period", "slash", + "pad_1", "pad_2", "pad_3", "pad_4", "pad_5", "pad_6", "pad_7", "pad_8", "pad_9", "pad_0", + "point", "enter", "add", "subtract", "multiply", "divide", + "num_lock", "caps_lock", + "up", "down", "left", "right", + "tab", "return", "spacebar", + "lctrl", "rctrl", "lalt", "ralt", "lshift", "rshift", "lsuper", "rsuper", "menu", + "keyboard.limit", + + //mouse + "mouse.x", "mouse.y", "mouse.z", + "mouse.button00", "mouse.button01", "mouse.button02", "mouse.button03", + "mouse.button04", "mouse.button05", "mouse.button06", "mouse.button07", + "mouse.limit", + }; + + static const char* input_find(uint16_t key) { + if(key < mouse::limit) return sym_table[key]; + + static char buffer[64]; + for(unsigned j = 0; j < 16; j++) { + if(key == joypad<>::index(j, joypad<>::up)) { sprintf(buffer, "joypad%.2d.up", j); return buffer; } + if(key == joypad<>::index(j, joypad<>::down)) { sprintf(buffer, "joypad%.2d.down", j); return buffer; } + if(key == joypad<>::index(j, joypad<>::left)) { sprintf(buffer, "joypad%.2d.left", j); return buffer; } + if(key == joypad<>::index(j, joypad<>::right)) { sprintf(buffer, "joypad%.2d.right", j); return buffer; } + + if(key >= joypad<>::index(j, joypad<>::axis + 0) + && key < joypad<>::index(j, joypad<>::axis + joypad<>::axes)) { + sprintf(buffer, "joypad%.2d.axis%.2d", j, key - joypad<>::index(j, joypad<>::axis)); + return buffer; + } + + if(key >= joypad<>::index(j, joypad<>::button + 0) + && key < joypad<>::index(j, joypad<>::button + joypad<>::buttons)) { + sprintf(buffer, "joypad%.2d.button%.2d", j, key - joypad<>::index(j, joypad<>::button)); + return buffer; + } } - if(key >= joypad<>::index(j, joypad<>::button + 0) - && key < joypad<>::index(j, joypad<>::button + joypad<>::buttons)) { - sprintf(buffer, "joypad%.2d.button%.2d", j, key - joypad<>::index(j, joypad<>::button)); - return buffer; + return "none"; + } + + static char* input_find(char *out, uint16_t key) { + strcpy(out, input_find(key)); + return out; + } + + static uint16_t input_find(const char *key) { + for(unsigned i = 0; i < mouse::limit; i++) { + if(!strcmp(sym_table[i], key)) return i; } - } - return "none"; -} - -static char* input_find(char *out, uint16_t key) { - strcpy(out, input_find(key)); - return out; -} - -static uint16_t input_find(const char *key) { - for(unsigned i = 0; i < mouse::limit; i++) { - if(!strcmp(sym_table[i], key)) return i; - } - - if(memcmp(key, "joypad", 6)) return keyboard::none; - key += 6; - if(!*key || !*(key + 1)) return keyboard::none; - uint8_t j = (*key - '0') * 10 + (*(key + 1) - '0'); - if(j > 15) return keyboard::none; - - key += 2; - if(!strcmp(key, ".up")) return joypad<>::index(j, joypad<>::up); - if(!strcmp(key, ".down")) return joypad<>::index(j, joypad<>::down); - if(!strcmp(key, ".left")) return joypad<>::index(j, joypad<>::left); - if(!strcmp(key, ".right")) return joypad<>::index(j, joypad<>::right); - - if(!memcmp(key, ".axis", 5)) { - key += 5; + if(memcmp(key, "joypad", 6)) return keyboard::none; + key += 6; if(!*key || !*(key + 1)) return keyboard::none; - uint8_t axis = (*key - '0') * 10 + (*(key + 1) - '0'); - if(axis >= joypad<>::axes) return keyboard::none; - return joypad<>::index(j, joypad<>::axis + axis); - } + uint8_t j = (*key - '0') * 10 + (*(key + 1) - '0'); + if(j > 15) return keyboard::none; - if(!memcmp(key, ".button", 7)) { - key += 7; - if(!*key || !*(key + 1)) return keyboard::none; - uint8_t button = (*key - '0') * 10 + (*(key + 1) - '0'); - if(button >= joypad<>::buttons) return keyboard::none; - return joypad<>::index(j, joypad<>::button + button); - } + key += 2; + if(!strcmp(key, ".up")) return joypad<>::index(j, joypad<>::up); + if(!strcmp(key, ".down")) return joypad<>::index(j, joypad<>::down); + if(!strcmp(key, ".left")) return joypad<>::index(j, joypad<>::left); + if(!strcmp(key, ".right")) return joypad<>::index(j, joypad<>::right); - return keyboard::none; + if(!memcmp(key, ".axis", 5)) { + key += 5; + if(!*key || !*(key + 1)) return keyboard::none; + uint8_t axis = (*key - '0') * 10 + (*(key + 1) - '0'); + if(axis >= joypad<>::axes) return keyboard::none; + return joypad<>::index(j, joypad<>::axis + axis); + } + + if(!memcmp(key, ".button", 7)) { + key += 7; + if(!*key || !*(key + 1)) return keyboard::none; + uint8_t button = (*key - '0') * 10 + (*(key + 1) - '0'); + if(button >= joypad<>::buttons) return keyboard::none; + return joypad<>::index(j, joypad<>::button + button); + } + + return keyboard::none; + } } -} //namespace nall - -#endif //ifndef NALL_INPUT_HPP +#endif diff --git a/src/lib/nall/lzss.hpp b/src/lib/nall/lzss.hpp index 0073460c..202bc814 100644 --- a/src/lib/nall/lzss.hpp +++ b/src/lib/nall/lzss.hpp @@ -6,78 +6,76 @@ #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]; -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; - 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++; + } - 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(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; } } - 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; + } } } - output[flagoffset] = flag; + return true; } + }; +} - 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; - } -}; - -} //namespace nall - -#endif //ifndef NALL_LZSS_HPP +#endif diff --git a/src/lib/nall/modulo.hpp b/src/lib/nall/modulo.hpp deleted file mode 100644 index 472056fd..00000000 --- a/src/lib/nall/modulo.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#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; - } - - modulo_array() { - buffer = new(zeromemory) T[size * 3]; - } - - ~modulo_array() { - delete[] buffer; - } - -private: - T *buffer; -}; - -} //namespace nall - -#endif //ifndef NALL_MODULO_HPP diff --git a/src/lib/nall/moduloarray.hpp b/src/lib/nall/moduloarray.hpp new file mode 100644 index 00000000..30e09777 --- /dev/null +++ b/src/lib/nall/moduloarray.hpp @@ -0,0 +1,36 @@ +#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; + } + + modulo_array() { + buffer = new(zeromemory) T[size * 3]; + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/src/lib/nall/new.hpp b/src/lib/nall/new.hpp index 62bf64fb..abc2ca59 100644 --- a/src/lib/nall/new.hpp +++ b/src/lib/nall/new.hpp @@ -6,11 +6,9 @@ #include namespace nall { - -struct zeromemory_t {}; -static zeromemory_t zeromemory; - -} //namespace nall + struct zeromemory_t {}; + static zeromemory_t zeromemory; +} inline void* operator new[](size_t size, const nall::zeromemory_t&) throw(std::bad_alloc) { void *p = new uint8_t[size]; @@ -24,4 +22,4 @@ inline void* operator new[](size_t size, const std::nothrow_t&, const nall::zero return p; } -#endif //ifndef NALL_NEW_HPP +#endif diff --git a/src/lib/nall/platform.hpp b/src/lib/nall/platform.hpp index ec02d5fa..bdae675c 100644 --- a/src/lib/nall/platform.hpp +++ b/src/lib/nall/platform.hpp @@ -5,6 +5,8 @@ //standard platform headers //========================= +#include + #include #include #include @@ -70,4 +72,4 @@ #define alwaysinline inline #endif -#endif //ifndef NALL_PLATFORM_HPP +#endif diff --git a/src/lib/nall/priorityqueue.hpp b/src/lib/nall/priorityqueue.hpp new file mode 100644 index 00000000..3daccfab --- /dev/null +++ b/src/lib/nall/priorityqueue.hpp @@ -0,0 +1,94 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#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 : noncopyable { + 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; + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + 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/src/lib/nall/serial.hpp b/src/lib/nall/serial.hpp index 706ebfb5..6f5cf6d6 100644 --- a/src/lib/nall/serial.hpp +++ b/src/lib/nall/serial.hpp @@ -9,74 +9,72 @@ #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) { - 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); - attr.c_cflag |= (CS8 | CREAD | CLOCAL); - 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; + 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); } - } - serial() { - port = -1; - port_open = false; - } + //-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); + } - ~serial() { - close(); - } + bool open(const char *portname, unsigned rate) { + close(); -private: - int port; - bool port_open; - termios original_attr; -}; + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; -} //namespace nall + 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; } -#endif //ifndef NALL_SERIAL_HPP + 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); + attr.c_cflag |= (CS8 | CREAD | CLOCAL); + 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/src/lib/nall/sort.hpp b/src/lib/nall/sort.hpp index 53e30575..23c317a5 100644 --- a/src/lib/nall/sort.hpp +++ b/src/lib/nall/sort.hpp @@ -18,47 +18,45 @@ //(* 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 -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; + //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]); } - if(min != i) swap(list[i], list[min]); + return; } - return; - } - //split list in half and recursively sort both - unsigned middle = length / 2; - sort(list, middle); - sort(list + middle, length - middle); + //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++]; + //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++]; + 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; + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } } -} //namespace nall - -#endif //ifndef NALL_SORT_HPP +#endif diff --git a/src/lib/nall/static.hpp b/src/lib/nall/static.hpp index 14e42bac..00c3664c 100644 --- a/src/lib/nall/static.hpp +++ b/src/lib/nall/static.hpp @@ -2,20 +2,16 @@ #define NALL_STATIC_HPP namespace nall { + template struct static_assert; + template<> struct static_assert {}; -template struct static_assert; -template<> struct static_assert {}; + template struct static_if { + typedef true_type type; + }; -template -struct static_if { - typedef true_type type; -}; + template struct static_if { + typedef false_type type; + }; +} -template -struct static_if { - typedef false_type type; -}; - -} //namespace nall - -#endif //ifndef NALL_STATIC_HPP +#endif diff --git a/src/lib/nall/stdint.hpp b/src/lib/nall/stdint.hpp index ba58bda2..7e2c7a46 100644 --- a/src/lib/nall/stdint.hpp +++ b/src/lib/nall/stdint.hpp @@ -30,17 +30,15 @@ #endif namespace nall { + static static_assert int8_t_assert; + static static_assert int16_t_assert; + static static_assert int32_t_assert; + static static_assert int64_t_assert; -static static_assert int8_t_assert; -static static_assert int16_t_assert; -static static_assert int32_t_assert; -static static_assert int64_t_assert; + static static_assert uint8_t_assert; + static static_assert uint16_t_assert; + static static_assert uint32_t_assert; + static static_assert uint64_t_assert; +} -static static_assert uint8_t_assert; -static static_assert uint16_t_assert; -static static_assert uint32_t_assert; -static static_assert uint64_t_assert; - -} //namespace nall - -#endif //ifndef NALL_STDINT_HPP +#endif diff --git a/src/lib/nall/string.cpp b/src/lib/nall/string.cpp index f1818bd4..565cbc7c 100644 --- a/src/lib/nall/string.cpp +++ b/src/lib/nall/string.cpp @@ -17,4 +17,4 @@ #include #include -#endif //ifndef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string.hpp b/src/lib/nall/string.hpp index 46b9b575..d1748a4a 100644 --- a/src/lib/nall/string.hpp +++ b/src/lib/nall/string.hpp @@ -51,46 +51,44 @@ char* rtrim_once(char *str, const char *key = " "); char* trim_once (char *str, const char *key = " "); namespace nall { + class string { + public: + char *data; + size_t size; -class string { -public: - char *data; - size_t size; + void reserve(size_t size_); - void reserve(size_t size_); + operator int() const; + operator const char*() const; + char* operator()(); + char& operator[](int); - operator int() const; - operator const char*() const; - char* operator()(); - char& operator[](int); + string& operator=(int num); + string& operator=(double num); + string& operator=(const char *str); + string& operator=(const string &str); + string& operator<<(int num); + string& operator<<(double num); + string& operator<<(const char *str); + string& operator<<(const string& str); - string& operator=(int num); - string& operator=(double num); - string& operator=(const char *str); - string& operator=(const string &str); - string& operator<<(int num); - string& operator<<(double num); - string& operator<<(const char *str); - string& operator<<(const string& str); + bool operator==(const char *str) const; + bool operator!=(const char *str) const; + bool operator< (const char *str) const; + bool operator<=(const char *str) const; + bool operator> (const char *str) const; + bool operator>=(const char *str) const; - bool operator==(const char *str) const; - bool operator!=(const char *str) const; - bool operator< (const char *str) const; - bool operator<=(const char *str) const; - bool operator> (const char *str) const; - bool operator>=(const char *str) const; + string(); + string(int num); + string(double num); + string(const char *source); + string(const string &source); + ~string(); + }; - string(); - string(int num); - string(double num); - string(const char *source); - string(const string &source); - ~string(); -}; - -typedef vector lstring; - -} //namespace nall + typedef vector lstring; +} size_t count(nall::lstring&); int find(nall::lstring &str, const char *key); @@ -118,4 +116,4 @@ nall::string& ltrim_once(nall::string &str, const char *key = " "); nall::string& rtrim_once(nall::string &str, const char *key = " "); nall::string& trim_once (nall::string &str, const char *key = " "); -#endif //ifndef NALL_STRING_HPP +#endif diff --git a/src/lib/nall/string/class.cpp b/src/lib/nall/string/class.cpp index efb6586f..9a159741 100644 --- a/src/lib/nall/string/class.cpp +++ b/src/lib/nall/string/class.cpp @@ -229,4 +229,4 @@ bool fread(nall::string &str, const char *filename) { return true; } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/compare.cpp b/src/lib/nall/string/compare.cpp index 081a05d7..168063a2 100644 --- a/src/lib/nall/string/compare.cpp +++ b/src/lib/nall/string/compare.cpp @@ -96,4 +96,4 @@ bool striend(const char *str, const char *key) { return true; } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/convert.cpp b/src/lib/nall/string/convert.cpp index 51152375..46744638 100644 --- a/src/lib/nall/string/convert.cpp +++ b/src/lib/nall/string/convert.cpp @@ -252,4 +252,4 @@ size_t strdouble(char *str, double value, size_t length /* = 0 */) { return length + 1; } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/match.cpp b/src/lib/nall/string/match.cpp index c73df02a..186ed198 100644 --- a/src/lib/nall/string/match.cpp +++ b/src/lib/nall/string/match.cpp @@ -68,4 +68,4 @@ bool match(const char *p, const char *s) { } } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/math.cpp b/src/lib/nall/string/math.cpp index 3dabde3b..31835b10 100644 --- a/src/lib/nall/string/math.cpp +++ b/src/lib/nall/string/math.cpp @@ -156,4 +156,4 @@ bool strmath(const char *s, int &result) { } } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/replace.cpp b/src/lib/nall/string/replace.cpp index b29e02d9..34acf7c8 100644 --- a/src/lib/nall/string/replace.cpp +++ b/src/lib/nall/string/replace.cpp @@ -87,4 +87,4 @@ nall::string &qreplace(nall::string &str, const char *key, const char *token) { return str; } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/split.cpp b/src/lib/nall/string/split.cpp index b68b5c13..7905dfa0 100644 --- a/src/lib/nall/string/split.cpp +++ b/src/lib/nall/string/split.cpp @@ -48,4 +48,4 @@ void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit) strcpy(dest[split_count++], src + lp); } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/strl.cpp b/src/lib/nall/string/strl.cpp index a140fbc9..c23f652f 100644 --- a/src/lib/nall/string/strl.cpp +++ b/src/lib/nall/string/strl.cpp @@ -44,4 +44,4 @@ size_t strlcat(char *dest, const char *src, size_t length) { return dlength + (s - src); //return length of resulting string, sans null terminator } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/string/trim.cpp b/src/lib/nall/string/trim.cpp index 861500e4..1c5e5bd3 100644 --- a/src/lib/nall/string/trim.cpp +++ b/src/lib/nall/string/trim.cpp @@ -32,4 +32,4 @@ char* trim_once(char *str, const char *key) { return ltrim_once(rtrim_once(str, key), key); } -#endif //ifdef NALL_STRING_CPP +#endif diff --git a/src/lib/nall/traits.hpp b/src/lib/nall/traits.hpp index 168e90d3..96320200 100644 --- a/src/lib/nall/traits.hpp +++ b/src/lib/nall/traits.hpp @@ -2,89 +2,93 @@ #define NALL_TRAITS_HPP namespace nall { + //== + //is + //== -/* is */ + template struct is_integral { enum { value = false }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; -template struct is_integral { enum { value = false }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; -template<> struct is_integral { enum { value = true }; }; + template struct is_floating_point { enum { value = false }; }; + template<> struct is_floating_point { enum { value = true }; }; + template<> struct is_floating_point { enum { value = true }; }; + template<> struct is_floating_point { enum { value = true }; }; -template struct is_floating_point { enum { value = false }; }; -template<> struct is_floating_point { enum { value = true }; }; -template<> struct is_floating_point { enum { value = true }; }; -template<> struct is_floating_point { enum { value = true }; }; + template struct is_void { enum { value = false }; }; + template<> struct is_void { enum { value = true }; }; -template struct is_void { enum { value = false }; }; -template<> struct is_void { enum { value = true }; }; + template struct is_arithmetic { + enum { value = is_integral::value || is_floating_point::value }; + }; -template struct is_arithmetic { - enum { value = is_integral::value || is_floating_point::value }; -}; + template struct is_fundamental { + enum { value = is_integral::value || is_floating_point::value || is_void::value }; + }; -template struct is_fundamental { - enum { value = is_integral::value || is_floating_point::value || is_void::value }; -}; + template struct is_compound { + enum { value = !is_fundamental::value }; + }; -template struct is_compound { - enum { value = !is_fundamental::value }; -}; + template struct is_array { enum { value = false }; }; + template struct is_array { enum { value = true }; }; + template struct is_array { enum { value = true }; }; -template struct is_array { enum { value = false }; }; -template struct is_array { enum { value = true }; }; -template struct is_array { enum { value = true }; }; + template struct is_const { enum { value = false }; }; + template struct is_const { enum { value = true }; }; + template struct is_const { enum { value = true }; }; -template struct is_const { enum { value = false }; }; -template struct is_const { enum { value = true }; }; -template struct is_const { enum { value = true }; }; + template struct is_pointer { enum { value = false }; }; + template struct is_pointer { enum { value = true }; }; -template struct is_pointer { enum { value = false }; }; -template struct is_pointer { enum { value = true }; }; + template struct is_reference { enum { value = false }; }; + template struct is_reference { enum { value = true }; }; -template struct is_reference { enum { value = false }; }; -template struct is_reference { enum { value = true }; }; + template struct is_same { enum { value = false }; }; + template struct is_same { enum { value = true }; }; -template struct is_same { enum { value = false }; }; -template struct is_same { enum { value = true }; }; + //=== + //add + //=== -/* add */ + template struct add_const { typedef const T type; }; + template struct add_const { typedef const T type; }; + template struct add_const { typedef const T& type; }; -template struct add_const { typedef const T type; }; -template struct add_const { typedef const T type; }; -template struct add_const { typedef const T& type; }; + template struct add_pointer { typedef T* type; }; + template struct add_pointer { typedef T** type; }; -template struct add_pointer { typedef T* type; }; -template struct add_pointer { typedef T** type; }; + template struct add_reference { typedef T& type; }; + template struct add_reference { typedef T& type; }; -template struct add_reference { typedef T& type; }; -template struct add_reference { typedef T& type; }; + //====== + //remove + //====== -/* remove */ + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; -template struct remove_const { typedef T type; }; -template struct remove_const { typedef T type; }; -template struct remove_const { typedef T type; }; + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; -template struct remove_extent { typedef T type; }; -template struct remove_extent { typedef T type; }; -template struct remove_extent { typedef T type; }; + template struct remove_pointer { typedef T type; }; + template struct remove_pointer { typedef T type; }; -template struct remove_pointer { typedef T type; }; -template struct remove_pointer { typedef T type; }; + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; +} -template struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; - -} //namespace nall - -#endif //ifndef NALL_TRAITS_HPP +#endif diff --git a/src/lib/nall/ups.hpp b/src/lib/nall/ups.hpp index 737e8527..610e3646 100644 --- a/src/lib/nall/ups.hpp +++ b/src/lib/nall/ups.hpp @@ -10,184 +10,182 @@ #include namespace nall { + class ups { + public: + enum result { + ok, + patch_unreadable, + patch_unwritable, + patch_invalid, + input_invalid, + output_invalid, + patch_crc32_invalid, + input_crc32_invalid, + output_crc32_invalid, + }; -class ups { -public: - enum result { - ok, - patch_unreadable, - patch_unwritable, - patch_invalid, - input_invalid, - output_invalid, - patch_crc32_invalid, - input_crc32_invalid, - output_crc32_invalid, - }; + ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; - ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { - if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; + crc32 = ~0; + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); - crc32 = ~0; - uint32_t x_crc32 = crc32_calculate(x_data, x_size); - uint32_t y_crc32 = crc32_calculate(y_data, y_size); + //header + write('U'); + write('P'); + write('S'); + write('1'); + encptr(x_size); + encptr(y_size); - //header - write('U'); - write('P'); - write('S'); - write('1'); - encptr(x_size); - encptr(y_size); + //body + unsigned max_size = max(x_size, y_size); + unsigned relative = 0; + for(unsigned i = 0; i < max_size;) { + uint8_t x = i < x_size ? x_data[i] : 0x00; + uint8_t y = i < y_size ? y_data[i] : 0x00; - //body - unsigned max_size = max(x_size, y_size); - unsigned relative = 0; - for(unsigned i = 0; i < max_size;) { - uint8_t x = i < x_size ? x_data[i] : 0x00; - uint8_t y = i < y_size ? y_data[i] : 0x00; + if(x == y) { + i++; + continue; + } - if(x == y) { - i++; - continue; + encptr(i++ - relative); + write(x ^ y); + + while(true) { + if(i >= max_size) { + write(0x00); + break; + } + + x = i < x_size ? x_data[i] : 0x00; + y = i < y_size ? y_data[i] : 0x00; + i++; + write(x ^ y); + if(x == y) break; + } + + relative = i; } - encptr(i++ - relative); - write(x ^ y); + //footer + for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); + for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + fp.close(); + return ok; + } + + ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { + if(p_size < 18) return patch_invalid; + p_buffer = p_data; + + crc32 = ~0; + + //header + if(read() != 'U') return patch_invalid; + if(read() != 'P') return patch_invalid; + if(read() != 'S') return patch_invalid; + if(read() != '1') return patch_invalid; + + unsigned px_size = decptr(); + unsigned py_size = decptr(); + + //mirror + if(x_size != px_size && x_size != py_size) return input_invalid; + y_size = (x_size == px_size) ? py_size : px_size; + y_data = new(zeromemory) uint8_t[y_size]; + + for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; + for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; + + //body + unsigned relative = 0; + while(p_buffer < p_data + p_size - 12) { + relative += decptr(); + + while(true) { + uint8_t x = read(); + if(x && relative < y_size) { + uint8_t y = relative < x_size ? x_data[relative] : 0x00; + y_data[relative] = x ^ y; + } + relative++; + if(!x) break; + } + } + + //footer + unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; + for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); + for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); + + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + if(px_size != py_size) { + if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; + if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; + if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; + } else { + if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; + } + + if(p_crc32 != pp_crc32) return patch_crc32_invalid; + return ok; + } + + private: + file fp; + uint32_t crc32; + const uint8_t *p_buffer; + + uint8_t read() { + uint8_t n = *p_buffer++; + crc32 = crc32_adjust(crc32, n); + return n; + } + + void write(uint8_t n) { + fp.write(n); + crc32 = crc32_adjust(crc32, n); + } + + void encptr(uint64_t offset) { while(true) { - if(i >= max_size) { - write(0x00); + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + write(0x80 | x); break; } - - x = i < x_size ? x_data[i] : 0x00; - y = i < y_size ? y_data[i] : 0x00; - i++; - write(x ^ y); - if(x == y) break; + write(x); + offset--; } - - relative = i; } - //footer - for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); - for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); - uint32_t p_crc32 = ~crc32; - for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); - - fp.close(); - return ok; - } - - ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { - if(p_size < 18) return patch_invalid; - p_buffer = p_data; - - crc32 = ~0; - - //header - if(read() != 'U') return patch_invalid; - if(read() != 'P') return patch_invalid; - if(read() != 'S') return patch_invalid; - if(read() != '1') return patch_invalid; - - unsigned px_size = decptr(); - unsigned py_size = decptr(); - - //mirror - if(x_size != px_size && x_size != py_size) return input_invalid; - y_size = (x_size == px_size) ? py_size : px_size; - y_data = new(zeromemory) uint8_t[y_size]; - - for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; - for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; - - //body - unsigned relative = 0; - while(p_buffer < p_data + p_size - 12) { - relative += decptr(); - + uint64_t decptr() { + uint64_t offset = 0, shift = 1; while(true) { uint8_t x = read(); - if(x && relative < y_size) { - uint8_t y = relative < x_size ? x_data[relative] : 0x00; - y_data[relative] = x ^ y; - } - relative++; - if(!x) break; + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; } + return offset; } + }; +} - //footer - unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; - for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); - for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); - uint32_t p_crc32 = ~crc32; - for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); - - uint32_t x_crc32 = crc32_calculate(x_data, x_size); - uint32_t y_crc32 = crc32_calculate(y_data, y_size); - - if(px_size != py_size) { - if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; - if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; - if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; - if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; - } else { - if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; - if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; - if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; - if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; - } - - if(p_crc32 != pp_crc32) return patch_crc32_invalid; - return ok; - } - -private: - file fp; - uint32_t crc32; - const uint8_t *p_buffer; - - uint8_t read() { - uint8_t n = *p_buffer++; - crc32 = crc32_adjust(crc32, n); - return n; - } - - void write(uint8_t n) { - fp.write(n); - crc32 = crc32_adjust(crc32, n); - } - - void encptr(uint64_t offset) { - while(true) { - uint64_t x = offset & 0x7f; - offset >>= 7; - if(offset == 0) { - write(0x80 | x); - break; - } - write(x); - offset--; - } - } - - uint64_t decptr() { - uint64_t offset = 0, shift = 1; - while(true) { - uint8_t x = read(); - offset += (x & 0x7f) * shift; - if(x & 0x80) break; - shift <<= 7; - offset += shift; - } - return offset; - } -}; - -} //namespace nall - -#endif //ifndef NALL_UPS_HPP +#endif diff --git a/src/lib/nall/utf8.hpp b/src/lib/nall/utf8.hpp index aedcbfdd..ad23fb73 100644 --- a/src/lib/nall/utf8.hpp +++ b/src/lib/nall/utf8.hpp @@ -8,64 +8,64 @@ #if defined(_WIN32) +#undef NOMINMAX +#define NOMINMAX #include namespace nall { + //UTF-8 to UTF-16 + class utf16 { + public: + operator wchar_t*() { + return buffer; + } -//UTF-8 to UTF-16 -class utf16 { -public: - operator wchar_t*() { - return buffer; - } + operator const wchar_t*() const { + return buffer; + } - operator const wchar_t*() const { - return buffer; - } + utf16(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new(zeromemory) wchar_t[length + 1]; + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } - utf16(const char *s = "") { - if(!s) s = ""; - unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); - buffer = new(zeromemory) wchar_t[length + 1]; - MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); - } + ~utf16() { + delete[] buffer; + } - ~utf16() { - delete[] buffer; - } + private: + wchar_t *buffer; + }; -private: - wchar_t *buffer; -}; + //UTF-16 to UTF-8 + class utf8 { + public: + operator char*() { + return buffer; + } -//UTF-16 to UTF-8 -class utf8 { -public: - operator char*() { - return buffer; - } + operator const char*() const { + return buffer; + } - operator const char*() const { - return buffer; - } + utf8(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(zeromemory) char[length + 1]; + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } - utf8(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(zeromemory) char[length + 1]; - WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); - } + ~utf8() { + delete[] buffer; + } - ~utf8() { - delete[] buffer; - } + private: + char *buffer; + }; +} -private: - char *buffer; -}; +#endif //if defined(_WIN32) -} //namespace nall - -#endif //if defined(_WIN32) - -#endif //ifndef NALL_UTF8_HPP +#endif diff --git a/src/lib/nall/utility.hpp b/src/lib/nall/utility.hpp index 6d5cfd7e..14d3f873 100644 --- a/src/lib/nall/utility.hpp +++ b/src/lib/nall/utility.hpp @@ -2,29 +2,28 @@ #define NALL_UTILITY_HPP namespace nall { + template + inline void swap(T &x, T &y) { + T temp(x); + x = y; + y = temp; + } -template inline void swap(T &x, T &y) { - T temp = x; - x = y; - y = temp; + template + struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + class noncopyable { + protected: + noncopyable() {} + ~noncopyable() {} + + private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); + }; } -template -struct base_from_member { - T value; - base_from_member(T value_) : value(value_) {} -}; - -class noncopyable { -protected: - noncopyable() {} - ~noncopyable() {} - -private: - noncopyable(const noncopyable&); - const noncopyable& operator=(const noncopyable&); -}; - -} //namespace nall - -#endif //ifndef NALL_UTILITY_HPP +#endif diff --git a/src/lib/nall/varint.hpp b/src/lib/nall/varint.hpp index 72eef460..cc3bb17c 100644 --- a/src/lib/nall/varint.hpp +++ b/src/lib/nall/varint.hpp @@ -6,89 +6,87 @@ #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, + 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 long) >= bytes, - unsigned long long, - void + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type >::type - >::type - >::type T; - static_assert::value> uint_assert; - T data; + >::type T; + static_assert::value> uint_assert; + 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); } + 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)) {} -}; + 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, + 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 long) >= bytes, - signed long long, - void + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type >::type - >::type - >::type T; - static_assert::value> int_assert; - T data; + >::type T; + static_assert::value> int_assert; + 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); } + 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)) {} -}; + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} -} //namespace nall - -#endif //ifndef NALL_VARINT_HPP +#endif diff --git a/src/lib/nall/vector.hpp b/src/lib/nall/vector.hpp index 699f5e1b..ca12b806 100644 --- a/src/lib/nall/vector.hpp +++ b/src/lib/nall/vector.hpp @@ -3,160 +3,160 @@ #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. -//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 : noncopyable { + protected: + T *pool; + unsigned poolsize, objectsize; -template class linear_vector { -protected: - T *pool; - unsigned poolsize, objectsize; + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } -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 reset() { - if(pool) { + 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 = 0; - poolsize = 0; - objectsize = 0; - } - - void reserve(unsigned newsize) { - newsize <<= 1; //amortized constant 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; + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); } - objectsize = newsize; - } + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); - void add(const T data) { - if(objectsize + 1 > poolsize) reserve(objectsize + 1); - new(pool + objectsize++) T(data); - } + 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; + } - 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]; - } - - linear_vector() : pool(0), poolsize(0), objectsize(0) {} - ~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 <<= 1; //amortized constant growth - - for(unsigned i = newsize; i < objectsize; i++) { - if(pool[i]) { delete pool[i]; pool[i] = 0; } + objectsize = newsize; } - 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; } + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); } - objectsize = newsize; - } + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } - void add(const T data) { - if(objectsize + 1 > poolsize) reserve(objectsize + 1); - pool[objectsize++] = new T(data); - } + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } - inline T& operator[](unsigned index) { - if(index >= objectsize) resize(index + 1); - if(!pool[index]) pool[index] = new T; - return *pool[index]; - } + linear_vector() : pool(0), poolsize(0), objectsize(0) {} + ~linear_vector() { reset(); } + }; - inline const T& operator[](unsigned index) const { - if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; - return *pool[index]; - } + //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. - pointer_vector() : pool(0), poolsize(0), objectsize(0) {} - ~pointer_vector() { reset(); } -}; + template class pointer_vector : noncopyable { + protected: + T **pool; + unsigned poolsize, objectsize; -//default vector type -template class vector : public linear_vector {}; - -} //namespace nall + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } -#endif //ifndef NALL_VECTOR_HPP + 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 add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + 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]; + } + + pointer_vector() : pool(0), poolsize(0), objectsize(0) {} + ~pointer_vector() { reset(); } + }; + + //default vector type + template class vector : public linear_vector {}; +} + +#endif diff --git a/src/lib/ruby/input/directinput.cpp b/src/lib/ruby/input/directinput.cpp index 0d4b80ad..ac9777fc 100644 --- a/src/lib/ruby/input/directinput.cpp +++ b/src/lib/ruby/input/directinput.cpp @@ -1,3 +1,4 @@ +#define DIRECTINPUT_VERSION 0x0800 #include #include diff --git a/src/lib/sync.bat b/src/lib/sync.bat index 88362bdd..47175d6e 100644 --- a/src/lib/sync.bat +++ b/src/lib/sync.bat @@ -9,3 +9,8 @@ mkdir ruby xcopy /E ..\..\..\nall nall xcopy /E ..\..\..\hiro hiro xcopy /E ..\..\..\ruby ruby + +del hiro\test* +del hiro\cc.* +del ruby\test* +del ruby\cc.* diff --git a/src/lib/tool/opgen_switch.cpp b/src/lib/tool/opgen_switch.cpp index eb6cfbde..80da12e1 100644 --- a/src/lib/tool/opgen_switch.cpp +++ b/src/lib/tool/opgen_switch.cpp @@ -3,8 +3,8 @@ using namespace nall; FILE *fp; -string data; -lstring line, part, subpart, output_op; +string data, output_op; +lstring line, part, subpart; struct OpList { string name; @@ -89,7 +89,7 @@ void gen_op() { void gen_end() { string t; for(unsigned i = 0; i < op_count; i++) { - t = output_op[0]; + t = output_op; replace(t, "$$", op_list[i].name); replace(t, "$0", op_list[i].arg[0]); replace(t, "$1", op_list[i].arg[1]); @@ -103,12 +103,14 @@ void gen_end() { } } -void generate(char *dest, char *src) { +void generate(const char *dest, const char *src) { fread(data, src); replace(data, "\r\n", "\n"); split(line, "\n", data); fp = fopen(dest, "wb"); + string header = CLASS_NAME; + fprintf(fp, "#ifdef %s_CPP\n\n", (const char*)strupper(header)); //inclusion guard line_num = 0; while(line_num < count(line)) { @@ -120,5 +122,6 @@ void generate(char *dest, char *src) { gen_end(); } + fprintf(fp, "#endif\n"); fclose(fp); } diff --git a/src/memory/smemory/smemory.cpp b/src/memory/smemory/smemory.cpp index cf201e67..a9ffce6a 100644 --- a/src/memory/smemory/smemory.cpp +++ b/src/memory/smemory/smemory.cpp @@ -1,4 +1,6 @@ #include <../base.hpp> +#include <../chip/chip.hpp> +#include <../cart/cart.hpp> #define SMEMORY_CPP #include "mapper/system.cpp" @@ -7,7 +9,7 @@ void sBus::power() { for(unsigned i = 0x2000; i <= 0x5fff; i++) memory::mmio.map(i, memory::mmio_unmapped); - for(unsigned i = 0; i < memory::wram.size(); i++) memory::wram[i] = config::cpu.wram_init_value; + for(unsigned i = 0; i < memory::wram.size(); i++) memory::wram[i] = snes.config.cpu.wram_init_value; reset(); } diff --git a/src/ppu/bppu/bppu.cpp b/src/ppu/bppu/bppu.cpp index 6ab44586..d5481f3c 100644 --- a/src/ppu/bppu/bppu.cpp +++ b/src/ppu/bppu/bppu.cpp @@ -40,22 +40,22 @@ void bPPU::add_clocks(unsigned clocks) { void bPPU::scanline() { snes.scanline(); - line.y = ivcounter(); + line = ivcounter(); - if(line.y == 0) { + if(line == 0) { //RTO flag reset regs.time_over = false; regs.range_over = false; } - if(line.y == 1) { + if(line == 1) { //mosaic reset for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1; regs.mosaic_countdown = regs.mosaic_size + 1; regs.mosaic_countdown--; } else { for(int bg = BG1; bg <= BG4; bg++) { - if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line.y; + if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line; } if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1; regs.mosaic_countdown--; @@ -68,7 +68,7 @@ void bPPU::render_scanline() { if(status.render_output == false) return; #endif - if(line.y >= 1 && line.y < (!overscan() ? 225 : 240)) { + if(line >= 1 && line < (!overscan() ? 225 : 240)) { render_line_oam_rto(); render_line(); } diff --git a/src/ppu/bppu/bppu.hpp b/src/ppu/bppu/bppu.hpp index bdce2a3b..5e2f2135 100644 --- a/src/ppu/bppu/bppu.hpp +++ b/src/ppu/bppu/bppu.hpp @@ -4,18 +4,15 @@ public: void add_clocks(unsigned clocks); uint8 region; + unsigned line; enum { NTSC = 0, PAL = 1 }; enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 }; enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 }; - struct { - uint y; - } line; - struct { bool interlace; - bool overscan; + bool overscan; } display; struct { @@ -153,9 +150,9 @@ public: uint16 oam_tdaddr; } cache; - alwaysinline bool interlace() { return display.interlace; } - alwaysinline bool overscan() { return display.overscan; } - alwaysinline bool hires() { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); } + alwaysinline bool interlace() const { return display.interlace; } + alwaysinline bool overscan() const { return display.overscan; } + alwaysinline bool hires() const { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); } uint16 get_vram_address(); uint8 vram_mmio_read (uint16 addr); @@ -230,8 +227,8 @@ public: uint8 mmio_r213e(); //STAT77 uint8 mmio_r213f(); //STAT78 - uint8 mmio_read(uint addr); - void mmio_write(uint addr, uint8 data); + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); void latch_counters(); diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index d3276e16..8b8945f3 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -737,7 +737,7 @@ uint8 r = 0x00; return regs.ppu2_mdr; } -uint8 bPPU::mmio_read(uint addr) { +uint8 bPPU::mmio_read(unsigned addr) { scheduler.sync_cpuppu(); switch(addr & 0xffff) { @@ -777,7 +777,7 @@ uint8 bPPU::mmio_read(uint addr) { return cpu.regs.mdr; } -void bPPU::mmio_write(uint addr, uint8 data) { +void bPPU::mmio_write(unsigned addr, uint8 data) { scheduler.sync_cpuppu(); switch(addr & 0xffff) { diff --git a/src/ppu/bppu/bppu_render_addsub.cpp b/src/ppu/bppu/bppu_render_addsub.cpp index e47ad811..447aa4e1 100644 --- a/src/ppu/bppu/bppu_render_addsub.cpp +++ b/src/ppu/bppu/bppu_render_addsub.cpp @@ -1,21 +1,19 @@ #ifdef BPPU_CPP - -/***** - * Color Addition / Subtraction - * Thanks to blargg for the optimized algorithms - *****/ + +//color addition / subtraction +//thanks go to blargg for the optimized algorithms inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) { if(!regs.color_mode) { if(!halve) { - uint sum = x + y; - uint carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; + unsigned sum = x + y; + unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; return (sum - carry) | (carry - (carry >> 5)); } else { return (x + y - ((x ^ y) & 0x0421)) >> 1; } } else { - uint diff = x - y + 0x8420; - uint borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; + unsigned diff = x - y + 0x8420; + unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; if(!halve) { return (diff - borrow) & (borrow - (borrow >> 5)); } else { @@ -24,4 +22,4 @@ inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) { } } -#endif //ifdef BPPU_CPP +#endif diff --git a/src/ppu/bppu/bppu_render_bg.cpp b/src/ppu/bppu/bppu_render_bg.cpp index 3a2b022a..0bf63d7a 100644 --- a/src/ppu/bppu/bppu_render_bg.cpp +++ b/src/ppu/bppu/bppu_render_bg.cpp @@ -2,8 +2,8 @@ //called once at the start of every rendered scanline void bPPU::update_bg_info() { - uint hires = (regs.bg_mode == 5 || regs.bg_mode == 6); - uint width = (!hires) ? 256 : 512; + unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6); + unsigned width = (!hires) ? 256 : 512; for(int bg = 0; bg < 4; bg++) { bg_info[bg].th = (regs.bg_tilesize[bg]) ? 4 : 3; @@ -71,7 +71,7 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri uint16 tile_mask = 0x0fff >> color_depth; //0x0fff, 0x07ff, 0x03ff //4 + color_depth = >>(4-6) -- / {16, 32, 64 } bytes/tile //index is a tile number count to add to base tile number - uint tiledata_index = regs.bg_tdaddr[bg] >> (4 + color_depth); + unsigned tiledata_index = regs.bg_tdaddr[bg] >> (4 + color_depth); uint8 *bg_td = bg_tiledata[color_depth]; uint8 *bg_td_state = bg_tiledata_state[color_depth]; @@ -85,8 +85,8 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri uint16 hscroll = regs.bg_hofs[bg]; uint16 vscroll = regs.bg_vofs[bg]; - uint hires = (regs.bg_mode == 5 || regs.bg_mode == 6); - uint width = (!hires) ? 256 : 512; + unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6); + unsigned width = (!hires) ? 256 : 512; if(hires) { hscroll <<= 1; @@ -95,13 +95,13 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri uint16 *mtable = mosaic_table[(regs.mosaic_enabled[bg]) ? regs.mosaic_size : 0]; - uint16 hval, vval; - uint16 t, tile_pri, tile_num; - uint8 pal_index, pal_num; - uint8 *tile_ptr; - uint xpos, ypos; - uint16 hoffset, voffset, opt_x, col; - bool mirror_x, mirror_y; + uint16 hval, vval; + uint16 t, tile_pri, tile_num; + uint8 pal_index, pal_num; + uint8 *tile_ptr; + unsigned xpos, ypos; + uint16 hoffset, voffset, opt_x, col; + bool mirror_x, mirror_y; const bool is_opt_mode = (regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6); build_window_tables(bg); diff --git a/src/ppu/bppu/bppu_render_line.cpp b/src/ppu/bppu/bppu_render_line.cpp index 43400ba6..47f734b9 100644 --- a/src/ppu/bppu/bppu_render_line.cpp +++ b/src/ppu/bppu/bppu_render_line.cpp @@ -85,7 +85,7 @@ inline uint16 bPPU::get_pixel_swap(uint32 x) { } inline void bPPU::render_line_output() { - uint16 *ptr = (uint16*)output + (line.y * 1024) + + uint16 *ptr = (uint16*)output + (line * 1024) + ((interlace() && ifield()) ? 512 : 0); uint16 *luma_b = light_table_b [regs.display_brightness]; uint16 *luma_gr = light_table_gr[regs.display_brightness]; @@ -131,7 +131,7 @@ inline void bPPU::render_line_output() { } inline void bPPU::render_line_clear() { - uint16 *ptr = (uint16*)output + (line.y * 1024) + + uint16 *ptr = (uint16*)output + (line * 1024) + ((interlace() && ifield()) ? 512 : 0); uint16 width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512; memset(ptr, 0, width * 2 * sizeof(uint16)); diff --git a/src/ppu/bppu/bppu_render_mode7.cpp b/src/ppu/bppu/bppu_render_mode7.cpp index 18de21f0..4134b51c 100644 --- a/src/ppu/bppu/bppu_render_mode7.cpp +++ b/src/ppu/bppu/bppu_render_mode7.cpp @@ -46,7 +46,7 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) { uint8 *wt_main = window[bg].main; uint8 *wt_sub = window[bg].sub; - int32 y = (regs.mode7_vflip == false) ? (line.y) : (255 - line.y); + int32 y = (regs.mode7_vflip == false ? line : 255 - line); uint16 *mtable_x, *mtable_y; if(bg == BG1) { diff --git a/src/ppu/bppu/bppu_render_oam.cpp b/src/ppu/bppu/bppu_render_oam.cpp index 5394fa73..ee0e9042 100644 --- a/src/ppu/bppu/bppu_render_oam.cpp +++ b/src/ppu/bppu/bppu_render_oam.cpp @@ -58,15 +58,15 @@ bool bPPU::is_sprite_on_scanline() { if(spr->x > 256 && (spr->x + spr->width - 1) < 512) return false; int spr_height = (regs.oam_interlace == false) ? (spr->height) : (spr->height >> 1); - if(line.y >= spr->y && line.y < (spr->y + spr_height)) return true; - if((spr->y + spr_height) >= 256 && line.y < ((spr->y + spr_height) & 255)) return true; + if(line >= spr->y && line < (spr->y + spr_height)) return true; + if((spr->y + spr_height) >= 256 && line < ((spr->y + spr_height) & 255)) return true; return false; } void bPPU::load_oam_tiles() { uint16 tile_width = spr->width >> 3; int x = spr->x; - int y = line.y - spr->y; + int y = line - spr->y; if(regs.oam_interlace == true) { y <<= 1; } @@ -96,21 +96,21 @@ void bPPU::load_oam_tiles() { chry &= 15; chry <<= 4; - for(uint tx = 0; tx < tile_width; tx++) { - uint sx = (x + (tx << 3)) & 511; + for(unsigned tx = 0; tx < tile_width; tx++) { + unsigned sx = (x + (tx << 3)) & 511; //ignore sprites that are offscreen, x==256 is a special case that loads all tiles in OBJ if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; if(regs.oam_tilecount++ > 34) break; - uint n = regs.oam_tilecount - 1; + unsigned n = regs.oam_tilecount - 1; oam_tilelist[n].x = sx; oam_tilelist[n].y = y; oam_tilelist[n].pri = spr->priority; oam_tilelist[n].pal = 128 + (spr->palette << 4); oam_tilelist[n].hflip = spr->hflip; - uint mx = (spr->hflip == false) ? tx : ((tile_width - 1) - tx); - uint pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5); + unsigned mx = (spr->hflip == false) ? tx : ((tile_width - 1) - tx); + unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5); oam_tilelist[n].tile = (pos >> 5) & 0x07ff; } } @@ -124,12 +124,12 @@ void bPPU::render_oam_tile(int tile_num) { render_bg_tile(COLORDEPTH_16, t->tile); } - uint sx = t->x; + unsigned sx = t->x; uint8 *tile_ptr = (uint8*)oam_td + (t->tile << 6) + ((t->y & 7) << 3); - for(uint x = 0; x < 8; x++) { + for(unsigned x = 0; x < 8; x++) { sx &= 511; if(sx < 256) { - uint col = *(tile_ptr + ((t->hflip == false) ? x : (7 - x))); + unsigned col = *(tile_ptr + ((t->hflip == false) ? x : (7 - x))); if(col) { col += t->pal; oam_line_pal[sx] = col; diff --git a/src/ppu/counter.cpp b/src/ppu/counter.cpp index a61f8b62..17bdce7e 100644 --- a/src/ppu/counter.cpp +++ b/src/ppu/counter.cpp @@ -1,9 +1,9 @@ #ifdef PPU_CPP //wrappers to allow PPUcounter::tick()/tock() to be inlined -bool PPUcounter::region() { return snes.region() == SNES::NTSC ? 0 : 1; } -bool PPUcounter::interlace() { return ppu.interlace(); } -void PPUcounter::scanline() { cpu.scanline(); } +bool PPUcounter::region() const { return snes.region() == SNES::NTSC ? 0 : 1; } +bool PPUcounter::interlace() const { return ppu.interlace(); } +void PPUcounter::scanline() { cpu.scanline(); } //one PPU dot = 4 CPU clocks // @@ -14,7 +14,7 @@ void PPUcounter::scanline() { cpu.scanline(); } //dot 323 range = { 1292, 1294, 1296 } //dot 327 range = { 1310, 1312, 1314 } -uint16 PPUcounter::hdot() { +uint16 PPUcounter::hdot() const { if(region() == 0 && interlace() == false && status.vcounter == 240 && status.field == 1) { return (status.hcounter >> 2); } else { @@ -22,12 +22,12 @@ uint16 PPUcounter::hdot() { } } -uint16 PPUcounter::lineclocks() { +uint16 PPUcounter::lineclocks() const { if(region() == 0 && interlace() == false && vcounter() == 240 && status.field == 1) return 1360; return 1364; } -uint16 PPUcounter::ilineclocks() { +uint16 PPUcounter::ilineclocks() const { if(region() == 0 && interlace() == false && ivcounter() == 240 && status.field == 1) return 1360; return 1364; } @@ -47,4 +47,4 @@ void PPUcounter::reset() { } } -#endif //ifndef PPU_CPP +#endif diff --git a/src/ppu/counter.hpp b/src/ppu/counter.hpp index a38204a0..515f64ee 100644 --- a/src/ppu/counter.hpp +++ b/src/ppu/counter.hpp @@ -32,28 +32,28 @@ public: } //timing information relative to S-CPU - alwaysinline bool field () { return status.field; } - alwaysinline uint16 vcounter() { return status.vcounter; } - alwaysinline uint16 hcounter() { return status.hcounter; } - uint16 hdot(); - uint16 lineclocks(); + alwaysinline bool field () const { return status.field; } + alwaysinline uint16 vcounter() const { return status.vcounter; } + alwaysinline uint16 hcounter() const { return status.hcounter; } + uint16 hdot() const; + uint16 lineclocks() const; //timing history information relative to S-CPU - alwaysinline bool field (unsigned offset) { return history.field [(history.index - (offset >> 1)) & 2047]; } - alwaysinline uint16 vcounter(unsigned offset) { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } - alwaysinline uint16 hcounter(unsigned offset) { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } + alwaysinline bool field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; } + alwaysinline uint16 vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } + alwaysinline uint16 hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } //timing information relative to S-PPU - alwaysinline bool ifield() { return history.field [(history.index - (history.ppudiff >> 1)) & 2047]; } - alwaysinline uint16 ivcounter() { return history.vcounter[(history.index - (history.ppudiff >> 1)) & 2047]; } - alwaysinline uint16 ihcounter() { return history.hcounter[(history.index - (history.ppudiff >> 1)) & 2047]; } - uint16 ilineclocks(); + alwaysinline bool ifield() const { return history.field [(history.index - (history.ppudiff >> 1)) & 2047]; } + alwaysinline uint16 ivcounter() const { return history.vcounter[(history.index - (history.ppudiff >> 1)) & 2047]; } + alwaysinline uint16 ihcounter() const { return history.hcounter[(history.index - (history.ppudiff >> 1)) & 2047]; } + uint16 ilineclocks() const; void reset(); private: - bool region(); - bool interlace(); + bool region() const; + bool interlace() const; void scanline(); struct { diff --git a/src/ppu/ppu.cpp b/src/ppu/ppu.cpp index 01528a72..2ddf84cd 100644 --- a/src/ppu/ppu.cpp +++ b/src/ppu/ppu.cpp @@ -28,7 +28,6 @@ void PPU::power() { } void PPU::reset() { - PPUcounter::reset(); memset(output, 0, 512 * 480 * sizeof(uint16)); } diff --git a/src/ppu/ppu.hpp b/src/ppu/ppu.hpp index 257aef16..a6064add 100644 --- a/src/ppu/ppu.hpp +++ b/src/ppu/ppu.hpp @@ -25,9 +25,9 @@ public: //* reported by $213f uint8 ppu2_version; - virtual bool interlace() = 0; - virtual bool overscan() = 0; - virtual bool hires() = 0; + virtual bool interlace() const = 0; + virtual bool overscan() const = 0; + virtual bool hires() const = 0; virtual void latch_counters() = 0; diff --git a/src/reader/reader.cpp b/src/reader/reader.cpp index df1dcc8c..035c7129 100644 --- a/src/reader/reader.cpp +++ b/src/reader/reader.cpp @@ -1,6 +1,7 @@ #include <../base.hpp> #define READER_CPP - + +#include "reader.hpp" #include "filereader.cpp" #if defined(GZIP_SUPPORT) diff --git a/src/smp/ssmp/core/op_misc.cpp b/src/smp/ssmp/core/op_misc.cpp index eb4360a9..a778b9e7 100644 --- a/src/smp/ssmp/core/op_misc.cpp +++ b/src/smp/ssmp/core/op_misc.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + //nop case 0x00: { op_io(); @@ -344,3 +346,4 @@ case 0x9e: { regs.p.z = (regs.a == 0); } break; +#endif diff --git a/src/smp/ssmp/core/op_mov.cpp b/src/smp/ssmp/core/op_mov.cpp index 5ebac4e1..03890394 100644 --- a/src/smp/ssmp/core/op_mov.cpp +++ b/src/smp/ssmp/core/op_mov.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + //mov_a_x case 0x7d: { op_io(); @@ -387,3 +389,4 @@ case 0xca: { op_writeaddr(dp, rd); } break; +#endif diff --git a/src/smp/ssmp/core/op_pc.cpp b/src/smp/ssmp/core/op_pc.cpp index d6f9e4b1..b9b59925 100644 --- a/src/smp/ssmp/core/op_pc.cpp +++ b/src/smp/ssmp/core/op_pc.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + //bra case 0x2f: { rd = op_readpc(); @@ -601,3 +603,4 @@ case 0x7f: { regs.pc = rd; } break; +#endif diff --git a/src/smp/ssmp/core/op_read.cpp b/src/smp/ssmp/core/op_read.cpp index 4109360d..3f8f77fd 100644 --- a/src/smp/ssmp/core/op_read.cpp +++ b/src/smp/ssmp/core/op_read.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + //adc_a_const case 0x88: { rd = op_readpc(); @@ -742,3 +744,4 @@ case 0x2a: { regs.p.c = regs.p.c | !(rd & (1 << bit)); } break; +#endif diff --git a/src/smp/ssmp/core/op_rmw.cpp b/src/smp/ssmp/core/op_rmw.cpp index 7c331626..7bba31b6 100644 --- a/src/smp/ssmp/core/op_rmw.cpp +++ b/src/smp/ssmp/core/op_rmw.cpp @@ -1,3 +1,5 @@ +#ifdef SSMP_CPP + //inc_a case 0xbc: { op_io(); @@ -260,3 +262,4 @@ case 0x1a: { regs.p.z = (rd == 0); } break; +#endif diff --git a/src/smp/ssmp/memory/memory.cpp b/src/smp/ssmp/memory/memory.cpp index 4c596021..431b87d9 100644 --- a/src/smp/ssmp/memory/memory.cpp +++ b/src/smp/ssmp/memory/memory.cpp @@ -258,12 +258,12 @@ void sSMP::op_writeaddr(uint16 addr, uint8 data) { alwaysinline uint8 sSMP::op_readdp(uint8 addr) { - return op_read((uint(regs.p.p) << 8) + addr); + return op_read((unsigned(regs.p.p) << 8) + addr); } alwaysinline void sSMP::op_writedp(uint8 addr, uint8 data) { - op_write((uint(regs.p.p) << 8) + addr, data); + op_write((unsigned(regs.p.p) << 8) + addr, data); } #endif //ifdef SSMP_CPP diff --git a/src/smp/ssmp/memory/memory.hpp b/src/smp/ssmp/memory/memory.hpp index 39ba859d..d6e36d51 100644 --- a/src/smp/ssmp/memory/memory.hpp +++ b/src/smp/ssmp/memory/memory.hpp @@ -4,9 +4,9 @@ uint8 port_read(uint8 port); void port_write(uint8 port, uint8 data); -/***** - * core SMP bus functions - *****/ + //====================== + //core SMP bus functions + //====================== uint8 op_busread(uint16 addr); void op_buswrite(uint16 addr, uint8 data); @@ -14,9 +14,9 @@ uint8 op_read(uint16 addr); void op_write(uint16 addr, uint8 data); -/***** - * helper memory addressing functions used by SMP core - *****/ + //=================================================== + //helper memory addressing functions used by SMP core + //=================================================== uint8 op_readpc(); uint8 op_readstack(); diff --git a/src/smp/ssmp/ssmp.cpp b/src/smp/ssmp/ssmp.cpp index 1f26efd2..8ec73bc1 100644 --- a/src/smp/ssmp/ssmp.cpp +++ b/src/smp/ssmp/ssmp.cpp @@ -6,8 +6,8 @@ #include "timing/timing.cpp" void sSMP::power() { - for(unsigned i = 0; i < 65536; i++) { - //SNES hardware APURAM contains pseudo-random data upon power up + for(unsigned i = 0; i < memory::apuram.size(); i++) { + //SNES hardware APURAM contains pseudo-random data upon power up (exact formula is unknown.) //memory::apuram.write(i, (i & 32) ? 0xff : 0x00); memory::apuram.write(i, 0x00); } diff --git a/src/smp/ssmp/ssmp.hpp b/src/smp/ssmp/ssmp.hpp index 2e882355..c8389b2e 100644 --- a/src/smp/ssmp/ssmp.hpp +++ b/src/smp/ssmp/ssmp.hpp @@ -2,7 +2,6 @@ class sSMP : public SMP { public: void enter(); -public: #include "core/core.hpp" #include "memory/memory.hpp" #include "timing/timing.hpp" diff --git a/src/smp/ssmp/timing/timing.cpp b/src/smp/ssmp/timing/timing.cpp index 508a2d66..80d9a794 100644 --- a/src/smp/ssmp/timing/timing.cpp +++ b/src/smp/ssmp/timing/timing.cpp @@ -1,15 +1,13 @@ #ifdef SSMP_CPP -alwaysinline -void sSMP::add_clocks(uint clocks) { +void sSMP::add_clocks(unsigned clocks) { scheduler.addclocks_smp(clocks); } -alwaysinline void sSMP::tick_timers() { t0.tick(); t1.tick(); t2.tick(); } -#endif //ifdef SSMP_CPP +#endif diff --git a/src/smp/ssmp/timing/timing.hpp b/src/smp/ssmp/timing/timing.hpp index 0f7376be..e72b39c2 100644 --- a/src/smp/ssmp/timing/timing.hpp +++ b/src/smp/ssmp/timing/timing.hpp @@ -29,6 +29,6 @@ public: sSMPTimer<128> t1; sSMPTimer< 16> t2; - void add_clocks(uint clocks); - void tick_timers(); + alwaysinline void add_clocks(unsigned clocks); + alwaysinline void tick_timers(); uint32 clocks_executed(); diff --git a/src/snes/scheduler/scheduler.cpp b/src/snes/scheduler/scheduler.cpp index 96e243d5..512e33ce 100644 --- a/src/snes/scheduler/scheduler.cpp +++ b/src/snes/scheduler/scheduler.cpp @@ -1,56 +1,56 @@ #ifdef SNES_CPP -Scheduler scheduler; - -void threadentry_cpu() { cpu.enter(); } -void threadentry_smp() { smp.enter(); } -void threadentry_ppu() { ppu.enter(); } -void threadentry_dsp() { dsp.enter(); } - -void Scheduler::enter() { - switch(clock.active) { - case THREAD_CPU: co_switch(thread_cpu); break; - case THREAD_SMP: co_switch(thread_smp); break; - case THREAD_PPU: co_switch(thread_ppu); break; - case THREAD_DSP: co_switch(thread_dsp); break; - } -} - -void Scheduler::exit() { - co_switch(thread_snes); -} - -void Scheduler::init() { - clock.cpu_freq = snes.region() == SNES::NTSC - ? config::cpu.ntsc_clock_rate - : config::cpu.pal_clock_rate; - clock.smp_freq = snes.region() == SNES::NTSC - ? config::smp.ntsc_clock_rate - : config::smp.pal_clock_rate; - - clock.active = THREAD_CPU; - clock.cpuppu = 0; - clock.cpusmp = 0; - clock.smpdsp = 0; - - if(thread_cpu) co_delete(thread_cpu); - if(thread_smp) co_delete(thread_smp); - if(thread_ppu) co_delete(thread_ppu); - if(thread_dsp) co_delete(thread_dsp); - - thread_snes = co_active(); - thread_cpu = co_create(65536 * sizeof(void*), threadentry_cpu); - thread_smp = co_create(65536 * sizeof(void*), threadentry_smp); - thread_ppu = co_create(65536 * sizeof(void*), threadentry_ppu); - thread_dsp = co_create(65536 * sizeof(void*), threadentry_dsp); -} +Scheduler scheduler; -Scheduler::Scheduler() { - thread_snes = 0; - thread_cpu = 0; - thread_smp = 0; - thread_ppu = 0; - thread_dsp = 0; -} +void threadentry_cpu() { cpu.enter(); } +void threadentry_smp() { smp.enter(); } +void threadentry_ppu() { ppu.enter(); } +void threadentry_dsp() { dsp.enter(); } + +void Scheduler::enter() { + switch(clock.active) { + case THREAD_CPU: co_switch(thread_cpu); break; + case THREAD_SMP: co_switch(thread_smp); break; + case THREAD_PPU: co_switch(thread_ppu); break; + case THREAD_DSP: co_switch(thread_dsp); break; + } +} + +void Scheduler::exit() { + co_switch(thread_snes); +} + +void Scheduler::init() { + clock.cpu_freq = snes.region() == SNES::NTSC + ? snes.config.cpu.ntsc_clock_rate + : snes.config.cpu.pal_clock_rate; + clock.smp_freq = snes.region() == SNES::NTSC + ? snes.config.smp.ntsc_clock_rate + : snes.config.smp.pal_clock_rate; + + clock.active = THREAD_CPU; + clock.cpuppu = 0; + clock.cpusmp = 0; + clock.smpdsp = 0; + + if(thread_cpu) co_delete(thread_cpu); + if(thread_smp) co_delete(thread_smp); + if(thread_ppu) co_delete(thread_ppu); + if(thread_dsp) co_delete(thread_dsp); + + thread_snes = co_active(); + thread_cpu = co_create(65536 * sizeof(void*), threadentry_cpu); + thread_smp = co_create(65536 * sizeof(void*), threadentry_smp); + thread_ppu = co_create(65536 * sizeof(void*), threadentry_ppu); + thread_dsp = co_create(65536 * sizeof(void*), threadentry_dsp); +} + +Scheduler::Scheduler() { + thread_snes = 0; + thread_cpu = 0; + thread_smp = 0; + thread_ppu = 0; + thread_dsp = 0; +} #endif //ifdef SNES_CPP diff --git a/src/snes/snes.cpp b/src/snes/snes.cpp index 3d2720a0..d10441eb 100644 --- a/src/snes/snes.cpp +++ b/src/snes/snes.cpp @@ -1,4 +1,6 @@ #include <../base.hpp> +#include <../chip/chip.hpp> +#include <../cart/cart.hpp> #define SNES_CPP SNES snes; @@ -62,8 +64,8 @@ void SNES::term() { } void SNES::power() { - snes_region = max(0, min(2, config::snes.region)); - snes_expansion = max(0, min(1, config::snes.expansion_port)); + snes_region = max(0, min(2, snes.config.region)); + snes_expansion = max(0, min(1, snes.config.expansion_port)); if(snes_region == Autodetect) { snes_region = cartridge.region() == Cartridge::NTSC ? NTSC : PAL; @@ -71,13 +73,14 @@ void SNES::power() { scheduler.init(); + ppu.PPUcounter::reset(); cpu.power(); smp.power(); dsp.power(); ppu.power(); bus.power(); - if(expansion() == ExpansionBSX) { bsxbase.power(); } + if(expansion() == ExpansionBSX) bsxbase.power(); if(cartridge.info.bsxcart) bsxcart.power(); if(cartridge.info.bsxflash) bsxflash.power(); @@ -114,8 +117,8 @@ void SNES::power() { if(cartridge.info.obc1) obc1.enable(); if(cartridge.info.st010) st010.enable(); - input.port_set_device(0, config::snes.controller_port1); - input.port_set_device(1, config::snes.controller_port2); + input.port_set_device(0, snes.config.controller_port1); + input.port_set_device(1, snes.config.controller_port2); input.update(); video.update(); } @@ -123,6 +126,7 @@ void SNES::power() { void SNES::reset() { scheduler.init(); + ppu.PPUcounter::reset(); cpu.reset(); smp.reset(); dsp.reset(); @@ -144,8 +148,8 @@ void SNES::reset() { if(cartridge.info.obc1) obc1.reset(); if(cartridge.info.st010) st010.reset(); - input.port_set_device(0, config::snes.controller_port1); - input.port_set_device(1, config::snes.controller_port2); + input.port_set_device(0, snes.config.controller_port1); + input.port_set_device(1, snes.config.controller_port2); input.update(); video.update(); } @@ -163,13 +167,39 @@ void SNES::scanline() { void SNES::frame() { } -SNES::Region SNES::region() { +SNES::Region SNES::region() const { return (SNES::Region)snes_region; } -SNES::ExpansionPortDevice SNES::expansion() { +SNES::ExpansionPortDevice SNES::expansion() const { return (SNES::ExpansionPortDevice)snes_expansion; } SNES::SNES() : snes_region(NTSC), snes_expansion(ExpansionNone) { + config.controller_port1 = Input::DeviceJoypad; + config.controller_port2 = Input::DeviceJoypad; + config.expansion_port = ExpansionBSX; + config.region = Autodetect; + + config.file.autodetect_type = false; + config.file.bypass_patch_crc32 = false; + + config.path.base = ""; + config.path.user = ""; + config.path.rom = ""; + config.path.save = ""; + config.path.patch = ""; + config.path.cheat = ""; + config.path.exportdata = ""; + config.path.bsx = ""; + config.path.st = ""; + + config.cpu.ntsc_clock_rate = 21477272; + config.cpu.pal_clock_rate = 21281370; + config.cpu.alu_mul_delay = 2; + config.cpu.alu_div_delay = 2; + config.cpu.wram_init_value = 0x55; + + config.smp.ntsc_clock_rate = 32041 * 768; + config.smp.pal_clock_rate = 32041 * 768; } diff --git a/src/snes/snes.hpp b/src/snes/snes.hpp index 78e9d337..8336046b 100644 --- a/src/snes/snes.hpp +++ b/src/snes/snes.hpp @@ -10,6 +10,37 @@ public: enum RegionAutodetect { Autodetect = 2 }; enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 }; + struct Config { + unsigned controller_port1; + unsigned controller_port2; + unsigned expansion_port; + unsigned region; + + struct File { + bool autodetect_type; + bool bypass_patch_crc32; + } file; + + struct Path { + string base, user; + string rom, save, patch, cheat, exportdata; + string bsx, st; + } path; + + struct CPU { + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + unsigned alu_mul_delay; + unsigned alu_div_delay; + unsigned wram_init_value; + } cpu; + + struct SMP { + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + } smp; + } config; + //system functions virtual void run(); virtual void runtoframe(); @@ -24,17 +55,17 @@ public: //return *active* region / expansion port device information //settings cached upon power-on - Region region(); - ExpansionPortDevice expansion(); + Region region() const; + ExpansionPortDevice expansion() const; #include "video/video.hpp" #include "audio/audio.hpp" #include "input/input.hpp" SNES(); - virtual ~SNES() {} - -private: + virtual ~SNES() {} + +private: unsigned snes_region; unsigned snes_expansion; }; diff --git a/src/snes/tracer/tracer.cpp b/src/snes/tracer/tracer.cpp index 1eea80af..2526ed8f 100644 --- a/src/snes/tracer/tracer.cpp +++ b/src/snes/tracer/tracer.cpp @@ -1,94 +1,94 @@ #ifdef SNES_CPP -Tracer tracer; - -void tprintf(const char *s, ...) { - if(tracer.enabled() == false) { return; } - -char str[4096]; -va_list args; - va_start(args, s); - vsprintf(str, s, args); - va_end(args); - fprintf(tracer.fp, "%s\r\n", str); -} - -void Tracer::trace_cpuop() { - if(enabled() == false) { return; } - if(cpuop_enabled() == false) { return; } - if(cpu.in_opcode() == true) { return; } - - if(cpuopmask_enabled() == true) { - uint addr = cpu.regs.pc.d; - if(settings.cpuopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) { return; } - settings.cpuopmasktbl[addr >> 3] |= 0x80 >> (addr & 7); - } - -char t[1024]; - cpu.disassemble_opcode(t); - fprintf(fp, "%s\r\n", t); -} - -void Tracer::trace_smpop() { - if(enabled() == false) { return; } - if(smpop_enabled() == false) { return; } - if(smp.in_opcode() == true) { return; } - - if(smpopmask_enabled() == true) { - uint addr = smp.regs.pc; - if(settings.smpopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) { return; } - settings.smpopmasktbl[addr >> 3] |= 0x80 >> (addr & 7); - } - -char t[1024]; - smp.disassemble_opcode(t); - fprintf(fp, "%s\r\n", t); -} - -void Tracer::enable(bool en) { - if(en == true && enabled() == false) { - fp = fopen(config::filepath("trace.log", config::path.exportdata), "wb"); - } else if(en == false && enabled() == true) { - fclose(fp); - fp = 0; - } - - settings.enabled = en; -} - -void Tracer::cpuopmask_enable(bool en) { - if(en == true && cpuopmask_enabled() == false) { - settings.cpuopmasktbl = new(zeromemory) uint8_t[0x200000]; - } else if(en == false && cpuopmask_enabled() == true) { - delete[] settings.cpuopmasktbl; - } - - settings.cpuopmask = en; -} - -void Tracer::smpopmask_enable(bool en) { - if(en == true && smpopmask_enabled() == false) { - settings.smpopmasktbl = new(zeromemory) uint8_t[0x2000]; - } else if(en == false && smpopmask_enabled() == true) { - delete[] settings.smpopmasktbl; - } - - settings.smpopmask = en; -} - -Tracer::Tracer() { - fp = 0; - - settings.cpuop = false; - settings.cpuopmask = false; - settings.cpuopmasktbl = 0; - - settings.smpop = false; - settings.smpopmask = false; - settings.smpopmasktbl = 0; -} - -Tracer::~Tracer() { -} +Tracer tracer; + +void tprintf(const char *s, ...) { + if(tracer.enabled() == false) return; + + char str[4096]; + va_list args; + va_start(args, s); + vsprintf(str, s, args); + va_end(args); + fprintf(tracer.fp, "%s\r\n", str); +} + +void Tracer::trace_cpuop() { + if(enabled() == false) return; + if(cpuop_enabled() == false) return; + if(cpu.in_opcode() == true) return; + + if(cpuopmask_enabled() == true) { + unsigned addr = cpu.regs.pc.d; + if(settings.cpuopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return; + settings.cpuopmasktbl[addr >> 3] |= 0x80 >> (addr & 7); + } + + char t[1024]; + cpu.disassemble_opcode(t); + fprintf(fp, "%s\r\n", t); +} + +void Tracer::trace_smpop() { + if(enabled() == false) return; + if(smpop_enabled() == false) return; + if(smp.in_opcode() == true) return; + + if(smpopmask_enabled() == true) { + unsigned addr = smp.regs.pc; + if(settings.smpopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return; + settings.smpopmasktbl[addr >> 3] |= 0x80 >> (addr & 7); + } + + char t[1024]; + smp.disassemble_opcode(t); + fprintf(fp, "%s\r\n", t); +} + +void Tracer::enable(bool en) { + if(en == true && enabled() == false) { + fp = fopen(Cartridge::filepath("trace.log", snes.config.path.exportdata), "wb"); + } else if(en == false && enabled() == true) { + fclose(fp); + fp = 0; + } + + settings.enabled = en; +} + +void Tracer::cpuopmask_enable(bool en) { + if(en == true && cpuopmask_enabled() == false) { + settings.cpuopmasktbl = new(zeromemory) uint8_t[0x200000]; + } else if(en == false && cpuopmask_enabled() == true) { + delete[] settings.cpuopmasktbl; + } + + settings.cpuopmask = en; +} + +void Tracer::smpopmask_enable(bool en) { + if(en == true && smpopmask_enabled() == false) { + settings.smpopmasktbl = new(zeromemory) uint8_t[0x2000]; + } else if(en == false && smpopmask_enabled() == true) { + delete[] settings.smpopmasktbl; + } + + settings.smpopmask = en; +} + +Tracer::Tracer() { + fp = 0; + + settings.cpuop = false; + settings.cpuopmask = false; + settings.cpuopmasktbl = 0; + + settings.smpop = false; + settings.smpopmask = false; + settings.smpopmasktbl = 0; +} + +Tracer::~Tracer() { +} #endif //ifdef SNES_CPP diff --git a/src/ui/base/main.cpp b/src/ui/base/main.cpp index 9986733b..0f58b0e2 100644 --- a/src/ui/base/main.cpp +++ b/src/ui/base/main.cpp @@ -2,7 +2,7 @@ bool MainWindow::input_ready() { //allow input if main window has focus if(focused() == true) return true; //allow input if config set to never block input - if(config::input.capture_mode == 0) return true; + if(config.input.capture_mode == 0) return true; //block input return false; } @@ -35,12 +35,12 @@ uintptr_t MainWindow::event(event_t e) { if(e.widget == &menu_system_controller_port2_justifier) { event::update_controller_port2(SNES::Input::DeviceJustifier); } if(e.widget == &menu_system_controller_port2_justifiers) { event::update_controller_port2(SNES::Input::DeviceJustifiers); } - if(e.widget == &menu_system_expansion_port_none) { config::snes.expansion_port = SNES::ExpansionNone; } - if(e.widget == &menu_system_expansion_port_bsx) { config::snes.expansion_port = SNES::ExpansionBSX; } + if(e.widget == &menu_system_expansion_port_none) { snes.config.expansion_port = SNES::ExpansionNone; } + if(e.widget == &menu_system_expansion_port_bsx) { snes.config.expansion_port = SNES::ExpansionBSX; } - if(e.widget == &menu_system_region_auto) { config::snes.region = SNES::Autodetect; } - if(e.widget == &menu_system_region_ntsc) { config::snes.region = SNES::NTSC; } - if(e.widget == &menu_system_region_pal) { config::snes.region = SNES::PAL; } + if(e.widget == &menu_system_region_auto) { snes.config.region = SNES::Autodetect; } + if(e.widget == &menu_system_region_ntsc) { snes.config.region = SNES::NTSC; } + if(e.widget == &menu_system_region_pal) { snes.config.region = SNES::PAL; } if(e.widget == &menu_system_exit) { event::quit(); } @@ -80,7 +80,7 @@ uintptr_t MainWindow::event(event_t e) { if(e.widget == &menu_settings_videoframeskip_9) { event::update_frameskip(9); } if(e.widget == &menu_settings_mute) { - config::audio.mute = menu_settings_mute.checked(); + config.audio.mute = menu_settings_mute.checked(); } if(e.widget == &menu_settings_emuspeed_slowest) { event::update_emulation_speed(0); } @@ -90,13 +90,13 @@ uintptr_t MainWindow::event(event_t e) { if(e.widget == &menu_settings_emuspeed_fastest) { event::update_emulation_speed(4); } if(e.widget == &menu_settings_emuspeed_videosync) { - config::video.synchronize = menu_settings_emuspeed_videosync.checked(); - video.set(Video::Synchronize, config::video.synchronize); + config.video.synchronize = menu_settings_emuspeed_videosync.checked(); + video.set(Video::Synchronize, config.video.synchronize); } if(e.widget == &menu_settings_emuspeed_audiosync) { - config::audio.synchronize = menu_settings_emuspeed_audiosync.checked(); - audio.set(Audio::Synchronize, config::audio.synchronize); + config.audio.synchronize = menu_settings_emuspeed_audiosync.checked(); + audio.set(Audio::Synchronize, config.audio.synchronize); } if(e.widget == &menu_settings_config) { window_settings.show(); } @@ -197,7 +197,7 @@ void MainWindow::setup() { menu_system_region.attach(menu_system_region_pal.create (group, translate["{{region}}PAL"])); group.reset(); - if(config::advanced.enable == true) { + if(config.misc.show_advanced_options == true) { menu_system.attach(menu_system_sep3); menu_system.attach(menu_system_expansion_port); menu_system.attach(menu_system_region); @@ -414,14 +414,14 @@ void MainWindow::setup() { void MainWindow::sync() { event::load_video_settings(); - switch(config::snes.controller_port1) { default: + switch(snes.config.controller_port1) { default: case SNES::Input::DeviceNone: menu_system_controller_port1_none.check(); break; case SNES::Input::DeviceJoypad: menu_system_controller_port1_joypad.check(); break; case SNES::Input::DeviceMultitap: menu_system_controller_port1_multitap.check(); break; case SNES::Input::DeviceMouse: menu_system_controller_port1_mouse.check(); break; } - switch(config::snes.controller_port2) { default: + switch(snes.config.controller_port2) { default: case SNES::Input::DeviceNone: menu_system_controller_port2_none.check(); break; case SNES::Input::DeviceJoypad: menu_system_controller_port2_joypad.check(); break; case SNES::Input::DeviceMultitap: menu_system_controller_port2_multitap.check(); break; @@ -431,12 +431,12 @@ void MainWindow::sync() { case SNES::Input::DeviceJustifiers: menu_system_controller_port2_justifiers.check(); break; } - switch(config::snes.expansion_port) { default: + switch(snes.config.expansion_port) { default: case SNES::ExpansionNone: menu_system_expansion_port_none.check(); break; case SNES::ExpansionBSX: menu_system_expansion_port_bsx.check(); break; } - switch(config::snes.region) { default: + switch(snes.config.region) { default: case SNES::Autodetect: menu_system_region_auto.check(); break; case SNES::NTSC: menu_system_region_ntsc.check(); break; case SNES::PAL: menu_system_region_pal.check(); break; @@ -471,9 +471,9 @@ void MainWindow::sync() { case 4: menu_settings_videofilter_swntsc.check(); break; } - menu_settings_mute.check(config::audio.mute); + menu_settings_mute.check(config.audio.mute); - switch(config::video.frameskip) { + switch(config.video.frameskip) { case 0: menu_settings_videoframeskip_0.check(); break; case 1: menu_settings_videoframeskip_1.check(); break; case 2: menu_settings_videoframeskip_2.check(); break; @@ -486,7 +486,7 @@ void MainWindow::sync() { case 9: menu_settings_videoframeskip_9.check(); break; } - switch(config::system.emulation_speed) { + switch(config.system.emulation_speed) { case 0: menu_settings_emuspeed_slowest.check(); break; case 1: menu_settings_emuspeed_slow.check(); break; case 2: menu_settings_emuspeed_normal.check(); break; @@ -494,6 +494,6 @@ void MainWindow::sync() { case 4: menu_settings_emuspeed_fastest.check(); break; } - menu_settings_emuspeed_videosync.check(config::video.synchronize); - menu_settings_emuspeed_audiosync.check(config::audio.synchronize); + menu_settings_emuspeed_videosync.check(config.video.synchronize); + menu_settings_emuspeed_audiosync.check(config.audio.synchronize); } diff --git a/src/ui/base/textview.cpp b/src/ui/base/textview.cpp index 021d448d..b3b84d92 100644 --- a/src/ui/base/textview.cpp +++ b/src/ui/base/textview.cpp @@ -7,7 +7,7 @@ void TextViewWindow::setup() { create(Window::AutoCenter, 415, 450); set_icon(48, 48, (uint32_t*)resource::icon48); - text.create(Editbox::Multiline | Editbox::Readonly | Editbox::HorizontalScrollNever | Editbox::VerticalScrollAlways, 405, 410); + text.create(Editbox::Multiline | Editbox::Readonly | Editbox::HorizontalScrollNever | Editbox::VerticalScrollAuto, 405, 410); ok.create(0, 100, 25, translate["{{textview}}Ok"]); attach(text, 5, 5); diff --git a/src/ui/config.cpp b/src/ui/config.cpp index e1e0ad44..818944f1 100644 --- a/src/ui/config.cpp +++ b/src/ui/config.cpp @@ -1,310 +1,263 @@ -namespace config { +#include -char bsnes_cfg[PATH_MAX] = ""; -char locale_cfg[PATH_MAX] = ""; +class bsnes_configuration : public configuration { +public: + char bsnes_cfg[PATH_MAX], locale_cfg[PATH_MAX]; -struct System { - static string_setting video, audio, input; - static integral_setting invoke_crash_handler; - static integral_setting emulation_speed; - static integral_setting gamma_ramp, sepia, grayscale, invert, contrast, brightness, gamma; -} system; + struct System { + string video, audio, input; + bool invoke_crash_handler; + unsigned emulation_speed; + } system; -string_setting System::video(config(), "system.video", "Video hardware interface", ""); -string_setting System::audio(config(), "system.audio", "Audio hardware interface", ""); -string_setting System::input(config(), "system.input", "Input hardware interface", ""); + struct Video { + unsigned mode; + bool synchronize; -integral_setting System::invoke_crash_handler(config(), "system.invoke_crash_handler", - "Do not modify this setting!\n" - "Used to detect crashes caused by initialization of video / audio / input drivers. " - "When the emulator crashes during driver initialization, this value will be left as true. " - "When true, driver selection crash handler window will appear on next start-up.", - integral_setting::boolean, false); + struct Windowed { + bool aspect_correction; + unsigned region, multiplier, hardware_filter, software_filter; + } windowed; -integral_setting System::emulation_speed(config(), "system.emulation_speed", - "Relative speed of emulator compared to SNES hardware:\n" - "0 = 50%\n" - "1 = 75%\n" - "2 = 100%\n" - "3 = 150%\n" - "4 = 200%", - integral_setting::decimal, 2); + struct Fullscreen { + bool aspect_correction; + unsigned region, multiplier, hardware_filter, software_filter; + } fullscreen; -integral_setting System::gamma_ramp(config(), "system.colorfilter.gamma_ramp", - "Use precalculated TV-style gamma ramp", integral_setting::boolean, true); -integral_setting System::sepia(config(), "system.colorfilter.sepia", - "Convert color to sepia tone", integral_setting::boolean, false); -integral_setting System::grayscale(config(), "system.colorfilter.grayscale", - "Convert color to grayscale tone", integral_setting::boolean, false); -integral_setting System::invert(config(), "system.colorfilter.invert", - "Invert output image colors", integral_setting::boolean, false); -integral_setting System::contrast(config(), "system.colorfilter.contrast", - "Contrast", integral_setting::decimal, 0); -integral_setting System::brightness(config(), "system.colorfilter.brightness", - "Brightness", integral_setting::decimal, 0); -integral_setting System::gamma(config(), "system.colorfilter.gamma", - "Gamma", integral_setting::decimal, 100); + unsigned aspect_ntsc_x, aspect_ntsc_y, aspect_pal_x, aspect_pal_y; + unsigned frameskip; -struct Video { - static integral_setting mode; - static integral_setting synchronize; + unsigned contrast, brightness, gamma; + bool gamma_ramp, sepia, grayscale, invert; + bool ntsc_filter_merge_fields; + } video; - struct Windowed { - static integral_setting aspect_correction; - static integral_setting region, multiplier, hardware_filter, software_filter; - } windowed; - struct Fullscreen { - static integral_setting aspect_correction; - static integral_setting region, multiplier, hardware_filter, software_filter; - } fullscreen; - static integral_setting aspect_ntsc_x, aspect_ntsc_y, aspect_pal_x, aspect_pal_y; - static integral_setting frameskip; - static integral_setting start_in_fullscreen_mode; -} video; + struct Audio { + unsigned output_frequency, input_frequency; + unsigned latency; + unsigned volume, mute; + bool synchronize; + } audio; -//0 = windowed, 1 = fullscreen, 2 = exclusive (not implemented yet) -integral_setting Video::mode("video.mode", "Active video mode", integral_setting::decimal, 0); -integral_setting Video::synchronize(config(), "video.synchronize", "Synchronize to video refresh rate", integral_setting::boolean, false); + struct Input { + unsigned capture_mode; + bool allow_invalid_input; + unsigned analog_axis_resistance; -integral_setting Video::Windowed::aspect_correction(config(), "video.windowed.aspect_correction", - "Correct video aspect ratio.\n" - "Defaults assume display pixels are perfectly square." - "Formula: width = width * video.aspect__x / video.aspect__y", - integral_setting::boolean, true); -integral_setting Video::Windowed::region(config(), "video.windowed.region", - "Video output region:\n" - "0 = NTSC\n" - "1 = PAL", - integral_setting::decimal, 0); -integral_setting Video::Windowed::multiplier(config(), "video.windowed.multiplier", - "Video output size multiplier (1-5x)\n" - "1 = 1x (<= 320x240)\n" - "2 = 2x (<= 640x480)\n" - "etc.", - integral_setting::decimal, 2); -integral_setting Video::Windowed::hardware_filter(config(), "video.windowed.hardware_filter", - "Video hardware filter:\n" - "0 = Point\n" - "1 = Linear", - integral_setting::decimal, 1); -integral_setting Video::Windowed::software_filter(config(), "video.windowed.software_filter", - "Video software filter:\n" - "0 = None\n" - "1 = Scanline\n" - "2 = Scale2x\n" - "3 = HQ2x\n" - "4 = NTSC", - integral_setting::decimal, 0); + struct Joypad { + string up, down, left, right, a, b, x, y, l, r, select, start; + } joypad1, joypad2, + multitap1a, multitap1b, multitap1c, multitap1d, + multitap2a, multitap2b, multitap2c, multitap2d; -integral_setting Video::Fullscreen::aspect_correction(config(), "video.fullscreen.aspect_correction", "", integral_setting::boolean, true); -integral_setting Video::Fullscreen::region (config(), "video.fullscreen.region", "", integral_setting::decimal, 0); -integral_setting Video::Fullscreen::multiplier (config(), "video.fullscreen.multiplier", "", integral_setting::decimal, 2); -integral_setting Video::Fullscreen::hardware_filter (config(), "video.fullscreen.hardware_filter", "", integral_setting::decimal, 1); -integral_setting Video::Fullscreen::software_filter (config(), "video.fullscreen.software_filter", "", integral_setting::decimal, 0); + struct Mouse { + string x, y, l, r; + } mouse1, mouse2; -integral_setting Video::aspect_ntsc_x(config(), "video.aspect_ntsc_x", "", integral_setting::decimal, 54); -integral_setting Video::aspect_ntsc_y(config(), "video.aspect_ntsc_y", "", integral_setting::decimal, 47); -integral_setting Video::aspect_pal_x (config(), "video.aspect_pal_x", "", integral_setting::decimal, 32); -integral_setting Video::aspect_pal_y (config(), "video.aspect_pal_y", "", integral_setting::decimal, 23); + struct SuperScope { + string x, y, trigger, turbo, cursor, pause; + } superscope; -integral_setting Video::frameskip("video.frameskip", "Video frameskip", integral_setting::decimal, 0); -integral_setting Video::start_in_fullscreen_mode(config(), "video.start_in_fullscreen_mode", - "If true, bsnes will start in fullscreen mode, rather than windowed mode.", - integral_setting::boolean, false); + struct Justifier { + string x, y, trigger, start; + } justifier1, justifier2; -struct Audio { - static integral_setting output_frequency, input_frequency; - static integral_setting latency; - static integral_setting volume, mute; - static integral_setting synchronize; -} audio; -integral_setting Audio::output_frequency(config(), "audio.output_frequency", "Sound card audio output frequency", integral_setting::decimal, 48000); -integral_setting Audio::input_frequency(config(), "audio.input_frequency", "Emulator audio input frequency", integral_setting::decimal, 32000); -integral_setting Audio::latency(config(), "audio.latency", "Sound card latency (in ms)", integral_setting::decimal, 100); -integral_setting Audio::volume(config(), "audio.volume", "Audio volume (10 - 100)", integral_setting::decimal, 100); -integral_setting Audio::mute(config(), "audio.mute", "Mute audio playback", integral_setting::boolean, false); -integral_setting Audio::synchronize(config(), "audio.synchronize", "Synchronize to audio sample rate", integral_setting::boolean, true); + struct GUI { + string load, pause, reset, power, quit; + string speed_decrease, speed_increase; + string frameskip_decrease, frameskip_increase; + string toggle_fullscreen, toggle_menubar, toggle_statusbar; + } gui; -struct Input { - static integral_setting capture_mode; - static integral_setting allow_invalid_input; - static integral_setting analog_axis_resistance; + struct Debugger { + string export_memory; + string toggle_cputrace, toggle_cputracemask; + string toggle_smptrace, toggle_smptracemask; + } debugger; + } input; - struct Joypad1 { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad1; - struct Joypad2 { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad2; + struct Misc { + bool start_in_fullscreen_mode; + unsigned window_opacity; + unsigned cheat_autosort; + bool show_advanced_options; + } misc; - struct Multitap1A { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1a; - struct Multitap1B { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1b; - struct Multitap1C { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1c; - struct Multitap1D { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1d; - struct Multitap2A { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2a; - struct Multitap2B { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2b; - struct Multitap2C { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2c; - struct Multitap2D { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2d; + bsnes_configuration() { + //======== + //external + //======== - struct Mouse1 { static string_setting x, y, l, r; } mouse1; - struct Mouse2 { static string_setting x, y, l, r; } mouse2; + attach(snes.config.controller_port1 = SNES::Input::DeviceJoypad, "snes.controller_port1"); + attach(snes.config.controller_port2 = SNES::Input::DeviceJoypad, "snes.controller_port2"); + attach(snes.config.expansion_port = SNES::ExpansionBSX, "snes.expansion_port"); + attach(snes.config.region = SNES::Autodetect, "snes.region"); - struct SuperScope { static string_setting x, y, trigger, turbo, cursor, pause; } superscope; - struct Justifier1 { static string_setting x, y, trigger, start; } justifier1; - struct Justifier2 { static string_setting x, y, trigger, start; } justifier2; + attach(snes.config.file.autodetect_type = false, "file.autodetect_type", "Detect filetype by header, rather than file extension"); + attach(snes.config.file.bypass_patch_crc32 = false, "file.bypass_patch_crc32", "Apply UPS patches even when checksum match fails"); - struct GUI { - static string_setting load; - static string_setting pause; - static string_setting reset; - static string_setting power; - static string_setting quit; - static string_setting speed_decrease; - static string_setting speed_increase; - static string_setting frameskip_decrease; - static string_setting frameskip_increase; - static string_setting toggle_fullscreen; - static string_setting toggle_menubar; - static string_setting toggle_statusbar; - } gui; + attach(snes.config.path.rom = "", "path.rom"); + attach(snes.config.path.save = "", "path.save"); + attach(snes.config.path.patch = "", "path.patch"); + attach(snes.config.path.cheat = "", "path.cheat"); + attach(snes.config.path.exportdata = "", "path.exportdata"); + attach(snes.config.path.bsx = "", "path.bsx"); + attach(snes.config.path.st = "", "path.st"); - struct Debugger { - static string_setting export_memory; - static string_setting toggle_cputrace; - static string_setting toggle_cputracemask; - static string_setting toggle_smptrace; - static string_setting toggle_smptracemask; - } debugger; -} input; + attach(snes.config.cpu.ntsc_clock_rate = 21477272, "cpu.ntsc_clock_rate"); + attach(snes.config.cpu.pal_clock_rate = 21281370, "cpu.pal_clock_rate"); + attach(snes.config.cpu.alu_mul_delay = 2, "cpu.alu_mul_delay"); + attach(snes.config.cpu.alu_div_delay = 2, "cpu.alu_div_delay"); + attach(snes.config.cpu.wram_init_value = 0x55, "cpu.wram_init_value"); -integral_setting Input::capture_mode(config(), "input.capture_mode", - "Capture method for input to main emulation window.\n" - "When emulation window does not have focus:\n" - "0 = Allow input\n" - "1 = Ignore input\n" - "2 = Pause emulator", - integral_setting::decimal, 2); + attach(snes.config.smp.ntsc_clock_rate = 32041 * 768, "smp.ntsc_clock_rate"); + attach(snes.config.smp.pal_clock_rate = 32041 * 768, "smp.pal_clock_rate"); -integral_setting Input::allow_invalid_input(config(), "input.allow_invalid_input", - "Allow up+down and left+right combinations (not recommended.)\n" - "This is not possible on an actual SNES controller, due to its design. " - "Enabling this option can trigger bugs in certain games.", - integral_setting::boolean, false); + //======== + //internal + //======== -integral_setting Input::analog_axis_resistance(config(), "input.analog_axis_resistance", - "Resistance required to activate analog stick in any given direction.\n" - "This value ranges from 1 (1%, virtually no resistance) to 99 (99%, near full resistance.) " - "For instance, a value of 50 means that to register 'left', the analog stick must be moved " - "50% between the center and the left.\n" - "Less resistance allows for more fluid movement; whereas more resistance helps to prevent " - "accidental movements, eg attempting to press up whilst bumping the stick slightly left.", - integral_setting::decimal, 50); + *bsnes_cfg = 0; + *locale_cfg = 0; -string_setting Input::Joypad1::up (config(), "input.joypad1.up", "", "up"); -string_setting Input::Joypad1::down (config(), "input.joypad1.down", "", "down"); -string_setting Input::Joypad1::left (config(), "input.joypad1.left", "", "left"); -string_setting Input::Joypad1::right (config(), "input.joypad1.right", "", "right"); -string_setting Input::Joypad1::a (config(), "input.joypad1.a", "", "x"); -string_setting Input::Joypad1::b (config(), "input.joypad1.b", "", "z"); -string_setting Input::Joypad1::x (config(), "input.joypad1.x", "", "s"); -string_setting Input::Joypad1::y (config(), "input.joypad1.y", "", "a"); -string_setting Input::Joypad1::l (config(), "input.joypad1.l", "", "d"); -string_setting Input::Joypad1::r (config(), "input.joypad1.r", "", "c"); -string_setting Input::Joypad1::select(config(), "input.joypad1.select", "", "rshift"); -string_setting Input::Joypad1::start (config(), "input.joypad1.start", "", "return"); + attach(system.video = "", "system.video", "Video hardware interface"); + attach(system.audio = "", "system.audio", "Audio hardware interface"); + attach(system.input = "", "system.input", "Input hardware interface"); -string_setting Input::Joypad2::up (config(), "input.joypad2.up", "", "t"); -string_setting Input::Joypad2::down (config(), "input.joypad2.down", "", "g"); -string_setting Input::Joypad2::left (config(), "input.joypad2.left", "", "f"); -string_setting Input::Joypad2::right (config(), "input.joypad2.right", "", "h"); -string_setting Input::Joypad2::a (config(), "input.joypad2.a", "", "k"); -string_setting Input::Joypad2::b (config(), "input.joypad2.b", "", "j"); -string_setting Input::Joypad2::x (config(), "input.joypad2.x", "", "i"); -string_setting Input::Joypad2::y (config(), "input.joypad2.y", "", "u"); -string_setting Input::Joypad2::l (config(), "input.joypad2.l", "", "o"); -string_setting Input::Joypad2::r (config(), "input.joypad2.r", "", "l"); -string_setting Input::Joypad2::select(config(), "input.joypad2.select", "", "lbracket"); -string_setting Input::Joypad2::start (config(), "input.joypad2.start", "", "rbracket"); + attach(system.invoke_crash_handler = false, "system.invoke_crash_handler", "Do not modify! Used to detect crashes caused by driver initialization"); + attach(system.emulation_speed = 2, "system.emulation_speed", "Relative speed of emulator compared to hardware"); -#define DeclMultitap(uname, lname) \ -string_setting Input::uname::up (config(), "input.multitap" lname ".up", "", "none"); \ -string_setting Input::uname::down (config(), "input.multitap" lname ".down", "", "none"); \ -string_setting Input::uname::left (config(), "input.multitap" lname ".left", "", "none"); \ -string_setting Input::uname::right (config(), "input.multitap" lname ".right", "", "none"); \ -string_setting Input::uname::a (config(), "input.multitap" lname ".a", "", "none"); \ -string_setting Input::uname::b (config(), "input.multitap" lname ".b", "", "none"); \ -string_setting Input::uname::x (config(), "input.multitap" lname ".x", "", "none"); \ -string_setting Input::uname::y (config(), "input.multitap" lname ".y", "", "none"); \ -string_setting Input::uname::l (config(), "input.multitap" lname ".l", "", "none"); \ -string_setting Input::uname::r (config(), "input.multitap" lname ".r", "", "none"); \ -string_setting Input::uname::select(config(), "input.multitap" lname ".select", "", "none"); \ -string_setting Input::uname::start (config(), "input.multitap" lname ".start", "", "none"); + video.mode = 0; + attach(video.synchronize = false, "video.synchronize", "Synchronize to video refresh rate"); -DeclMultitap(Multitap1A, "1a") -DeclMultitap(Multitap1B, "1b") -DeclMultitap(Multitap1C, "1c") -DeclMultitap(Multitap1D, "1d") -DeclMultitap(Multitap2A, "2a") -DeclMultitap(Multitap2B, "2b") -DeclMultitap(Multitap2C, "2c") -DeclMultitap(Multitap2D, "2d") + attach(video.windowed.aspect_correction = true, "video.windowed.aspect_correction"); + attach(video.windowed.region = 0, "video.windowed.region"); + attach(video.windowed.multiplier = 2, "video.windowed.multiplier"); + attach(video.windowed.hardware_filter = 1, "video.windowed.hardware_filter"); + attach(video.windowed.software_filter = 0, "video.windowed.software_filter"); -#undef DeclMultitap + attach(video.fullscreen.aspect_correction = true, "video.fullscreen.aspect_correction"); + attach(video.fullscreen.region = 0, "video.fullscreen.region"); + attach(video.fullscreen.multiplier = 2, "video.fullscreen.multiplier"); + attach(video.fullscreen.hardware_filter = 1, "video.fullscreen.hardware_filter"); + attach(video.fullscreen.software_filter = 0, "video.fullscreen.software_filter"); -string_setting Input::Mouse1::x(config(), "input.mouse1.x", "", "mouse.x"); -string_setting Input::Mouse1::y(config(), "input.mouse1.y", "", "mouse.y"); -string_setting Input::Mouse1::l(config(), "input.mouse1.l", "", "mouse.button00"); -string_setting Input::Mouse1::r(config(), "input.mouse1.r", "", "mouse.button02"); + attach(video.aspect_ntsc_x = 54, "video.aspect_ntsc_x", "NTSC TV aspect correction ratio"); + attach(video.aspect_ntsc_y = 47, "video.aspect_ntsc_y"); + attach(video.aspect_pal_x = 32, "video.aspect_pal_x", "PAL TV aspect correction ratio"); + attach(video.aspect_pal_y = 23, "video.aspect_pal_y"); -string_setting Input::Mouse2::x(config(), "input.mouse2.x", "", "mouse.x"); -string_setting Input::Mouse2::y(config(), "input.mouse2.y", "", "mouse.y"); -string_setting Input::Mouse2::l(config(), "input.mouse2.l", "", "mouse.button00"); -string_setting Input::Mouse2::r(config(), "input.mouse2.r", "", "mouse.button02"); + video.frameskip = 0; + attach(video.contrast = 0, "video.contrast"); + attach(video.brightness = 0, "video.brightness"); + attach(video.gamma = 100, "video.gamma"); -string_setting Input::SuperScope::x (config(), "input.superscope.x", "", "mouse.x"); -string_setting Input::SuperScope::y (config(), "input.superscope.y", "", "mouse.y"); -string_setting Input::SuperScope::trigger(config(), "input.superscope.trigger", "", "mouse.button00"); -string_setting Input::SuperScope::cursor (config(), "input.superscope.cursor", "", "mouse.button02"); -string_setting Input::SuperScope::turbo (config(), "input.superscope.turbo", "", "t"); -string_setting Input::SuperScope::pause (config(), "input.superscope.pause", "", "p"); + attach(video.gamma_ramp = true, "video.gamma_ramp"); + attach(video.sepia = false, "video.sepia"); + attach(video.grayscale = false, "video.grayscale"); + attach(video.invert = false, "video.invert"); + attach(video.ntsc_filter_merge_fields = true, "video.ntsc_filter_merge_fields"); -string_setting Input::Justifier1::x (config(), "input.justifier1.x", "", "mouse.x"); -string_setting Input::Justifier1::y (config(), "input.justifier1.y", "", "mouse.y"); -string_setting Input::Justifier1::trigger(config(), "input.justifier1.trigger", "", "mouse.button00"); -string_setting Input::Justifier1::start (config(), "input.justifier1.start", "", "mouse.button02"); + attach(audio.output_frequency = 48000, "audio.output_frequency"); + attach(audio.input_frequency = 32000, "audio.input_frequency"); + attach(audio.latency = 100, "audio.latency"); + attach(audio.volume = 100, "audio.volume"); + attach(audio.mute = false, "audio.mute"); + attach(audio.synchronize = true, "audio.synchronize"); -string_setting Input::Justifier2::x (config(), "input.justifier2.x", "", "none"); -string_setting Input::Justifier2::y (config(), "input.justifier2.y", "", "none"); -string_setting Input::Justifier2::trigger(config(), "input.justifier2.trigger", "", "none"); -string_setting Input::Justifier2::start (config(), "input.justifier2.start", "", "none"); + attach(input.capture_mode = 2, "input.capture_mode", "Capture method: 0 = allow, 1 = ignore, 2 = pause"); + attach(input.allow_invalid_input = false, "input.allow_invalid_input", "Allow D-pad up+down and left+right combinations (not recommended)"); + attach(input.analog_axis_resistance = 50, "input.analog_axis_resistance", "Analog stick resistance percentage; lower = less, higher = more"); -string_setting Input::GUI::load (config(), "input.gui.load", "", "none"); -string_setting Input::GUI::pause (config(), "input.gui.pause", "", "f12"); -string_setting Input::GUI::reset (config(), "input.gui.reset", "", "none"); -string_setting Input::GUI::power (config(), "input.gui.power", "", "none"); -string_setting Input::GUI::quit (config(), "input.gui.quit", "", "none"); -string_setting Input::GUI::speed_decrease (config(), "input.gui.speed_decrease", "", "divide"); -string_setting Input::GUI::speed_increase (config(), "input.gui.speed_increase", "", "multiply"); -string_setting Input::GUI::frameskip_decrease(config(), "input.gui.frameskip_decrease", "", "subtract"); -string_setting Input::GUI::frameskip_increase(config(), "input.gui.frameskip_increase", "", "add"); -string_setting Input::GUI::toggle_fullscreen (config(), "input.gui.toggle_fullscreen", "", "f11"); -string_setting Input::GUI::toggle_menubar (config(), "input.gui.toggle_menubar", "", "escape"); -string_setting Input::GUI::toggle_statusbar (config(), "input.gui.toggle_statusbar", "", "escape"); + attach(input.joypad1.up = "up", "input.joypad1.up"); + attach(input.joypad1.down = "down", "input.joypad1.down"); + attach(input.joypad1.left = "left", "input.joypad1.left"); + attach(input.joypad1.right = "right", "input.joypad1.right"); + attach(input.joypad1.a = "x", "input.joypad1.a"); + attach(input.joypad1.b = "z", "input.joypad1.b"); + attach(input.joypad1.x = "s", "input.joypad1.x"); + attach(input.joypad1.y = "a", "input.joypad1.y"); + attach(input.joypad1.l = "d", "input.joypad1.l"); + attach(input.joypad1.r = "c", "input.joypad1.r"); + attach(input.joypad1.select = "rshift", "input.joypad1.select"); + attach(input.joypad1.start = "return", "input.joypad1.start"); -string_setting Input::Debugger::export_memory (config(), "input.debugger.export_memory", "", "none"); -string_setting Input::Debugger::toggle_cputrace (config(), "input.debugger.toggle_cputrace", "", "none"); -string_setting Input::Debugger::toggle_cputracemask(config(), "input.debugger.toggle_cputracemask", "", "none"); -string_setting Input::Debugger::toggle_smptrace (config(), "input.debugger.toggle_smptrace", "", "none"); -string_setting Input::Debugger::toggle_smptracemask(config(), "input.debugger.toggle_smptracemask", "", "none"); + attach_joypad(input.joypad2, "input.joypad2"); + attach_joypad(input.multitap1a, "input.multitap1a"); + attach_joypad(input.multitap1b, "input.multitap1b"); + attach_joypad(input.multitap1c, "input.multitap1c"); + attach_joypad(input.multitap1d, "input.multitap1d"); + attach_joypad(input.multitap2a, "input.multitap2a"); + attach_joypad(input.multitap2b, "input.multitap2b"); + attach_joypad(input.multitap2c, "input.multitap2c"); + attach_joypad(input.multitap2d, "input.multitap2d"); -struct Misc { - static integral_setting cheat_autosort; - static integral_setting opacity; -} misc; + attach(input.mouse1.x = "mouse1.x", "input.mouse1.x"); + attach(input.mouse1.y = "mouse1.y", "input.mouse1.y"); + attach(input.mouse1.l = "mouse1.button00", "input.mouse1.l"); + attach(input.mouse1.r = "mouse1.button02", "input.mouse1.r"); -integral_setting Misc::cheat_autosort(config(), "misc.cheat_autosort", "Keep cheat code list sorted by description", integral_setting::boolean, false); -integral_setting Misc::opacity(config(), "misc.opacity", "Opacity of user interface windows", integral_setting::decimal, 100); + attach(input.mouse2.x = "mouse2.x", "input.mouse2.x"); + attach(input.mouse2.y = "mouse2.y", "input.mouse2.y"); + attach(input.mouse2.l = "mouse2.button00", "input.mouse2.l"); + attach(input.mouse2.r = "mouse2.button02", "input.mouse2.r"); -struct Advanced { - static integral_setting enable; -} advanced; + attach(input.superscope.x = "mouse.x", "input.superscope.x"); + attach(input.superscope.y = "mouse.y", "input.superscope.y"); + attach(input.superscope.trigger = "mouse.button00", "input.superscope.trigger"); + attach(input.superscope.cursor = "mouse.button02", "input.superscope.cursor"); + attach(input.superscope.turbo = "t", "input.superscope.turbo"); + attach(input.superscope.pause = "p", "input.superscope.pause"); -integral_setting Advanced::enable(config(), "advanced.enable", "Enable advanced, developer-oriented UI options", integral_setting::boolean, false); + attach(input.justifier1.x = "mouse.x", "input.justifier1.x"); + attach(input.justifier1.y = "mouse.y", "input.justifier1.y"); + attach(input.justifier1.trigger = "mouse.button00", "input.justifier1.trigger"); + attach(input.justifier1.start = "mouse.button02", "input.justifier1.start"); -} //namespace config + attach(input.justifier2.x = "none", "input.justifier2.x"); + attach(input.justifier2.y = "none", "input.justifier2.y"); + attach(input.justifier2.trigger = "none", "input.justifier2.trigger"); + attach(input.justifier2.start = "none", "input.justifier2.start"); + + attach(input.gui.load = "none", "input.gui.load"); + attach(input.gui.pause = "f12", "input.gui.pause"); + attach(input.gui.reset = "none", "input.gui.reset"); + attach(input.gui.power = "none", "input.gui.power"); + attach(input.gui.quit = "none", "input.gui.quit"); + + attach(input.gui.speed_decrease = "divide", "input.gui.speed_decrease"); + attach(input.gui.speed_increase = "multiply", "input.gui.speed_increase"); + attach(input.gui.frameskip_decrease = "subtract", "input.gui.frameskip_decrease"); + attach(input.gui.frameskip_increase = "add", "input.gui.frameskip_increase"); + + attach(input.gui.toggle_fullscreen = "f11", "input.gui.toggle_fullscreen"); + attach(input.gui.toggle_menubar = "escape", "input.gui.toggle_menubar"); + attach(input.gui.toggle_statusbar = "escape", "input.gui.toggle_statusbar"); + + attach(input.debugger.export_memory = "none", "input.debugger.export_memory"); + attach(input.debugger.toggle_cputrace = "none", "input.debugger.toggle_cputrace"); + attach(input.debugger.toggle_cputracemask = "none", "input.debugger.toggle_cputracemask"); + attach(input.debugger.toggle_smptrace = "none", "input.debugger.toggle_smptrace"); + attach(input.debugger.toggle_smptracemask = "none", "input.debugger.toggle_smptracemask"); + + attach(misc.start_in_fullscreen_mode = false, "misc.start_in_fullscreen_mode"); + attach(misc.window_opacity = 100, "misc.window_opacity", "Translucency percentage of helper windows (50%-100%)"); + attach(misc.cheat_autosort = false, "misc.cheat_autosort"); + attach(misc.show_advanced_options = false, "misc.show_advanced_options", "Enable developer-oriented GUI options"); + } + + void attach_joypad(Input::Joypad &joypad, const char *name) { + attach(joypad.up = "none", string() << name << ".up"); + attach(joypad.down = "none", string() << name << ".down"); + attach(joypad.left = "none", string() << name << ".left"); + attach(joypad.right = "none", string() << name << ".right"); + attach(joypad.a = "none", string() << name << ".a"); + attach(joypad.b = "none", string() << name << ".b"); + attach(joypad.x = "none", string() << name << ".x"); + attach(joypad.y = "none", string() << name << ".y"); + attach(joypad.l = "none", string() << name << ".l"); + attach(joypad.r = "none", string() << name << ".r"); + attach(joypad.select = "none", string() << name << ".select"); + attach(joypad.start = "none", string() << name << ".start"); + } +} config; diff --git a/src/ui/event/debugger.cpp b/src/ui/event/debugger.cpp index 5066abef..ee76b868 100644 --- a/src/ui/event/debugger.cpp +++ b/src/ui/event/debugger.cpp @@ -1,23 +1,23 @@ void export_memory() { file fp; - fp.open(config::filepath("wram.bin", config::path.exportdata), file::mode_write); + fp.open(Cartridge::filepath("wram.bin", snes.config.path.exportdata), file::mode_write); for(unsigned i = 0; i < memory::wram.size(); i++) fp.write(memory::wram[i]); fp.close(); - fp.open(config::filepath("apuram.bin", config::path.exportdata), file::mode_write); + fp.open(Cartridge::filepath("apuram.bin", snes.config.path.exportdata), file::mode_write); for(unsigned i = 0; i < memory::apuram.size(); i++) fp.write(memory::apuram[i]); fp.close(); - fp.open(config::filepath("vram.bin", config::path.exportdata), file::mode_write); + fp.open(Cartridge::filepath("vram.bin", snes.config.path.exportdata), file::mode_write); for(unsigned i = 0; i < memory::vram.size(); i++) fp.write(memory::vram[i]); fp.close(); - fp.open(config::filepath("oam.bin", config::path.exportdata), file::mode_write); + fp.open(Cartridge::filepath("oam.bin", snes.config.path.exportdata), file::mode_write); for(unsigned i = 0; i < memory::oam.size(); i++) fp.write(memory::oam[i]); fp.close(); - fp.open(config::filepath("cgram.bin", config::path.exportdata), file::mode_write); + fp.open(Cartridge::filepath("cgram.bin", snes.config.path.exportdata), file::mode_write); for(unsigned i = 0; i < memory::cgram.size(); i++) fp.write(memory::cgram[i]); fp.close(); diff --git a/src/ui/event/event.cpp b/src/ui/event/event.cpp index e3f60663..15279460 100644 --- a/src/ui/event/event.cpp +++ b/src/ui/event/event.cpp @@ -11,7 +11,7 @@ void input_event(uint16_t code) { if(window_main.focused()) { if(code == inputuigeneral.load.code) load_cart(); if(code == inputuigeneral.pause.code) { - app.pause = !app.pause; //toggle pause state + app.pause = !app.pause; //toggle pause state if(app.pause) { audio.clear(); if(cartridge.loaded()) status.update(); @@ -21,16 +21,16 @@ void input_event(uint16_t code) { if(code == inputuigeneral.power.code) modify_system_state(PowerCycle); if(code == inputuigeneral.quit.code) quit(); if(code == inputuigeneral.speed_decrease.code) { - update_emulation_speed(config::system.emulation_speed - 1); + update_emulation_speed(config.system.emulation_speed - 1); } if(code == inputuigeneral.speed_increase.code) { - update_emulation_speed(config::system.emulation_speed + 1); + update_emulation_speed(config.system.emulation_speed + 1); } if(code == inputuigeneral.frameskip_decrease.code) { - update_frameskip(config::video.frameskip - 1); + update_frameskip(config.video.frameskip - 1); } if(code == inputuigeneral.frameskip_increase.code) { - update_frameskip(config::video.frameskip + 1); + update_frameskip(config.video.frameskip + 1); } if(code == inputuigeneral.toggle_fullscreen.code) toggle_fullscreen(); if(code == inputuigeneral.toggle_menubar.code) toggle_menubar(); @@ -46,11 +46,11 @@ void input_event(uint16_t code) { void acquire() { if(cartridge.loaded() == true) { - if(config::snes.controller_port1 == SNES::Input::DeviceMouse - || config::snes.controller_port2 == SNES::Input::DeviceMouse - || config::snes.controller_port2 == SNES::Input::DeviceSuperScope - || config::snes.controller_port2 == SNES::Input::DeviceJustifier - || config::snes.controller_port2 == SNES::Input::DeviceJustifiers + if(snes.config.controller_port1 == SNES::Input::DeviceMouse + || snes.config.controller_port2 == SNES::Input::DeviceMouse + || snes.config.controller_port2 == SNES::Input::DeviceSuperScope + || snes.config.controller_port2 == SNES::Input::DeviceJustifier + || snes.config.controller_port2 == SNES::Input::DeviceJustifiers ) input.acquire(); } } @@ -60,79 +60,79 @@ void unacquire() { } void load_video_settings() { - video_settings.mode = config::video.mode; - video_settings.synchronize = config::video.synchronize; + video_settings.mode = config.video.mode; + video_settings.synchronize = config.video.synchronize; switch(video_settings.mode) { default: case 0: { //windowed - video_settings.aspect_correction = config::video.windowed.aspect_correction; - video_settings.region = config::video.windowed.region; - video_settings.multiplier = config::video.windowed.multiplier; - video_settings.hardware_filter = config::video.windowed.hardware_filter; - video_settings.software_filter = config::video.windowed.software_filter; + video_settings.aspect_correction = config.video.windowed.aspect_correction; + video_settings.region = config.video.windowed.region; + video_settings.multiplier = config.video.windowed.multiplier; + video_settings.hardware_filter = config.video.windowed.hardware_filter; + video_settings.software_filter = config.video.windowed.software_filter; } break; case 1: { //fullscreen - video_settings.aspect_correction = config::video.fullscreen.aspect_correction; - video_settings.region = config::video.fullscreen.region; - video_settings.multiplier = config::video.fullscreen.multiplier; - video_settings.hardware_filter = config::video.fullscreen.hardware_filter; - video_settings.software_filter = config::video.fullscreen.software_filter; + video_settings.aspect_correction = config.video.fullscreen.aspect_correction; + video_settings.region = config.video.fullscreen.region; + video_settings.multiplier = config.video.fullscreen.multiplier; + video_settings.hardware_filter = config.video.fullscreen.hardware_filter; + video_settings.software_filter = config.video.fullscreen.software_filter; } break; } } void update_aspect_correction(bool aspect_correction) { - switch(config::video.mode) { default: - case 0: config::video.windowed.aspect_correction = aspect_correction; break; - case 1: config::video.fullscreen.aspect_correction = aspect_correction; break; + switch(config.video.mode) { default: + case 0: config.video.windowed.aspect_correction = aspect_correction; break; + case 1: config.video.fullscreen.aspect_correction = aspect_correction; break; } update_video_settings(); } -void update_multiplier(uint multiplier) { - switch(config::video.mode) { default: - case 0: config::video.windowed.multiplier = multiplier; break; - case 1: config::video.fullscreen.multiplier = multiplier; break; +void update_multiplier(unsigned multiplier) { + switch(config.video.mode) { default: + case 0: config.video.windowed.multiplier = multiplier; break; + case 1: config.video.fullscreen.multiplier = multiplier; break; } update_video_settings(); } -void update_region(uint region) { - switch(config::video.mode) { default: - case 0: config::video.windowed.region = region; break; - case 1: config::video.fullscreen.region = region; break; +void update_region(unsigned region) { + switch(config.video.mode) { default: + case 0: config.video.windowed.region = region; break; + case 1: config.video.fullscreen.region = region; break; } update_video_settings(); } -void update_hardware_filter(uint hardware_filter) { - switch(config::video.mode) { default: - case 0: config::video.windowed.hardware_filter = hardware_filter; break; - case 1: config::video.fullscreen.hardware_filter = hardware_filter; break; +void update_hardware_filter(unsigned hardware_filter) { + switch(config.video.mode) { default: + case 0: config.video.windowed.hardware_filter = hardware_filter; break; + case 1: config.video.fullscreen.hardware_filter = hardware_filter; break; } update_video_settings(); } -void update_software_filter(uint software_filter) { - switch(config::video.mode) { default: - case 0: config::video.windowed.software_filter = software_filter; break; - case 1: config::video.fullscreen.software_filter = software_filter; break; +void update_software_filter(unsigned software_filter) { + switch(config.video.mode) { default: + case 0: config.video.windowed.software_filter = software_filter; break; + case 1: config.video.fullscreen.software_filter = software_filter; break; } update_video_settings(); } void update_frameskip(int speed) { - config::video.frameskip = max(0, min(9, speed)); + config.video.frameskip = max(0, min(9, speed)); window_main.sync(); } void update_emulation_speed(int speed) { - config::system.emulation_speed = speed = max(0, min(4, speed)); + config.system.emulation_speed = speed = max(0, min(4, speed)); double scale[] = { 0.50, 0.75, 1.00, 1.50, 2.00 }; - unsigned outfreq = config::audio.output_frequency; - unsigned infreq = config::audio.input_frequency * scale[speed] + 0.5; + unsigned outfreq = config.audio.output_frequency; + unsigned infreq = config.audio.input_frequency * scale[speed] + 0.5; audio.set(Audio::Resample, outfreq != infreq); //only resample when necessary audio.set(Audio::ResampleOutputFrequency, outfreq); @@ -238,23 +238,24 @@ void modify_system_state(system_state_t state) { ? window_main.menu_system_power_on.check() : window_main.menu_system_power_off.check(); window_cheat_editor.refresh(); + if(cartridge.loaded() == false) window_cheat_code_editor.hide(); } void update_controller_port1(int device) { - unsigned current_device = config::snes.controller_port1; + unsigned current_device = snes.config.controller_port1; if(device != current_device) { - snes.input.port_set_device(0, config::snes.controller_port1 = device); + snes.input.port_set_device(0, snes.config.controller_port1 = device); } window_main.sync(); } void update_controller_port2(int device) { - unsigned current_device = config::snes.controller_port2; + unsigned current_device = snes.config.controller_port2; if(device != current_device) { - snes.input.port_set_device(1, config::snes.controller_port2 = device); + snes.input.port_set_device(1, snes.config.controller_port2 = device); } window_main.sync(); @@ -273,9 +274,9 @@ void update_video_settings() { if(video_settings.aspect_correction == true) { double scalar; if(mode == SNES::Video::ModeNTSC) { - scalar = (double)config::video.aspect_ntsc_x / (double)config::video.aspect_ntsc_y; + scalar = (double)config.video.aspect_ntsc_x / (double)config.video.aspect_ntsc_y; } else { - scalar = (double)config::video.aspect_pal_x / (double)config::video.aspect_pal_y; + scalar = (double)config.video.aspect_pal_x / (double)config.video.aspect_pal_y; } width = (unsigned)((double)width * (double)scalar); } @@ -309,6 +310,7 @@ void update_video_settings() { window_main.move(window_main.view, (viewwidth - width) / 2, (viewheight - height) / 2); window_main.view.resize(width, height); + //update software filter mode libfilter::FilterInterface::FilterType filter; switch(video_settings.software_filter) { default: case 0: filter = libfilter::FilterInterface::Direct; break; @@ -317,8 +319,8 @@ void update_video_settings() { case 3: filter = libfilter::FilterInterface::HQ2x; break; case 4: filter = libfilter::FilterInterface::NTSC; break; } - libfilter::filter.set(filter); + snes.video.set_mode(mode); video.set(Video::Synchronize, video_settings.synchronize); video.set(Video::Filter, video_settings.hardware_filter); @@ -327,22 +329,24 @@ void update_video_settings() { void update_opacity() { //convert opacity from 50-100 (percentage) to 128-255 (8-bit alpha) - unsigned opacity = max(50, min(100, config::misc.opacity)); + unsigned opacity = max(50, min(100, config.misc.window_opacity)); opacity = unsigned(256.0 / 100.0 * opacity); opacity = max(128, min(255, opacity)); - window_settings.set_opacity(opacity); - window_input_capture.set_opacity(opacity); - window_bsxloader.set_opacity(opacity); - window_stloader.set_opacity(opacity); window_about.set_opacity(opacity); + window_bsxloader.set_opacity(opacity); + window_cheat_code_editor.set_opacity(opacity); + window_input_capture.set_opacity(opacity); + window_settings.set_opacity(opacity); + window_stloader.set_opacity(opacity); + window_textview.set_opacity(opacity); } void toggle_fullscreen() { - if(config::video.mode != 1) { //switch to fullscreen mode if not already in it - config::video.mode = 1; + if(config.video.mode != 1) { //switch to fullscreen mode if not already in it + config.video.mode = 1; } else { //switch to windowed mode if already in fullscreen mode - config::video.mode = 0; + config.video.mode = 0; } update_video_settings(); } @@ -364,7 +368,7 @@ bool load_cart(char *fn) { lstring dir; strcpy(fn, ""); - strcpy(dir[0], config::path.rom); + strcpy(dir[0], snes.config.path.rom); replace(dir[0], "\\", "/"); if(strlen(dir[0]) && !strend(dir[0], "/")) strcat(dir[0], "/"); @@ -372,7 +376,7 @@ bool load_cart(char *fn) { if(strbegin(dir[0], "./")) { ltrim(dir[0], "./"); strcpy(dir[1], dir[0]); - strcpy(dir[0], config::path.base); + strcpy(dir[0], snes.config.path.base); strcat(dir[0], dir[1]); } @@ -429,7 +433,7 @@ void load_image(const char *filename) { case Cartridge::TypeBSX: { window_bsxloader.mode = BSXLoaderWindow::ModeBSX; window_bsxloader.set_text(translate["Load BS-X Cartridge"]); - window_bsxloader.tbase.set_text(config::path.bsx); + window_bsxloader.tbase.set_text(snes.config.path.bsx); window_bsxloader.tslot.set_text(filename); window_bsxloader.load.focus(); window_bsxloader.focus(); @@ -444,7 +448,7 @@ void load_image(const char *filename) { } break; case Cartridge::TypeSufamiTurbo: { - window_stloader.tbase.set_text(config::path.st); + window_stloader.tbase.set_text(snes.config.path.st); window_stloader.tslotA.set_text(filename); window_stloader.tslotB.set_text(""); window_stloader.load.focus(); @@ -497,12 +501,15 @@ void unload_cart() { void quit() { app.term = true; - window_textview.hide(); - window_about.hide(); - window_settings.hide(); - window_bsxloader.hide(); - window_stloader.hide(); window_main.hide(); + + window_about.hide(); + window_bsxloader.hide(); + window_cheat_code_editor.hide(); + window_input_capture.hide(); + window_settings.hide(); + window_stloader.hide(); + window_textview.hide(); } }; diff --git a/src/ui/event/event.hpp b/src/ui/event/event.hpp index 38717c45..29c6147d 100644 --- a/src/ui/event/event.hpp +++ b/src/ui/event/event.hpp @@ -8,21 +8,21 @@ void acquire(); void unacquire(); struct VideoSettings { - uint mode; + unsigned mode; bool synchronize; bool aspect_correction; - uint region; - uint multiplier; - uint hardware_filter; - uint software_filter; + unsigned region; + unsigned multiplier; + unsigned hardware_filter; + unsigned software_filter; } video_settings; void load_video_settings(); void update_aspect_correction(bool); -void update_multiplier(uint); -void update_region(uint); -void update_hardware_filter(uint); -void update_software_filter(uint); +void update_multiplier(unsigned); +void update_region(unsigned); +void update_hardware_filter(unsigned); +void update_software_filter(unsigned); void update_frameskip(int); void update_emulation_speed(int); diff --git a/src/ui/inputdevices.cpp b/src/ui/inputdevices.cpp index 3156e7e3..8342f39c 100644 --- a/src/ui/inputdevices.cpp +++ b/src/ui/inputdevices.cpp @@ -34,12 +34,12 @@ struct InputCode { struct InputObject { enum Type { Button, Axis } type; const char *name; - string_setting &setting; + string &setting; uint16_t code; int16_t state; - void bind() { code = input_find(setting); } - InputObject(Type t, const char *n, string_setting &s) : type(t), name(n), setting(s) {} + void bind() { code = input_find((const char*)setting); } + InputObject(Type t, const char *n, string &s) : type(t), name(n), setting(s) {} }; struct InputGroup { @@ -96,6 +96,13 @@ struct Joypad : InputDevice { InputObject l, r, select, start; int16_t state(unsigned index) { + if(config.input.allow_invalid_input == false) { + //SNES D-pads have central pivot point, making up+down or left+right combinations impossible. + //some software programs rely on this, and will crash if these combinations are allowed. + if(index == SNES::Input::JoypadDown && up.state ) return 0; + if(index == SNES::Input::JoypadRight && left.state) return 0; + } + switch(index) { case SNES::Input::JoypadUp: return up.state; case SNES::Input::JoypadDown: return down.state; @@ -115,9 +122,9 @@ struct Joypad : InputDevice { Joypad( SNES::Input::DeviceID id, bool port, const char *name, - string_setting &up_t, string_setting &down_t, string_setting &left_t, string_setting &right_t, - string_setting &a_t, string_setting &b_t, string_setting &x_t, string_setting &y_t, - string_setting &l_t, string_setting &r_t, string_setting &select_t, string_setting &start_t + string &up_t, string &down_t, string &left_t, string &right_t, + string &a_t, string &b_t, string &x_t, string &y_t, + string &l_t, string &r_t, string &select_t, string &start_t ) : InputDevice(id, port, name), up (InputObject::Button, "Up", up_t), @@ -155,8 +162,7 @@ struct Mouse : InputDevice { Mouse( SNES::Input::DeviceID id, bool port, const char *name, - string_setting &x_t, string_setting &y_t, - string_setting &left_t, string_setting &right_t + string &x_t, string &y_t, string &left_t, string &right_t ) : InputDevice(id, port, name), x (InputObject::Axis, "X-axis", x_t), @@ -186,8 +192,7 @@ struct SuperScope : InputDevice { SuperScope( SNES::Input::DeviceID id, bool port, const char *name, - string_setting &x_t, string_setting &y_t, - string_setting &trigger_t, string_setting &cursor_t, string_setting &turbo_t, string_setting &pause_t + string &x_t, string &y_t, string &trigger_t, string &cursor_t, string &turbo_t, string &pause_t ) : InputDevice(id, port, name), x (InputObject::Axis, "X-axis", x_t), @@ -218,8 +223,7 @@ struct Justifier : InputDevice { Justifier( SNES::Input::DeviceID id, bool port, const char *name, - string_setting &x_t, string_setting &y_t, - string_setting &trigger_t, string_setting &start_t + string &x_t, string &y_t, string &trigger_t, string &start_t ) : InputDevice(id, port, name), x (InputObject::Axis, "X-axis", x_t), @@ -263,101 +267,101 @@ struct InputDevicePool { Joypad joypad1( SNES::Input::DeviceIDJoypad1, InputDevice::Port1, "Joypad", -config::input.joypad1.up, config::input.joypad1.down, config::input.joypad1.left, config::input.joypad1.right, -config::input.joypad1.a, config::input.joypad1.b, config::input.joypad1.x, config::input.joypad1.y, -config::input.joypad1.l, config::input.joypad1.r, config::input.joypad1.select, config::input.joypad1.start +config.input.joypad1.up, config.input.joypad1.down, config.input.joypad1.left, config.input.joypad1.right, +config.input.joypad1.a, config.input.joypad1.b, config.input.joypad1.x, config.input.joypad1.y, +config.input.joypad1.l, config.input.joypad1.r, config.input.joypad1.select, config.input.joypad1.start ); Joypad joypad2( SNES::Input::DeviceIDJoypad2, InputDevice::Port2, "Joypad", -config::input.joypad2.up, config::input.joypad2.down, config::input.joypad2.left, config::input.joypad2.right, -config::input.joypad2.a, config::input.joypad2.b, config::input.joypad2.x, config::input.joypad2.y, -config::input.joypad2.l, config::input.joypad2.r, config::input.joypad2.select, config::input.joypad2.start +config.input.joypad2.up, config.input.joypad2.down, config.input.joypad2.left, config.input.joypad2.right, +config.input.joypad2.a, config.input.joypad2.b, config.input.joypad2.x, config.input.joypad2.y, +config.input.joypad2.l, config.input.joypad2.r, config.input.joypad2.select, config.input.joypad2.start ); Joypad multitap1a( SNES::Input::DeviceIDMultitap1A, InputDevice::Port1, "Multitap - Port 1", -config::input.multitap1a.up, config::input.multitap1a.down, config::input.multitap1a.left, config::input.multitap1a.right, -config::input.multitap1a.a, config::input.multitap1a.b, config::input.multitap1a.x, config::input.multitap1a.y, -config::input.multitap1a.l, config::input.multitap1a.r, config::input.multitap1a.select, config::input.multitap1a.start +config.input.multitap1a.up, config.input.multitap1a.down, config.input.multitap1a.left, config.input.multitap1a.right, +config.input.multitap1a.a, config.input.multitap1a.b, config.input.multitap1a.x, config.input.multitap1a.y, +config.input.multitap1a.l, config.input.multitap1a.r, config.input.multitap1a.select, config.input.multitap1a.start ); Joypad multitap1b( SNES::Input::DeviceIDMultitap1B, InputDevice::Port1, "Multitap - Port 2", -config::input.multitap1b.up, config::input.multitap1b.down, config::input.multitap1b.left, config::input.multitap1b.right, -config::input.multitap1b.a, config::input.multitap1b.b, config::input.multitap1b.x, config::input.multitap1b.y, -config::input.multitap1b.l, config::input.multitap1b.r, config::input.multitap1b.select, config::input.multitap1b.start +config.input.multitap1b.up, config.input.multitap1b.down, config.input.multitap1b.left, config.input.multitap1b.right, +config.input.multitap1b.a, config.input.multitap1b.b, config.input.multitap1b.x, config.input.multitap1b.y, +config.input.multitap1b.l, config.input.multitap1b.r, config.input.multitap1b.select, config.input.multitap1b.start ); Joypad multitap1c( SNES::Input::DeviceIDMultitap1C, InputDevice::Port1, "Multitap - Port 3", -config::input.multitap1c.up, config::input.multitap1c.down, config::input.multitap1c.left, config::input.multitap1c.right, -config::input.multitap1c.a, config::input.multitap1c.b, config::input.multitap1c.x, config::input.multitap1c.y, -config::input.multitap1c.l, config::input.multitap1c.r, config::input.multitap1c.select, config::input.multitap1c.start +config.input.multitap1c.up, config.input.multitap1c.down, config.input.multitap1c.left, config.input.multitap1c.right, +config.input.multitap1c.a, config.input.multitap1c.b, config.input.multitap1c.x, config.input.multitap1c.y, +config.input.multitap1c.l, config.input.multitap1c.r, config.input.multitap1c.select, config.input.multitap1c.start ); Joypad multitap1d( SNES::Input::DeviceIDMultitap1D, InputDevice::Port1, "Multitap - Port 4", -config::input.multitap1d.up, config::input.multitap1d.down, config::input.multitap1d.left, config::input.multitap1d.right, -config::input.multitap1d.a, config::input.multitap1d.b, config::input.multitap1d.x, config::input.multitap1d.y, -config::input.multitap1d.l, config::input.multitap1d.r, config::input.multitap1d.select, config::input.multitap1d.start +config.input.multitap1d.up, config.input.multitap1d.down, config.input.multitap1d.left, config.input.multitap1d.right, +config.input.multitap1d.a, config.input.multitap1d.b, config.input.multitap1d.x, config.input.multitap1d.y, +config.input.multitap1d.l, config.input.multitap1d.r, config.input.multitap1d.select, config.input.multitap1d.start ); Joypad multitap2a( SNES::Input::DeviceIDMultitap2A, InputDevice::Port2, "Multitap - Port 1", -config::input.multitap2a.up, config::input.multitap2a.down, config::input.multitap2a.left, config::input.multitap2a.right, -config::input.multitap2a.a, config::input.multitap2a.b, config::input.multitap2a.x, config::input.multitap2a.y, -config::input.multitap2a.l, config::input.multitap2a.r, config::input.multitap2a.select, config::input.multitap2a.start +config.input.multitap2a.up, config.input.multitap2a.down, config.input.multitap2a.left, config.input.multitap2a.right, +config.input.multitap2a.a, config.input.multitap2a.b, config.input.multitap2a.x, config.input.multitap2a.y, +config.input.multitap2a.l, config.input.multitap2a.r, config.input.multitap2a.select, config.input.multitap2a.start ); Joypad multitap2b( SNES::Input::DeviceIDMultitap2B, InputDevice::Port2, "Multitap - Port 2", -config::input.multitap2b.up, config::input.multitap2b.down, config::input.multitap2b.left, config::input.multitap2b.right, -config::input.multitap2b.a, config::input.multitap2b.b, config::input.multitap2b.x, config::input.multitap2b.y, -config::input.multitap2b.l, config::input.multitap2b.r, config::input.multitap2b.select, config::input.multitap2b.start +config.input.multitap2b.up, config.input.multitap2b.down, config.input.multitap2b.left, config.input.multitap2b.right, +config.input.multitap2b.a, config.input.multitap2b.b, config.input.multitap2b.x, config.input.multitap2b.y, +config.input.multitap2b.l, config.input.multitap2b.r, config.input.multitap2b.select, config.input.multitap2b.start ); Joypad multitap2c( SNES::Input::DeviceIDMultitap2C, InputDevice::Port2, "Multitap - Port 3", -config::input.multitap2c.up, config::input.multitap2c.down, config::input.multitap2c.left, config::input.multitap2c.right, -config::input.multitap2c.a, config::input.multitap2c.b, config::input.multitap2c.x, config::input.multitap2c.y, -config::input.multitap2c.l, config::input.multitap2c.r, config::input.multitap2c.select, config::input.multitap2c.start +config.input.multitap2c.up, config.input.multitap2c.down, config.input.multitap2c.left, config.input.multitap2c.right, +config.input.multitap2c.a, config.input.multitap2c.b, config.input.multitap2c.x, config.input.multitap2c.y, +config.input.multitap2c.l, config.input.multitap2c.r, config.input.multitap2c.select, config.input.multitap2c.start ); Joypad multitap2d( SNES::Input::DeviceIDMultitap2D, InputDevice::Port2, "Multitap - Port 4", -config::input.multitap2d.up, config::input.multitap2d.down, config::input.multitap2d.left, config::input.multitap2d.right, -config::input.multitap2d.a, config::input.multitap2d.b, config::input.multitap2d.x, config::input.multitap2d.y, -config::input.multitap2d.l, config::input.multitap2d.r, config::input.multitap2d.select, config::input.multitap2d.start +config.input.multitap2d.up, config.input.multitap2d.down, config.input.multitap2d.left, config.input.multitap2d.right, +config.input.multitap2d.a, config.input.multitap2d.b, config.input.multitap2d.x, config.input.multitap2d.y, +config.input.multitap2d.l, config.input.multitap2d.r, config.input.multitap2d.select, config.input.multitap2d.start ); Mouse mouse1( SNES::Input::DeviceIDMouse1, InputDevice::Port1, "Mouse", -config::input.mouse1.x, config::input.mouse1.y, config::input.mouse1.l, config::input.mouse1.r +config.input.mouse1.x, config.input.mouse1.y, config.input.mouse1.l, config.input.mouse1.r ); Mouse mouse2( SNES::Input::DeviceIDMouse2, InputDevice::Port2, "Mouse", -config::input.mouse2.x, config::input.mouse2.y, config::input.mouse2.l, config::input.mouse2.r +config.input.mouse2.x, config.input.mouse2.y, config.input.mouse2.l, config.input.mouse2.r ); SuperScope superscope( SNES::Input::DeviceIDSuperScope, InputDevice::Port2, "Super Scope", -config::input.superscope.x, config::input.superscope.y, -config::input.superscope.trigger, config::input.superscope.cursor, -config::input.superscope.turbo, config::input.superscope.pause +config.input.superscope.x, config.input.superscope.y, +config.input.superscope.trigger, config.input.superscope.cursor, +config.input.superscope.turbo, config.input.superscope.pause ); Justifier justifier1( SNES::Input::DeviceIDJustifier1, InputDevice::Port2, "Justifier 1", -config::input.justifier1.x, config::input.justifier1.y, -config::input.justifier1.trigger, config::input.justifier1.start +config.input.justifier1.x, config.input.justifier1.y, +config.input.justifier1.trigger, config.input.justifier1.start ); Justifier justifier2( SNES::Input::DeviceIDJustifier2, InputDevice::Port2, "Justifier 2", -config::input.justifier2.x, config::input.justifier2.y, -config::input.justifier2.trigger, config::input.justifier2.start +config.input.justifier2.x, config.input.justifier2.y, +config.input.justifier2.trigger, config.input.justifier2.start ); InputDevicePool::InputDevicePool() { diff --git a/src/ui/inputui.cpp b/src/ui/inputui.cpp index 368bd9d8..49c16a1a 100644 --- a/src/ui/inputui.cpp +++ b/src/ui/inputui.cpp @@ -14,18 +14,18 @@ struct InputUIGeneral : InputGroup { InputUIGeneral() : InputGroup("General"), - load (InputObject::Button, "Load cartridge", config::input.gui.load), - pause (InputObject::Button, "Pause emulation", config::input.gui.pause), - reset (InputObject::Button, "Reset system", config::input.gui.reset), - power (InputObject::Button, "Power cycle system", config::input.gui.power), - quit (InputObject::Button, "Exit emulator", config::input.gui.quit), - speed_decrease (InputObject::Button, "Decrease emulation speed", config::input.gui.speed_decrease), - speed_increase (InputObject::Button, "Increase emulation speed", config::input.gui.speed_increase), - frameskip_decrease(InputObject::Button, "Decrease frameskip rate", config::input.gui.frameskip_decrease), - frameskip_increase(InputObject::Button, "Increase frameskip rate", config::input.gui.frameskip_increase), - toggle_fullscreen (InputObject::Button, "Toggle fullscreen mode", config::input.gui.toggle_fullscreen), - toggle_menubar (InputObject::Button, "Toggle menubar", config::input.gui.toggle_menubar), - toggle_statusbar (InputObject::Button, "Toggle statusbar", config::input.gui.toggle_statusbar) + load (InputObject::Button, "Load cartridge", config.input.gui.load), + pause (InputObject::Button, "Pause emulation", config.input.gui.pause), + reset (InputObject::Button, "Reset system", config.input.gui.reset), + power (InputObject::Button, "Power cycle system", config.input.gui.power), + quit (InputObject::Button, "Exit emulator", config.input.gui.quit), + speed_decrease (InputObject::Button, "Decrease emulation speed", config.input.gui.speed_decrease), + speed_increase (InputObject::Button, "Increase emulation speed", config.input.gui.speed_increase), + frameskip_decrease(InputObject::Button, "Decrease frameskip rate", config.input.gui.frameskip_decrease), + frameskip_increase(InputObject::Button, "Increase frameskip rate", config.input.gui.frameskip_increase), + toggle_fullscreen (InputObject::Button, "Toggle fullscreen mode", config.input.gui.toggle_fullscreen), + toggle_menubar (InputObject::Button, "Toggle menubar", config.input.gui.toggle_menubar), + toggle_statusbar (InputObject::Button, "Toggle statusbar", config.input.gui.toggle_statusbar) { attach(load); attach(pause); attach(reset); attach(power); attach(quit); attach(speed_decrease); attach(speed_increase); @@ -43,11 +43,11 @@ struct InputUIDebug : InputGroup { InputUIDebug() : InputGroup("Debugger"), - export_memory (InputObject::Button, "Export memory", config::input.debugger.export_memory), - toggle_cputrace (InputObject::Button, "Toggle S-CPU tracing", config::input.debugger.toggle_cputrace), - toggle_cputracemask(InputObject::Button, "Toggle S-CPU trace mask", config::input.debugger.toggle_cputracemask), - toggle_smptrace (InputObject::Button, "Toggle S-SMP tracing", config::input.debugger.toggle_smptrace), - toggle_smptracemask(InputObject::Button, "Toggle S-SMP trace mask", config::input.debugger.toggle_smptracemask) + export_memory (InputObject::Button, "Export memory", config.input.debugger.export_memory), + toggle_cputrace (InputObject::Button, "Toggle S-CPU tracing", config.input.debugger.toggle_cputrace), + toggle_cputracemask(InputObject::Button, "Toggle S-CPU trace mask", config.input.debugger.toggle_cputracemask), + toggle_smptrace (InputObject::Button, "Toggle S-SMP tracing", config.input.debugger.toggle_smptrace), + toggle_smptracemask(InputObject::Button, "Toggle S-SMP trace mask", config.input.debugger.toggle_smptracemask) { attach(export_memory); attach(toggle_cputrace); attach(toggle_cputracemask); diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index 3329f654..16e05f68 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -24,15 +24,15 @@ void SNESInterface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line } } - if(config::video.frameskip == 0) { + if(config.video.frameskip == 0) { ppu.enable_renderer(true); } else { frameskip_counter++; - frameskip_counter %= config::video.frameskip + 1; + frameskip_counter %= config.video.frameskip + 1; if(frameskip_counter == 0) { //randomize which frame of set will be rendered, //helps with two-frame animations (such as blinking) - frameskip_offset = rand() % (config::video.frameskip + 1); + frameskip_offset = rand() % (config.video.frameskip + 1); } ppu.enable_renderer(frameskip_counter == frameskip_offset); } @@ -41,7 +41,7 @@ void SNESInterface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line //audio void SNESInterface::audio_sample(uint16 l_sample, uint16 r_sample) { - if(config::audio.mute == true) { + if(config.audio.mute == true) { l_sample = 0; r_sample = 0; } @@ -67,14 +67,15 @@ int16_t SNESInterface::input_poll(unsigned deviceid, unsigned id) { void SNESInterface::init() { input_manager.bind(); libfilter::colortable.set_format(libfilter::Colortable::RGB888); - libfilter::colortable.set_contrast(config::system.contrast); - libfilter::colortable.set_brightness(config::system.brightness); - libfilter::colortable.set_gamma(config::system.gamma); - libfilter::colortable.enable_gamma_ramp(config::system.gamma_ramp); - libfilter::colortable.enable_sepia(config::system.sepia); - libfilter::colortable.enable_grayscale(config::system.grayscale); - libfilter::colortable.enable_invert(config::system.invert); + libfilter::colortable.set_contrast(config.video.contrast); + libfilter::colortable.set_brightness(config.video.brightness); + libfilter::colortable.set_gamma(config.video.gamma); + libfilter::colortable.enable_gamma_ramp(config.video.gamma_ramp); + libfilter::colortable.enable_sepia(config.video.sepia); + libfilter::colortable.enable_grayscale(config.video.grayscale); + libfilter::colortable.enable_invert(config.video.invert); libfilter::colortable.update(); + libfilter::filter_ntsc.adjust(0, 0, 0, 0, 0, config.video.ntsc_filter_merge_fields); } void SNESInterface::term() { diff --git a/src/ui/loader/bsxloader.cpp b/src/ui/loader/bsxloader.cpp index f04fadc5..e443c84e 100644 --- a/src/ui/loader/bsxloader.cpp +++ b/src/ui/loader/bsxloader.cpp @@ -4,7 +4,7 @@ uintptr_t BSXLoaderWindow::close(event_t) { } uintptr_t BSXLoaderWindow::bbase_tick(event_t) { -char fn[PATH_MAX]; + char fn[PATH_MAX]; if(event::load_cart(fn) == true) tbase.set_text(fn); return true; } @@ -15,7 +15,7 @@ uintptr_t BSXLoaderWindow::cbase_tick(event_t) { } uintptr_t BSXLoaderWindow::bslot_tick(event_t) { -char fn[PATH_MAX]; + char fn[PATH_MAX]; if(event::load_cart(fn) == true) tslot.set_text(fn); return true; } @@ -26,12 +26,12 @@ uintptr_t BSXLoaderWindow::cslot_tick(event_t) { } uintptr_t BSXLoaderWindow::load_tick(event_t) { -char base[PATH_MAX], slot[PATH_MAX]; + char base[PATH_MAX], slot[PATH_MAX]; tbase.get_text(base, PATH_MAX); tslot.get_text(slot, PATH_MAX); if(mode == ModeBSX) { - config::path.bsx = base; + snes.config.path.bsx = base; event::load_cart_bsx(base, slot); } else if(mode == ModeBSC) { event::load_cart_bsc(base, slot); diff --git a/src/ui/loader/stloader.cpp b/src/ui/loader/stloader.cpp index a31349ed..b48a5f94 100644 --- a/src/ui/loader/stloader.cpp +++ b/src/ui/loader/stloader.cpp @@ -4,7 +4,7 @@ uintptr_t STLoaderWindow::close(event_t) { } uintptr_t STLoaderWindow::bbase_tick(event_t) { -char fn[PATH_MAX]; + char fn[PATH_MAX]; if(event::load_cart(fn) == true) tbase.set_text(fn); return true; } @@ -15,7 +15,7 @@ uintptr_t STLoaderWindow::cbase_tick(event_t) { } uintptr_t STLoaderWindow::bslotA_tick(event_t) { -char fn[PATH_MAX]; + char fn[PATH_MAX]; if(event::load_cart(fn) == true) tslotA.set_text(fn); return true; } @@ -26,7 +26,7 @@ uintptr_t STLoaderWindow::cslotA_tick(event_t) { } uintptr_t STLoaderWindow::bslotB_tick(event_t) { -char fn[PATH_MAX]; + char fn[PATH_MAX]; if(event::load_cart(fn) == true) tslotB.set_text(fn); return true; } @@ -37,12 +37,12 @@ uintptr_t STLoaderWindow::cslotB_tick(event_t) { } uintptr_t STLoaderWindow::load_tick(event_t) { -char base[PATH_MAX], slotA[PATH_MAX], slotB[PATH_MAX]; + char base[PATH_MAX], slotA[PATH_MAX], slotB[PATH_MAX]; tbase.get_text(base, PATH_MAX); tslotA.get_text(slotA, PATH_MAX); tslotB.get_text(slotB, PATH_MAX); - config::path.st = base; + snes.config.path.st = base; event::load_cart_st(base, slotA, slotB); tbase.set_text(""); diff --git a/src/ui/main.cpp b/src/ui/main.cpp index ba4418da..13d5bb76 100644 --- a/src/ui/main.cpp +++ b/src/ui/main.cpp @@ -1,4 +1,5 @@ #include <../base.hpp> +#include <../cart/cart.hpp> #include "main.hpp" #include "config.cpp" @@ -35,16 +36,16 @@ void get_paths(const char *image) { } if(strend(temp, "/") == false) strcat(temp, "/"); - config::path.base = temp; + snes.config.path.base = temp; } else { - config::path.base = ""; + snes.config.path.base = ""; } if(userpath(temp)) { if(strend(temp, "/") == false) strcat(temp, "/"); - config::path.user = temp; + snes.config.path.user = temp; } else { - config::path.user = ""; + snes.config.path.user = ""; } } @@ -52,26 +53,26 @@ void set_config_filenames() { char filename[PATH_MAX]; //locate bsnes.cfg - strcpy(filename, config::path.base); + strcpy(filename, snes.config.path.base); strcat(filename, "bsnes.cfg"); if(!file::exists(filename)) { - strcpy(filename, config::path.user); + strcpy(filename, snes.config.path.user); strcat(filename, ".bsnes"); mkdir(filename); strcat(filename, "/bsnes.cfg"); } - strcpy(config::bsnes_cfg, filename); + strcpy(config.bsnes_cfg, filename); //locate locale.cfg - strcpy(filename, config::path.base); + strcpy(filename, snes.config.path.base); strcat(filename, "locale.cfg"); if(!file::exists(filename)) { - strcpy(filename, config::path.user); + strcpy(filename, snes.config.path.user); strcat(filename, ".bsnes"); mkdir(filename); strcat(filename, "/locale.cfg"); } - strcpy(config::locale_cfg, filename); + strcpy(config.locale_cfg, filename); } void run() { @@ -79,7 +80,7 @@ void run() { status.update(); input_manager.refresh(); - if(config::input.capture_mode == 2) { + if(config.input.capture_mode == 2) { bool inactive = (window_main.focused() == false); if(app.autopause == false && inactive == true) { app.autopause = true; @@ -102,13 +103,13 @@ int hiromain(int argc, const char *const argv[]) { get_paths(argv[0]); set_config_filenames(); - config::config().load(config::bsnes_cfg); - if(file::exists(config::bsnes_cfg) == false) { + config.load(config.bsnes_cfg); + if(file::exists(config.bsnes_cfg) == false) { //in case program crashes on first run, save config file //settings, so that they can be modified by hand ... - config::config().save(config::bsnes_cfg); + config.save(config.bsnes_cfg); } - translate.import(config::locale_cfg); + translate.import(config.locale_cfg); ui_init(); if(app.term == false) { @@ -118,7 +119,7 @@ int hiromain(int argc, const char *const argv[]) { event::unload_cart(); } - config::config().save(config::bsnes_cfg); + config.save(config.bsnes_cfg); snes.term(); ui_term(); return 0; diff --git a/src/ui/settings/advanced.cpp b/src/ui/settings/advanced.cpp index c1e33e99..c4ea45e5 100644 --- a/src/ui/settings/advanced.cpp +++ b/src/ui/settings/advanced.cpp @@ -1,49 +1,40 @@ -uintptr_t AdvancedWindow::list_change(event_t) { - int pos = list.get_selection(); - edit_val.enable(pos >= 0); - set_val.enable(pos >= 0); - set_def.enable(pos >= 0); - if(pos >= 0) { - unsigned i = lookup[pos]; - string default_; - config::config().list[i]->get_default(default_); - desc.set_text(string() - << "(" << translate["{{advanced}}Default"] << " = " << default_ << ")\n" - << config::config().list[i]->description); - string value_; - config::config().list[i]->get(value_); - edit_val.set_text(value_); +uintptr_t AdvancedWindow::list_activate(event_t) { + int item = list.get_selection(); + if(item >= 0) { + //if item is integral_setting::boolean, toggle its state + if(config.list[lookup[item]].type == configuration::boolean_t) { + *(bool*)config.list[lookup[item]].data = *(bool*)config.list[lookup[item]].data ? false : true; + update(item); + } } return true; } -uintptr_t AdvancedWindow::setval_tick(event_t) { - char t[4096]; - edit_val.get_text(t, sizeof t); - update(list.get_selection(), t); +uintptr_t AdvancedWindow::list_change(event_t) { + int item = list.get_selection(); + edit.enable(item >= 0); + set.enable (item >= 0); + edit.set_text(item >= 0 ? config.list[lookup[item]].get() : translate[""]); return true; } -uintptr_t AdvancedWindow::setdef_tick(event_t) { - update(list.get_selection(), 0); +uintptr_t AdvancedWindow::set_tick(event_t) { + int item = list.get_selection(); + if(item >= 0) { + char value[4096]; + edit.get_text(value, sizeof value); + config.list[lookup[item]].set(value); + update(item); + } return true; } -void AdvancedWindow::update(uint pos, const char *data) { - unsigned i = lookup[pos]; - string default_; - config::config().list[i]->get_default(default_); - config::config().list[i]->set(data ? data : (const char*)default_); - string value_; - config::config().list[i]->get(value_); - edit_val.set_text(value_); - list.set_item(pos, string() - << config::config().list[i]->name - << (value_ == default_ ? "" : " (*)") - << "\t" - << (config::config().list[i]->type == setting::string_type ? translate["string"] : translate["integer"]) - << "\t" - << value_ +void AdvancedWindow::update(unsigned item) { + edit.set_text(config.list[lookup[item]].get()); + list.set_item(item, string() + << config.list[lookup[item]].name << "\t" + << config.list[lookup[item]].get() << "\t" + << config.list[lookup[item]].desc ); list.autosize_columns(); } @@ -51,8 +42,8 @@ void AdvancedWindow::update(uint pos, const char *data) { void AdvancedWindow::load() { lookup.reset(); - for(unsigned i = 0; i < config::config().list.size(); i++) { - string name = config::config().list[i]->name; + for(unsigned i = 0; i < config.list.size(); i++) { + string name = config.list[i].name; //blacklist (omit/hide options that can be configured through the standard UI) if(name == "snes.expansion_port") continue; @@ -60,11 +51,15 @@ void AdvancedWindow::load() { if(strbegin(name, "system.")) continue; if(strbegin(name, "path.")) continue; if(strbegin(name, "snes.controller_port")) continue; - if(strpos(name, "colorfilter.") >= 0) continue; if(name == "system.emulation_speed") continue; if(strbegin(name, "video.windowed.")) continue; if(strbegin(name, "video.fullscreen.")) continue; if(name == "video.synchronize") continue; + if(name == "video.contrast") continue; + if(name == "video.brightness") continue; + if(name == "video.gamma") continue; + if(name == "video.gamma_ramp") continue; + if(name == "video.ntsc_filter_merge_fields") continue; if(strbegin(name, "audio.")) continue; if(name == "input.capture_mode") continue; if(strbegin(name, "input.joypad")) continue; @@ -76,16 +71,10 @@ void AdvancedWindow::load() { if(strbegin(name, "input.debugger")) continue; if(name == "misc.cheat_autosort") continue; - string value_, default_; - config::config().list[i]->get(value_); - config::config().list[i]->get_default(default_); list.add_item(string() - << name - << (value_ == default_ ? "" : " (*)") - << "\t" - << (config::config().list[i]->type == setting::string_type ? translate["string"] : translate["integer"]) - << "\t" - << value_ + << name << "\t" + << config.list[i].get() << "\t" + << config.list[i].desc ); lookup.add(i); } @@ -94,29 +83,23 @@ void AdvancedWindow::load() { void AdvancedWindow::setup() { create(0, 451, 370); - list.create(Listbox::Header | Listbox::VerticalScrollAlways, - 451, 263, string() << translate["Name"] << "\t" << translate["Type"] << "\t" << translate["Value"]); - desc.create(Editbox::Multiline | Editbox::HorizontalScrollNever | Editbox::VerticalScrollAlways | Editbox::Readonly, - 451, 72, translate[""]); - edit_val.create(0, 241, 25, translate[""]); - set_val.create (0, 100, 25, translate["{{advanced}}Set"]); - set_def.create (0, 100, 25, translate["{{advanced}}Default"]); + list.create(Listbox::Header | Listbox::VerticalScrollAlways, 451, 340, + string() << translate["Name"] << "\t" << translate["Value"] << "\t" << translate["Description"]); + edit.create(0, 346, 25, translate[""]); + set.create (0, 100, 25, translate["{{advanced}}Set"]); unsigned y = 0; - attach(list, 0, y); y += 263 + 5; - attach(desc, 0, y); y += 72 + 5; - attach(edit_val, 0, y); - attach(set_val, 246, y); - attach(set_def, 351, y); y += 25 + 5; + attach(list, 0, y); y += 340 + 5; + attach(edit, 0, y); + attach(set, 351, y); load(); list.autosize_columns(); - edit_val.disable(); - set_val.disable(); - set_def.disable(); + edit.disable(); + set.disable(); - list.on_change = bind(&AdvancedWindow::list_change, this); - set_val.on_tick = bind(&AdvancedWindow::setval_tick, this); - set_def.on_tick = bind(&AdvancedWindow::setdef_tick, this); + list.on_activate = bind(&AdvancedWindow::list_activate, this); + list.on_change = bind(&AdvancedWindow::list_change, this); + set.on_tick = bind(&AdvancedWindow::set_tick, this); } diff --git a/src/ui/settings/advanced.hpp b/src/ui/settings/advanced.hpp index f0423230..10cb04b5 100644 --- a/src/ui/settings/advanced.hpp +++ b/src/ui/settings/advanced.hpp @@ -1,16 +1,15 @@ class AdvancedWindow : public Window { public: Listbox list; - Editbox desc; - Editbox edit_val; - Button set_val; - Button set_def; - + Editbox edit; + Button set; + array lookup; - void update(uint pos, const char *data); + void update(unsigned item); void load(); void setup(); + + uintptr_t list_activate(event_t); uintptr_t list_change(event_t); - uintptr_t setval_tick(event_t); - uintptr_t setdef_tick(event_t); + uintptr_t set_tick(event_t); } window_advanced; diff --git a/src/ui/settings/audiosettings.cpp b/src/ui/settings/audiosettings.cpp index 347ee8f7..e01f9c76 100644 --- a/src/ui/settings/audiosettings.cpp +++ b/src/ui/settings/audiosettings.cpp @@ -1,14 +1,14 @@ uintptr_t AudioSettingsWindow::volume_change(event_t) { - config::audio.volume = 25 + cvolume.get_selection() * 25; - audio.set(Audio::Volume, config::audio.volume); + config.audio.volume = 25 + cvolume.get_selection() * 25; + audio.set(Audio::Volume, config.audio.volume); sync_ui(); return true; } uintptr_t AudioSettingsWindow::latency_change(event_t) { - config::audio.latency = 20 + clatency.get_selection() * 20; - audio.set(Audio::Latency, config::audio.latency); + config.audio.latency = 20 + clatency.get_selection() * 20; + audio.set(Audio::Latency, config.audio.latency); sync_ui(); return true; @@ -16,41 +16,41 @@ uintptr_t AudioSettingsWindow::latency_change(event_t) { uintptr_t AudioSettingsWindow::frequency_change(event_t) { switch(cfrequency.get_selection()) { - case 0: config::audio.output_frequency = 32000; break; - case 1: config::audio.output_frequency = 44100; break; default: - case 2: config::audio.output_frequency = 48000; break; - case 3: config::audio.output_frequency = 96000; break; + case 0: config.audio.output_frequency = 32000; break; + case 1: config.audio.output_frequency = 44100; break; default: + case 2: config.audio.output_frequency = 48000; break; + case 3: config.audio.output_frequency = 96000; break; } - audio.set(Audio::Frequency, config::audio.output_frequency); - event::update_emulation_speed(config::system.emulation_speed); + audio.set(Audio::Frequency, config.audio.output_frequency); + event::update_emulation_speed(config.system.emulation_speed); sync_ui(); return true; } uintptr_t AudioSettingsWindow::skew_change(event_t) { - config::audio.input_frequency = sskew.get_position() + 32000 - 200; - event::update_emulation_speed(config::system.emulation_speed); + config.audio.input_frequency = sskew.get_position() + 32000 - 200; + event::update_emulation_speed(config.system.emulation_speed); sync_ui(); return true; } void AudioSettingsWindow::sync_ui() { - cvolume.set_selection((config::audio.volume - 25) / 25); + cvolume.set_selection((config.audio.volume - 25) / 25); unsigned position; - if(config::audio.output_frequency <= 32000) position = 0; - else if(config::audio.output_frequency <= 44100) position = 1; - else if(config::audio.output_frequency <= 48000) position = 2; + if(config.audio.output_frequency <= 32000) position = 0; + else if(config.audio.output_frequency <= 44100) position = 1; + else if(config.audio.output_frequency <= 48000) position = 2; else position = 3; cfrequency.set_selection(position); - clatency.set_selection((config::audio.latency - 20) / 20); + clatency.set_selection((config.audio.latency - 20) / 20); - lskew.set_text(string() << translate["{{audio}}Frequency adjust:"] << " " << (int)config::audio.input_frequency << "hz"); - sskew.set_position(config::audio.input_frequency - 32000 + 200); + lskew.set_text(string() << translate["{{audio}}Frequency adjust:"] << " " << (int)config.audio.input_frequency << "hz"); + sskew.set_position(config.audio.input_frequency - 32000 + 200); } void AudioSettingsWindow::setup() { @@ -62,7 +62,7 @@ void AudioSettingsWindow::setup() { cvolume.add_item( "50%"); cvolume.add_item( "75%"); cvolume.add_item("100%"); - if(config::advanced.enable == true) { + if(config.misc.show_advanced_options == true) { cvolume.add_item("125%"); cvolume.add_item("150%"); cvolume.add_item("175%"); @@ -74,7 +74,7 @@ void AudioSettingsWindow::setup() { cfrequency.add_item("32Khz"); cfrequency.add_item("44.1Khz"); cfrequency.add_item("48Khz"); - if(config::advanced.enable == true) { + if(config.misc.show_advanced_options == true) { cfrequency.add_item("96Khz"); } @@ -86,7 +86,7 @@ void AudioSettingsWindow::setup() { clatency.add_item( "80ms"); clatency.add_item("100ms"); clatency.add_item("120ms"); - if(config::advanced.enable == true) { + if(config.misc.show_advanced_options == true) { clatency.add_item("140ms"); clatency.add_item("160ms"); } diff --git a/src/ui/settings/cheateditor.cpp b/src/ui/settings/cheateditor.cpp index 6568bac8..37b1c029 100644 --- a/src/ui/settings/cheateditor.cpp +++ b/src/ui/settings/cheateditor.cpp @@ -1,86 +1,75 @@ +//================= +//CheatEditorWindow +//================= + void CheatEditorWindow::setup() { create(0, 451, 370); - list.create(Listbox::Header | Listbox::VerticalScrollAlways, 451, 287, + list.create(Listbox::Header | Listbox::VerticalScrollAlways, 451, 317, string() << translate["Status"] << "\t" << translate["Code"] << "\t" << translate["Description"]); autosort.create (0, 451, 18, translate["Keep cheat code list sorted by description"]); add_code.create (0, 147, 25, translate["Add Code"]); - toggle_code.create(0, 147, 25, translate["Toggle Status"]); + edit_code.create (0, 147, 25, translate["Edit Code"]); delete_code.create(0, 147, 25, translate["Delete Code"]); - code.create(0, 147, 25, translate[""]); - desc.create(0, 299, 25, translate[""]); unsigned y = 0; - attach(list, 0, y); y += 287 + 5; + attach(list, 0, y); y += 317 + 5; attach(autosort, 0, y); y += 18 + 5; attach(add_code, 0, y); - attach(toggle_code, 152, y); + attach(edit_code, 152, y); attach(delete_code, 304, y); y += 25 + 5; - attach(code, 0, y); - attach(desc, 152, y); y += 25 + 5; - autosort.check(config::misc.cheat_autosort); + autosort.check(config.misc.cheat_autosort); list.on_activate = bind(&CheatEditorWindow::toggle_code_state, this); list.on_change = bind(&CheatEditorWindow::list_change, this); autosort.on_tick = bind(&CheatEditorWindow::autosort_tick, this); - code.on_change = bind(&CheatEditorWindow::code_input, this); add_code.on_tick = bind(&CheatEditorWindow::add_tick, this); - toggle_code.on_tick = bind(&CheatEditorWindow::toggle_code_state, this); + edit_code.on_tick = bind(&CheatEditorWindow::edit_tick, this); delete_code.on_tick = bind(&CheatEditorWindow::delete_tick, this); - refresh(); + sync_ui(); + window_cheat_code_editor.setup(); +} + +void CheatEditorWindow::sync_ui() { + add_code.enable(cartridge.loaded()); + edit_code.enable(cartridge.loaded() && list.get_selection() >= 0); + delete_code.enable(cartridge.loaded() && list.get_selection() >= 0); +} + +string CheatEditorWindow::read_code(unsigned index) { + string s; + + Cheat::cheat_t item; + cheat.get(index, item); + s << (item.enabled ? translate["Enabled"] : translate["Disabled"]) << "\t"; + + lstring line; + split(line, "+", item.code); + if(count(line) > 1) line[0] << "+..."; + s << line[0] << "\t"; + + split(line, "\n", item.desc); + if(count(line) > 1) line[0] << " ..."; + s << line[0]; + + return s; } void CheatEditorWindow::refresh() { + if(config.misc.cheat_autosort == true) cheat.sort(); + list.reset(); - if(config::misc.cheat_autosort == true) cheat.sort(); - - for(unsigned i = 0; i < cheat.count(); i++) { - Cheat::cheat_t cheatcode; - cheat.get(i, cheatcode); - list.add_item(string() - << (cheatcode.enabled ? translate["Enabled"] : translate["Disabled"]) << "\t" - << cheatcode.code << "\t" - << cheatcode.desc); - } - + for(unsigned i = 0; i < cheat.count(); i++) list.add_item(read_code(i)); list.autosize_columns(); - //enable controls only if cartridge is loaded - bool loaded = cartridge.loaded(); - if(loaded == false) { - code.set_text(translate[""]); - desc.set_text(translate[""]); - } - code.enable(loaded); - desc.enable(loaded); - - add_code.enable(loaded && is_code_valid()); - toggle_code.disable(); //no list item will be selected; - delete_code.disable(); //so there's nothing to toggle / delete. -} - -bool CheatEditorWindow::is_code_valid() { - //input - char s_code[16]; - code.get_text(s_code, sizeof s_code); - //output - unsigned addr; - uint8_t data; - Cheat::type_t type; - return cheat.decode(s_code, addr, data, type); -} - -//enable "Add Code" button only when cheat code is valid -uintptr_t CheatEditorWindow::code_input(event_t) { - add_code.enable(is_code_valid()); - return true; + sync_ui(); } uintptr_t CheatEditorWindow::autosort_tick(event_t) { - config::misc.cheat_autosort = autosort.checked(); - if(config::misc.cheat_autosort == true) refresh(); + config.misc.cheat_autosort = autosort.checked(); + if(config.misc.cheat_autosort == true) refresh(); return true; } @@ -88,30 +77,32 @@ uintptr_t CheatEditorWindow::toggle_code_state(event_t) { int index = list.get_selection(); if(index >= 0 && index < cheat.count()) { cheat.enabled(index) ? cheat.disable(index) : cheat.enable(index); - Cheat::cheat_t cheatcode; - cheat.get(index, cheatcode); - list.set_item(index, string() - << (cheatcode.enabled ? translate["Enabled"] : translate["Disabled"]) << "\t" - << cheatcode.code << "\t" - << cheatcode.desc); + list.set_item(index, read_code(index)); } return true; } -//enables "Toggle Code" / "Delete Code" buttons when a code is selected +//enables "Edit Code" / "Delete Code" buttons when a code is selected uintptr_t CheatEditorWindow::list_change(event_t) { int index = list.get_selection(); - toggle_code.enable(index >= 0); + edit_code.enable(index >= 0); delete_code.enable(index >= 0); return true; } uintptr_t CheatEditorWindow::add_tick(event_t) { - char s_code[1024], s_desc[1024]; - code.get_text(s_code, sizeof s_code); - desc.get_text(s_desc, sizeof s_desc); - cheat.add(false, s_code, s_desc); //param 0 = false, meaning new codes are disabled by default - refresh(); + add_code.disable(); + edit_code.disable(); + delete_code.disable(); + window_cheat_code_editor.show(false); + return true; +} + +uintptr_t CheatEditorWindow::edit_tick(event_t) { + add_code.disable(); + edit_code.disable(); + delete_code.disable(); + window_cheat_code_editor.show(true, list.get_selection()); return true; } @@ -123,3 +114,125 @@ uintptr_t CheatEditorWindow::delete_tick(event_t) { } return true; } + +//===================== +//CheatCodeEditorWindow +//===================== + +uintptr_t CheatCodeEditorWindow::close(event_t) { + hide(); + window_cheat_editor.sync_ui(); + return false; +} + +uintptr_t CheatCodeEditorWindow::validate(event_t) { + char s_codes[1024]; + codes.get_text(s_codes, sizeof s_codes); + + string s_code = s_codes; + strtr(s_code, " ,;&|\t\n", "+++++++"); + while(strpos(s_code, "++") >= 0) replace(s_code, "++", "+"); + trim(s_code, "+"); + + Cheat::cheat_t item; + bool valid = cheat.decode(s_code, item); + + lvalid.set_text(valid ? "" : translate["Cheat code is invalid."]); + ok.enable(valid); + return true; +} + +uintptr_t CheatCodeEditorWindow::ok_tick(event_t) { + char s_desc[1024], s_codes[1024]; + description.get_text(s_desc, sizeof s_desc); + codes.get_text(s_codes, sizeof s_codes); + + string s_code = s_codes; + strtr(s_code, " ,;&|\t\n", "+++++++"); + while(strpos(s_code, "++") >= 0) replace(s_code, "++", "+"); + trim(s_code, "+"); + + if(active_mode == false) { + //add a new code + cheat.add(enabled.checked(), s_code, s_desc); + } else { + //modify an existing code + cheat.edit(active_code, enabled.checked(), s_code, s_desc); + } + + hide(); + window_cheat_editor.refresh(); + return true; +} + +uintptr_t CheatCodeEditorWindow::cancel_tick(event_t) { + hide(); + window_cheat_editor.sync_ui(); + return true; +} + +uintptr_t CheatCodeEditorWindow::clear_tick(event_t) { + description.set_text(""); + codes.set_text(""); + enabled.check(false); + return true; +} + +void CheatCodeEditorWindow::show(bool editmode, unsigned codenumber) { + active_mode = editmode; + active_code = codenumber; + + if(active_mode == false) { + set_text(translate["Add new cheat code"]); + description.set_text(""); + codes.set_text(""); + enabled.check(false); + } else { + Cheat::cheat_t item; + cheat.get(active_code, item); + + set_text(translate["Modify existing cheat code"]); + description.set_text(item.desc); + string s = item.code; + replace(s, "+", " + "); + codes.set_text(s); + enabled.check(item.enabled); + } + + enabled.focus(); + validate(event_t(event_t::Change)); + Window::show(); +} + +void CheatCodeEditorWindow::setup() { + create(Window::AutoCenter, 395, 235); + set_icon(48, 48, (uint32_t*)resource::icon48); + + ldescription.create(0, 385, 18, translate["Description:"]); + description.create(Editbox::Multiline | Editbox::HorizontalScrollNever, 385, 65); + lcodes.create(0, 385, 18, translate["Enter one or more cheat codes below:"]); + codes.create(Editbox::Multiline | Editbox::HorizontalScrollNever, 385, 65); + enabled.create(0, 190, 18, translate["Enable this cheat code"]); + lvalid.create(0, 190, 18); + ok.create(0, 125, 25, translate["Ok"]); + cancel.create(0, 125, 25, translate["Cancel"]); + clear.create(0, 125, 25, translate["Clear"]); + + unsigned y = 5; + attach(ldescription, 5, y); y += 18; + attach(description, 5, y); y += 65 + 5; + attach(lcodes, 5, y); y += 18; + attach(codes, 5, y); y += 65 + 5; + attach(enabled, 5, y); + attach(lvalid, 200, y); y += 18 + 5; + attach(ok, 5, y); + attach(cancel, 135, y); + attach(clear, 265, y); y += 25 + 5; + lvalid.disable(); + + on_close = bind(&CheatCodeEditorWindow::close, this); + codes.on_change = bind(&CheatCodeEditorWindow::validate, this); + ok.on_tick = bind(&CheatCodeEditorWindow::ok_tick, this); + cancel.on_tick = bind(&CheatCodeEditorWindow::cancel_tick, this); + clear.on_tick = bind(&CheatCodeEditorWindow::clear_tick, this); +} diff --git a/src/ui/settings/cheateditor.hpp b/src/ui/settings/cheateditor.hpp index 7d2c92ec..81c5882c 100644 --- a/src/ui/settings/cheateditor.hpp +++ b/src/ui/settings/cheateditor.hpp @@ -1,21 +1,48 @@ +//main editor window for all codes class CheatEditorWindow : public Window { public: Listbox list; Checkbox autosort; Button add_code; - Button toggle_code; + Button edit_code; Button delete_code; - Editbox code; - Editbox desc; + void sync_ui(); void setup(); + + string read_code(unsigned index); void refresh(); - bool is_code_valid(); uintptr_t toggle_code_state(event_t); uintptr_t list_change(event_t); - uintptr_t code_input(event_t); uintptr_t autosort_tick(event_t); uintptr_t add_tick(event_t); + uintptr_t edit_tick(event_t); uintptr_t delete_tick(event_t); } window_cheat_editor; + +//individual cheat code editor +class CheatCodeEditorWindow : public Window { +public: + Label ldescription; + Editbox description; + Label lcodes; + Editbox codes; + Checkbox enabled; + Label lvalid; + Button ok; + Button cancel; + Button clear; + + bool active_mode; + unsigned active_code; + + void show(bool editmode, unsigned codenumber = 0); + void setup(); + + uintptr_t close(event_t); + uintptr_t validate(event_t); + uintptr_t ok_tick(event_t); + uintptr_t cancel_tick(event_t); + uintptr_t clear_tick(event_t); +} window_cheat_code_editor; diff --git a/src/ui/settings/driverselect.cpp b/src/ui/settings/driverselect.cpp index fb5f0ec3..54611141 100644 --- a/src/ui/settings/driverselect.cpp +++ b/src/ui/settings/driverselect.cpp @@ -1,21 +1,21 @@ uintptr_t DriverSelectWindow::video_change(event_t) { lstring part; split(part, ";", video.driver_list()); - config::system.video = part[cvideo.get_selection()]; + config.system.video = part[cvideo.get_selection()]; return true; } uintptr_t DriverSelectWindow::audio_change(event_t) { lstring part; split(part, ";", audio.driver_list()); - config::system.audio = part[caudio.get_selection()]; + config.system.audio = part[caudio.get_selection()]; return true; } uintptr_t DriverSelectWindow::input_change(event_t) { lstring part; split(part, ";", input.driver_list()); - config::system.input = part[cinput.get_selection()]; + config.system.input = part[cinput.get_selection()]; return true; } @@ -36,7 +36,7 @@ void DriverSelectWindow::setup() { split(part, ";", video.driver_list()); for(unsigned i = 0; i < count(part); i++) { cvideo.add_item(translate[string() << "{{videodriver}}" << part[i]]); - if(part[i] == config::system.video) cvideo.set_selection(i); + if(part[i] == config.system.video) cvideo.set_selection(i); } laudio.create(0, 147, 18, translate["Audio driver:"]); @@ -45,7 +45,7 @@ void DriverSelectWindow::setup() { split(part, ";", audio.driver_list()); for(unsigned i = 0; i < count(part); i++) { caudio.add_item(translate[string() << "{{audiodriver}}" << part[i]]); - if(part[i] == config::system.audio) caudio.set_selection(i); + if(part[i] == config.system.audio) caudio.set_selection(i); } linput.create(0, 147, 18, translate["Input driver:"]); @@ -54,7 +54,7 @@ void DriverSelectWindow::setup() { split(part, ";", input.driver_list()); for(unsigned i = 0; i < count(part); i++) { cinput.add_item(translate[string() << "{{inputdriver}}" << part[i]]); - if(part[i] == config::system.input) cinput.set_selection(i); + if(part[i] == config.system.input) cinput.set_selection(i); } video_caps.create(0, 451, 18); @@ -85,17 +85,17 @@ void DriverSelectWindow::setup() { restart_message.create(0, 451, 36, translate["Note: bsnes must be restarted for changes to take effect."]); - bool crashed = config::system.invoke_crash_handler; + bool crashed = config.system.invoke_crash_handler; string t = translate["Capabilities of active video driver ($):"]; - replace(t, "$", config::system.video); + replace(t, "$", config.system.video); video_caps.set_text(t); video_sync.check(video.cap(Video::Synchronize)); video_filter.check(video.cap(Video::Filter)); t = translate["Capabilities of active audio driver ($):"]; - replace(t, "$", config::system.audio); + replace(t, "$", config.system.audio); audio_caps.set_text(t); audio_sync.check(audio.cap(Audio::Synchronize)); @@ -103,7 +103,7 @@ void DriverSelectWindow::setup() { audio_latency.check(audio.cap(Audio::Latency)); t = translate["Capabilities of active input driver ($):"]; - replace(t, "$", config::system.input); + replace(t, "$", config.system.input); input_caps.set_text(t); input_keyboard.check(input.cap(Input::KeyboardSupport)); diff --git a/src/ui/settings/inputconfig.cpp b/src/ui/settings/inputconfig.cpp index e0162b7d..3fd0cf5a 100644 --- a/src/ui/settings/inputconfig.cpp +++ b/src/ui/settings/inputconfig.cpp @@ -49,9 +49,9 @@ void InputConfigWindow::setup() { setkey.on_tick = bind(&InputConfigWindow::set_tick, this); clrkey.on_tick = bind(&InputConfigWindow::clr_tick, this); - if(config::input.capture_mode == 1) capture_focus.check(); - else if(config::input.capture_mode == 2) capture_pause.check(); - else config::input.capture_mode = 0; //capture_always + if(config.input.capture_mode == 1) capture_focus.check(); + else if(config.input.capture_mode == 2) capture_pause.check(); + else config.input.capture_mode = 0; //capture_always refresh_list(); window_input_capture.setup(); @@ -75,7 +75,7 @@ InputGroup* InputConfigWindow::get_group() { return device; } else { //user interface - switch(index) { + switch(index) { default: case 0: return &inputuigeneral; case 1: return &inputuidebugger; } @@ -150,9 +150,9 @@ void InputConfigWindow::refresh_list() { } uintptr_t InputConfigWindow::capture_change(event_t e) { - if(e.widget == &capture_always) config::input.capture_mode = 0; - if(e.widget == &capture_focus) config::input.capture_mode = 1; - if(e.widget == &capture_pause) config::input.capture_mode = 2; + if(e.widget == &capture_always) config.input.capture_mode = 0; + if(e.widget == &capture_focus) config.input.capture_mode = 1; + if(e.widget == &capture_pause) config.input.capture_mode = 2; return true; } @@ -250,6 +250,8 @@ uintptr_t InputCaptureWindow::close(event_t) { void InputCaptureWindow::setup() { create(Window::AutoCenter, 382, 206, translate["bsnes Key Capture"]); + set_icon(48, 48, (uint32_t*)resource::icon48); + label.create(0, 340, 18); canvas.create(0, 372, 178); memcpy(canvas.buffer(), resource::controller, 372 * 178 * 4); diff --git a/src/ui/settings/inputconfig.hpp b/src/ui/settings/inputconfig.hpp index e9a1e892..3db7735a 100644 --- a/src/ui/settings/inputconfig.hpp +++ b/src/ui/settings/inputconfig.hpp @@ -35,7 +35,7 @@ public: bool waiting; bool locked; - uint index; + unsigned index; void assign(uint16_t code); void show(); diff --git a/src/ui/settings/pathsettings.cpp b/src/ui/settings/pathsettings.cpp index ae6fc7f6..40908289 100644 --- a/src/ui/settings/pathsettings.cpp +++ b/src/ui/settings/pathsettings.cpp @@ -5,14 +5,14 @@ uintptr_t PathSettingsWindow::selectpath_rom(event_t) { char t[PATH_MAX]; if(hiro().folder_select(&window_settings, t) == true) { - config::path.rom = t; + snes.config.path.rom = t; update_paths(); } return true; } uintptr_t PathSettingsWindow::defaultpath_rom(event_t) { - config::path.rom = ""; + snes.config.path.rom = ""; update_paths(); return true; } @@ -24,14 +24,14 @@ uintptr_t PathSettingsWindow::defaultpath_rom(event_t) { uintptr_t PathSettingsWindow::selectpath_save(event_t) { char t[PATH_MAX]; if(hiro().folder_select(&window_settings, t) == true) { - config::path.save = t; + snes.config.path.save = t; update_paths(); } return true; } uintptr_t PathSettingsWindow::defaultpath_save(event_t) { - config::path.save = ""; + snes.config.path.save = ""; update_paths(); return true; } @@ -43,14 +43,14 @@ uintptr_t PathSettingsWindow::defaultpath_save(event_t) { uintptr_t PathSettingsWindow::selectpath_patch(event_t) { char t[PATH_MAX]; if(hiro().folder_select(&window_settings, t) == true) { - config::path.patch = t; + snes.config.path.patch = t; update_paths(); } return true; } uintptr_t PathSettingsWindow::defaultpath_patch(event_t) { - config::path.patch = ""; + snes.config.path.patch = ""; update_paths(); return true; } @@ -62,14 +62,14 @@ uintptr_t PathSettingsWindow::defaultpath_patch(event_t) { uintptr_t PathSettingsWindow::selectpath_cheat(event_t) { char t[PATH_MAX]; if(hiro().folder_select(&window_settings, t) == true) { - config::path.cheat = t; + snes.config.path.cheat = t; update_paths(); } return true; } uintptr_t PathSettingsWindow::defaultpath_cheat(event_t) { - config::path.cheat = ""; + snes.config.path.cheat = ""; update_paths(); return true; } @@ -81,14 +81,14 @@ uintptr_t PathSettingsWindow::defaultpath_cheat(event_t) { uintptr_t PathSettingsWindow::selectpath_export(event_t) { char t[PATH_MAX]; if(hiro().folder_select(&window_settings, t) == true) { - config::path.exportdata = t; + snes.config.path.exportdata = t; update_paths(); } return true; } uintptr_t PathSettingsWindow::defaultpath_export(event_t) { - config::path.exportdata = ""; + snes.config.path.exportdata = ""; update_paths(); return true; } @@ -98,19 +98,19 @@ uintptr_t PathSettingsWindow::defaultpath_export(event_t) { //============== void PathSettingsWindow::update_paths() { - if(config::path.rom != "") rompath.set_text(config::path.rom); + if(snes.config.path.rom != "") rompath.set_text(snes.config.path.rom); else rompath.set_text(translate[""]); - if(config::path.save != "") savepath.set_text(config::path.save); + if(snes.config.path.save != "") savepath.set_text(snes.config.path.save); else savepath.set_text(translate[""]); - if(config::path.patch != "") patchpath.set_text(config::path.patch); + if(snes.config.path.patch != "") patchpath.set_text(snes.config.path.patch); else patchpath.set_text(translate[""]); - if(config::path.cheat != "") cheatpath.set_text(config::path.cheat); + if(snes.config.path.cheat != "") cheatpath.set_text(snes.config.path.cheat); else cheatpath.set_text(translate[""]); - if(config::path.exportdata != "") exportpath.set_text(config::path.exportdata); + if(snes.config.path.exportdata != "") exportpath.set_text(snes.config.path.exportdata); else exportpath.set_text(translate[""]); } diff --git a/src/ui/settings/settings.cpp b/src/ui/settings/settings.cpp index 44373c6b..60cbcf2e 100644 --- a/src/ui/settings/settings.cpp +++ b/src/ui/settings/settings.cpp @@ -8,7 +8,7 @@ void SettingsWindow::setup() { panel_list.add_item(translate["Audio"]); panel_list.add_item(translate["Input"]); panel_list.add_item(translate["Paths"]); - panel_list.add_item(translate["Cheat Codes"]); + panel_list.add_item(translate["Cheat Codes"]); panel_list.add_item(translate["Advanced"]); attach(panel_list, 5, 5); @@ -25,8 +25,8 @@ void SettingsWindow::setup() { //if emulator crashed on last run, default to driver select window and disable list selection, //otherwise default to input configuration (most frequently used panel) - panel_list.set_selection(config::system.invoke_crash_handler == true ? 0 : 3); - panel_list.enable(config::system.invoke_crash_handler == false); + panel_list.set_selection(config.system.invoke_crash_handler == true ? 0 : 3); + panel_list.enable(config.system.invoke_crash_handler == false); list_change(event_t(event_t::Change)); } diff --git a/src/ui/settings/videosettings.cpp b/src/ui/settings/videosettings.cpp index 633bf798..8bf5c69d 100644 --- a/src/ui/settings/videosettings.cpp +++ b/src/ui/settings/videosettings.cpp @@ -1,123 +1,102 @@ void VideoSettingsWindow::setup() { create(0, 451, 370); - lcontrast.create (0, 451, 18); - contrast.create (0, 451, 30, 191); - lbrightness.create(0, 451, 18); - brightness.create (0, 451, 30, 191); - lgamma.create (0, 451, 18); - gamma.create (0, 451, 30, 191); - gamma_ramp.create (0, 223, 18, translate["Gamma ramp"]); - sepia.create (0, 223, 18, translate["Sepia"]); - grayscale.create (0, 223, 18, translate["Grayscale"]); - invert.create (0, 223, 18, translate["Invert colors"]); - sync_ui(); + lcontrast.create (0, 451, 18); + contrast.create (0, 451, 30, 191); + lbrightness.create (0, 451, 18); + brightness.create (0, 451, 30, 191); + lgamma.create (0, 451, 18); + gamma.create (0, 451, 30, 191); + gamma_ramp.create (0, 223, 18, translate["Simulate TV gamma ramp"]); + merge_fields.create(0, 223, 18, translate["Merge fields for NTSC filter"]); unsigned y = 0; - attach(lcontrast, 0, y); y += 18; - attach(contrast, 0, y); y += 30; - attach(lbrightness, 0, y); y += 18; - attach(brightness, 0, y); y += 30; - attach(lgamma, 0, y); y += 18; - attach(gamma, 0, y); y += 30; + attach(lcontrast, 0, y); y += 18; + attach(contrast, 0, y); y += 30; + attach(lbrightness, 0, y); y += 18; + attach(brightness, 0, y); y += 30; + attach(lgamma, 0, y); y += 18; + attach(gamma, 0, y); y += 30; - attach(gamma_ramp, 0, y); - attach(sepia, 228, y); y += 18; - attach(grayscale, 0, y); - attach(invert, 228, y); y += 18 + 5; + attach(gamma_ramp, 0, y); + attach(merge_fields, 228, y); y += 18; contrast.on_change = bind(&VideoSettingsWindow::contrast_change, this); brightness.on_change = bind(&VideoSettingsWindow::brightness_change, this); gamma.on_change = bind(&VideoSettingsWindow::gamma_change, this); gamma_ramp.on_tick = bind(&VideoSettingsWindow::gammaramp_tick, this); - sepia.on_tick = bind(&VideoSettingsWindow::sepia_tick, this); - grayscale.on_tick = bind(&VideoSettingsWindow::grayscale_tick, this); - invert.on_tick = bind(&VideoSettingsWindow::invert_tick, this); + merge_fields.on_tick = bind(&VideoSettingsWindow::merge_fields_tick, this); + + sync_ui(); } //update all UI controls to match config file values ... void VideoSettingsWindow::sync_ui() { - contrast.set_position(config::system.contrast + 95); + contrast.set_position(config.video.contrast + 95); lcontrast.set_text(string() << translate["Contrast adjust"] << ": " - << ((int)config::system.contrast > 0 ? "+" : "") - << (int)config::system.contrast << "%"); - brightness.set_position(config::system.brightness + 95); + << ((int)config.video.contrast > 0 ? "+" : "") + << (int)config.video.contrast << "%"); + brightness.set_position(config.video.brightness + 95); lbrightness.set_text(string() << translate["Brightness adjust"] << ": " - << ((int)config::system.brightness > 0 ? "+" : "") - << (int)config::system.brightness << "%"); - gamma.set_position(config::system.gamma - 5); + << ((int)config.video.brightness > 0 ? "+" : "") + << (int)config.video.brightness << "%"); + gamma.set_position(config.video.gamma - 5); lgamma.set_text(string() << translate["Gamma adjust"] << ": " - << ((int)config::system.gamma > 100 ? "+" : "") - << (int)(config::system.gamma - 100) << "%"); - gamma_ramp.check(config::system.gamma_ramp); - sepia.check(config::system.sepia); - grayscale.check(config::system.grayscale); - invert.check(config::system.invert); + << ((int)config.video.gamma > 100 ? "+" : "") + << (int)(config.video.gamma - 100) << "%"); - libfilter::colortable.set_contrast(config::system.contrast); - libfilter::colortable.set_brightness(config::system.brightness); - libfilter::colortable.set_gamma(config::system.gamma); - libfilter::colortable.enable_gamma_ramp(config::system.gamma_ramp); - libfilter::colortable.enable_sepia(config::system.sepia); - libfilter::colortable.enable_grayscale(config::system.grayscale); - libfilter::colortable.enable_invert(config::system.invert); + gamma_ramp.check(config.video.gamma_ramp); + merge_fields.check(config.video.ntsc_filter_merge_fields); + + libfilter::colortable.set_contrast(config.video.contrast); + libfilter::colortable.set_brightness(config.video.brightness); + libfilter::colortable.set_gamma(config.video.gamma); + libfilter::colortable.enable_gamma_ramp(config.video.gamma_ramp); + libfilter::colortable.enable_sepia(config.video.sepia); + libfilter::colortable.enable_grayscale(config.video.grayscale); + libfilter::colortable.enable_invert(config.video.invert); libfilter::colortable.update(); } uintptr_t VideoSettingsWindow::contrast_change(event_t) { - if(config::system.contrast != contrast.get_position() - 95) { - config::system.contrast = contrast.get_position() - 95; + if(config.video.contrast != contrast.get_position() - 95) { + config.video.contrast = contrast.get_position() - 95; sync_ui(); } return true; } uintptr_t VideoSettingsWindow::brightness_change(event_t) { - if(config::system.brightness != brightness.get_position() - 95) { - config::system.brightness = brightness.get_position() - 95; + if(config.video.brightness != brightness.get_position() - 95) { + config.video.brightness = brightness.get_position() - 95; sync_ui(); } return true; } uintptr_t VideoSettingsWindow::gamma_change(event_t) { - if(config::system.gamma != gamma.get_position() + 5) { - config::system.gamma = gamma.get_position() + 5; + if(config.video.gamma != gamma.get_position() + 5) { + config.video.gamma = gamma.get_position() + 5; sync_ui(); } return true; } uintptr_t VideoSettingsWindow::gammaramp_tick(event_t) { - if(config::system.gamma_ramp != gamma_ramp.checked()) { - config::system.gamma_ramp = gamma_ramp.checked(); + if(config.video.gamma_ramp != gamma_ramp.checked()) { + config.video.gamma_ramp = gamma_ramp.checked(); sync_ui(); } return true; } -uintptr_t VideoSettingsWindow::sepia_tick(event_t) { - if(config::system.sepia != sepia.checked()) { - config::system.sepia = sepia.checked(); - sync_ui(); - } - return true; -} - -uintptr_t VideoSettingsWindow::grayscale_tick(event_t) { - if(config::system.grayscale != grayscale.checked()) { - config::system.grayscale = grayscale.checked(); - sync_ui(); - } - return true; -} - -uintptr_t VideoSettingsWindow::invert_tick(event_t) { - if(config::system.invert != invert.checked()) { - config::system.invert = invert.checked(); +uintptr_t VideoSettingsWindow::merge_fields_tick(event_t) { + if(config.video.ntsc_filter_merge_fields != merge_fields.checked()) { + config.video.ntsc_filter_merge_fields = merge_fields.checked(); + libfilter::filter_ntsc.adjust(0, 0, 0, 0, 0, config.video.ntsc_filter_merge_fields); sync_ui(); } return true; diff --git a/src/ui/settings/videosettings.hpp b/src/ui/settings/videosettings.hpp index b27881f0..09be3112 100644 --- a/src/ui/settings/videosettings.hpp +++ b/src/ui/settings/videosettings.hpp @@ -8,9 +8,7 @@ public: Slider gamma; Checkbox gamma_ramp; - Checkbox sepia; - Checkbox grayscale; - Checkbox invert; + Checkbox merge_fields; void setup(); void sync_ui(); @@ -18,8 +16,7 @@ public: uintptr_t contrast_change(event_t); uintptr_t brightness_change(event_t); uintptr_t gamma_change(event_t); + uintptr_t gammaramp_tick(event_t); - uintptr_t sepia_tick(event_t); - uintptr_t grayscale_tick(event_t); - uintptr_t invert_tick(event_t); + uintptr_t merge_fields_tick(event_t); } window_video_settings; diff --git a/src/ui/status.cpp b/src/ui/status.cpp index c46c66b1..599ad43f 100644 --- a/src/ui/status.cpp +++ b/src/ui/status.cpp @@ -25,7 +25,7 @@ void Status::update() { ppu.status.frames_updated = false; unsigned max_framerate = snes.region() == SNES::NTSC ? 60 : 50; - switch(config::system.emulation_speed) { + switch(config.system.emulation_speed) { case 0: max_framerate = unsigned(0.50 * max_framerate); break; case 1: max_framerate = unsigned(0.75 * max_framerate); break; case 2: max_framerate = unsigned(1.00 * max_framerate); break; diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index de0f6a3e..100e89af 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -21,14 +21,14 @@ void ui_init() { resource::init(); //ruby can initialize with "", but driver selection window needs active driver name - if(config::system.video == "") config::system.video = video.default_driver(); - if(config::system.audio == "") config::system.audio = audio.default_driver(); - if(config::system.input == "") config::system.input = input.default_driver(); + if(config.system.video == "") config.system.video = video.default_driver(); + if(config.system.audio == "") config.system.audio = audio.default_driver(); + if(config.system.input == "") config.system.input = input.default_driver(); - if(config::system.invoke_crash_handler == false) { - video.driver(config::system.video); - audio.driver(config::system.audio); - input.driver(config::system.input); + if(config.system.invoke_crash_handler == false) { + video.driver(config.system.video); + audio.driver(config.system.audio); + input.driver(config.system.input); } window_main.setup(); @@ -47,11 +47,11 @@ void ui_init() { window_advanced.setup(); window_settings.setup(); - config::video.mode = (config::video.start_in_fullscreen_mode == false) ? 0 : 1; + config.video.mode = (config.misc.start_in_fullscreen_mode == false ? 0 : 1); event::update_opacity(); - event::update_video_settings(); //call first time to resize main window and update menubar + event::update_video_settings(); //call first time to resize main window and update menubar - if(config::system.invoke_crash_handler == true) { + if(config.system.invoke_crash_handler == true) { //application crashed during driver setup on last run ... //allow selection of new drivers, and then exit emulator. window_settings.show(); @@ -59,7 +59,7 @@ void ui_init() { while(hiro().pending()) hiro().run(); usleep(20 * 1000); } - config::system.invoke_crash_handler = false; + config.system.invoke_crash_handler = false; event::quit(); return; } @@ -68,21 +68,21 @@ void ui_init() { while(hiro().pending()) hiro().run(); //detect crashes during driver initialization - config::system.invoke_crash_handler = true; - config::config().save(config::bsnes_cfg); + config.system.invoke_crash_handler = true; + config.save(config.bsnes_cfg); video.set(Video::Handle, window_main.view.handle()); - video.set(Video::Synchronize, config::video.synchronize); + video.set(Video::Synchronize, config.video.synchronize); audio.set(Audio::Handle, window_main.handle()); - audio.set(Audio::Synchronize, config::audio.synchronize); - audio.set(Audio::Volume, config::audio.volume); - audio.set(Audio::Frequency, config::audio.output_frequency); - audio.set(Audio::Latency, config::audio.latency); + audio.set(Audio::Synchronize, config.audio.synchronize); + audio.set(Audio::Volume, config.audio.volume); + audio.set(Audio::Frequency, config.audio.output_frequency); + audio.set(Audio::Latency, config.audio.latency); input.set(Input::Handle, window_main.handle()); - input.set(Input::AnalogAxisResistance, config::input.analog_axis_resistance); + input.set(Input::AnalogAxisResistance, config.input.analog_axis_resistance); //sets Audio::Resample, Audio::ResampleOutputFrequency and Audio::ResampleInputFrequency - event::update_emulation_speed(config::system.emulation_speed); + event::update_emulation_speed(config.system.emulation_speed); video.init(); audio.init(); @@ -92,10 +92,10 @@ void ui_init() { audio.clear(); //if code has reached this point, driver initialized successfully - config::system.invoke_crash_handler = false; - config::config().save(config::bsnes_cfg); + config.system.invoke_crash_handler = false; + config.save(config.bsnes_cfg); - event::update_video_settings(); //call second time to update video class settings + event::update_video_settings(); //call second time to update video class settings //UI setup complete, hook input callbacks snesinterface.input_ready = bind(&MainWindow::input_ready, &window_main); diff --git a/test_hdma.smc b/test_hdma.smc deleted file mode 100644 index f6ad7f2f1ff437e32404186b36aa50803dcdc856..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 diff --git a/test_hdmasync.smc b/test_hdmasync.smc deleted file mode 100644 index c98bb433fe9bced7c01ebcafc1a1d83607cf5c08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 diff --git a/test_hdmatiming.smc b/test_hdmatiming.smc deleted file mode 100644 index 74ef35e229b721812894590471723f500cd0b9a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 G68JxZE=yAY diff --git a/test_irq.smc b/test_irq.smc deleted file mode 100644 index 40c8df20f7395099d9a8d6d5c0838c0f4d18bf08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 diff --git a/test_irq4200.smc b/test_irq4200.smc deleted file mode 100644 index 830d1234f1f19cd84c7c346ada5d386c403ad684..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 WAOHd&00JNY0w4eaAn<>Zz^?!x8`)3* diff --git a/test_irq4209.smc b/test_irq4209.smc deleted file mode 100644 index 19201d34119d34acdd4e11af1474eba3a6ecbc0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 diff --git a/test_nmi.smc b/test_nmi.smc deleted file mode 100644 index 90804d11bc469329341336b8792759c4b40d2c80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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