Compare commits

...

29 Commits
v078 ... v082

Author SHA1 Message Date
Tim Allen
e8775319c8 Update to v082 release.
byuu says:

This release features many substantial Game Boy emulation improvements
(all courtesy of Jonas Quinn), a new audio DSP class, and BPS patching
support.

Changelog (since v081):

- added new DSP audio engine; supports sample-averaging for the Game
  Boy's high frequency rate
- GB: MMM01 images with boot loader at bottom of ROM can now be loaded
- GB: EI is delayed one cycle; fixes Bubble Bobble [Jonas Quinn]
- GB: fixed window -7 offset behavior; fixes Contra 3 first boss [Jonas
  Quinn]
- GB: disable LCD interrupts when rendering is off; fixes Super Mario
  Land 2 [Jonas Quinn]
- GB: fixed noise channel LFSR; fixes Zelda: LA lightning sound [Jonas
  Quinn]
- GB: square channels use initial_length like the noise channel [Jonas
  Quinn]
- UI: added BPS patching support; removed UPS patching support
- UI: when loading BS-X/Sufami Turbo/Game Boy games; display game title
  instead of BIOS title
- UI: modified timestamps on screenshots for Windows/NTFS (which
  disallows use of ':')
2011-08-21 01:02:27 +10:00
Tim Allen
095181af62 Update to v081r04 release.
byuu says:

- GB: square channels cache initial_length and invert the length value
  on writes [Jonas Quinn]
- GB: updated LCD disable to just ignore interrupts and pixel writes,
  fixes Contra
- BPS patch information and cartridge sections are now copied from the
  patch metadata, if it is present
- fixed bpslinear out-of-bounds issue, which will be in snespurify v11
  official [Danish]
- simplified Makefile again since command-line trumps manual assignments
2011-08-19 21:36:26 +10:00
Tim Allen
b28c54770c Update to v081r03 release.
byuu says:

- GameBoy: fixed window behavior for Contra 3 first boss [Jonas Quinn]
- GameBoy: fixed noise channel for Zelda: LA intro [Jonas Quinn]
- GameBoy: completely disable LCD when turned off, no interrupts; fixes
  Super Mario Land 2 [thanks to Jonas Quinn]
- GameBoy: keep track of where pixels come from for OBJ priority mode
  [Jonas Quinn]
- updated mode+slot-dependent name handling: simplifies Path class,
  allows SGB/BSX/ST games to show the slot title (instead of BIOS name)
  on the title bar
- Makefile allows command-line definitions for ui and profile now (make
  profile=compatibility ui=ui-libsnes)
- Makefile now allows (make pgo=instrument) and (make pgo=optimize)
- added BPS patching support, removed UPS patching support
2011-08-18 23:58:27 +10:00
Tim Allen
71763f2d98 Update to v081r02 release.
byuu says:

This release adds nall/dsp, which is a new framework for audio DSP
effects. It currently supports the usual fractional hermite resampling
and volume adjustments from ruby; but it also adds balance, and
arbitrary precision input and output (still limited to two channels, and
still signed audio [just subtract 1<<(bits-1) from an unsigned value.])
Internally, all samples are converted to doubles in the range of -1.0 to
+1.0, to allow for far greater precision with the hermite resampler and
volume/balance/etc adjustments.

As a result of this, all of the extra resampling/volume code from
ruby::audio has been removed. bsnes pulls in a copy of nall::dsp to
handle that stuff now.
2011-08-14 20:34:11 +10:00
Tim Allen
423d9ba00d Update to v081r01 release.
byuu says:

- EI takes an extra cycle to raise IME; fixes Bubble Bobble [for the
  Gameboy], and likely many more games [Jonas Quinn]
- nall/gameboy/cartridge.hpp descrambles MMM01 images (header can be at
  top or bottom of image now)
- screenshot timestamps use filename-yyyy-mm-dd hh.mm.ss.bmp format; so
  that they can save on Windows
- closing the emulator will switch to windowed mode first, so that
  geometry is preserved
2011-08-13 13:51:29 +10:00
Tim Allen
064ca4c626 Update to v081 release.
byuu says:

This release polishes up the GUI, adds some more features, and fixes
a few minor issues.

Changelog (since v080):
- rewrote S-DD1 module to decompress individual bytes at a time
- simplified SPC7110 deinterleaving code
- OBC1 should not clear RAM on reset [Jonas Quinn]
- fixed enum-cast compilation errors with the latest GCC 4.6.x releases
- added bsnes logo to about screen
- make phoenix=gtk will now build the GTK+ port on Linux
- added settings.startFullScreen to the config file for front-end users
- added advanced settings option to disable window compositor (only
  works on Windows and Xfce)
- merged settings windows into the panel approach used by bsnes/Qt in
  the past
- fixed a crashing bug on input settings window
- fixed GTK+ auto-geometry sizing
- added screenshot capture capability
- added exit emulator hotkey (defaults to being unmapped)
- Xorg keyboard polling now uses cached Display variable [Bisqwit]
- cheat code database updated [mightymo]
2011-08-12 22:33:07 +10:00
Tim Allen
10906d8418 Update to v080r08 release.
byuu says, in a post between the v080r07 release and the v080r08
release:

phoenix/Windows:

The slider and scrollbar setParent calls setLength+setPosition, but
setLength sets position = 0 (due to new length possibly invalidating
previous position.)
Cache position first to fix this, can now reparent widgets with proper
slider/scroll positions.

ListView had a workaround where the horizontal scrollbar was always
appearing on single-column lists. The workaround was forcing the config
settings list in bsnes to only select the text portions of each item in
the list, instead of the entire lines. The workaround was needed because
without setting a single header text, the header text count was equal to
zero, causing autoSizeColumns to have no effect. Made the constructor
call setHeaderText("") to guarantee size() >= 1 always. Removes the need
for the workaround, and gives a good file browser and configuration
setting window.

phoenix/Qt:

Worked around Qt bugs #258,674+258,675: if you click a list item with
your mouse, currentItem()->isSelected() returns false. It does not
return true until you select an item with a keyboard key. I forced it to
set the selected item upon currentItemChanged() message. It was also not
sending a changed message upon clearing the selection and then selecting
the same item again. I had to do something undocumented:
setCurrentItem(nullptr) so that currentItemChanged works again.

phoenix/All:

Fonts are now initialized to the platform default settings, Tahoma or
Sans 8-point. This lets geometry on widgets not attached to windows work
better. Makes the ../... buttons smaller pretty much everywhere.

byuu says, announcing the v080r08 release:

Fixed all of the above phoenix issues, and improved the auto-disabling
of buttons on the input setting and state manager windows.
Also manually initiailized lastConfigure for Valgrind in GTK+. Windows
and GTK+ ports look a lot nicer now.
2011-08-08 22:04:47 +10:00
Tim Allen
e88ab60663 Update to v080r07 release.
byuu says:

- fixed a long-standing crash: when you have a device index above the
  range permitted by another port, the app would crash (eg Controller
  Port 2 -> Mouse, then switch to Hotkeys)
- Qt bug workaround: have to use currentItemChanged signal instead of
  itemSelectionChanged signal for QTreeWidget, otherwise scrolling with
  mouse gives you the previous item with currentItem() ...
- added support for toggling the Xfce compositor
- added support for detecting if the compositor is enabled in the first
  place on Windows, so that it won't get turned on when you had it off
  permanently
- added advanced setting to toggle behavior (never disable, disable only
  in fullscreen mode, disable whenever emulator is open)
- worked around GTK+ ../... button height issue
- worked around Windows slider position issue when attaching to a new
  window (need to research this one more)
- fixed up input settings window more: closing window ends assignment,
  custom mapping buttons hidden by default

Some of those bugs have been there since the phoenix port began, good
times.
2011-08-08 22:02:51 +10:00
Tim Allen
564e38ea9f Update to v080r06 release.
byuu says:

Ran out of time, so this is incomplete, but ...

Windows will disable the compositor in fullscreen mode, and enable it
when switching back to windowed mode. Should help with Vsync issues, but
of course only in fullscreen mode.

I've also merged the four settings windows back into a panel with a list
view (since I have no tab control widget.) The input settings window is
a bit incomplete, need to break assignment on window close, hide the
capture buttons on first showing, etc. Will probably try and finish that
up tonight.
2011-08-08 22:01:09 +10:00
Tim Allen
0c3f0834ab Update to v080r05 release.
byuu says:

Includes updated versions of nall and phoenix, which mostly improves the
GTK+ version. However, it appears to be crashing at the moment after
loading a game. Unfortunately it works when gdb is used, so I can't
easily debug it :/

You can now build with make phoenix=gtk if you want the GTK+ version on
Linux (the Qt version is leagues better even on Gnome, please use it if
at all possible.)

There's also settings.startFullScreen, config-file only, to allow for
front-end use. Forgot to add the reset/power hotkeys.

I also fixed compilation of ui-gameboy on GCC 4.6. I hope that's the
last switch(enum) error, those are damn annoying. Can't wait to switch
to GCC 4.6 on Windows.
2011-08-07 00:03:52 +10:00
Tim Allen
f38af85e0a Update to v080r04 release.
byuu says:

Adds nall/inflate.hpp and nall/unzip.hpp. Updates nall/resource.hpp to
encode and decode using ZIP/deflate files, rather than a much simpler
(and less powerful) LZSS implementation. Cuts the bsnes-logo.hpp file
from 270KB to 130KB, and the binary overhead from 80KB to 35KB.
2011-07-24 23:51:01 +10:00
Tim Allen
8276700381 Update to v080r03 release.
byuu says:

Wow, nothing in 19 days. Anyway, I wanted to get Nick's logo back in on
the about screen. Adds 80kb to both the binary and source archive, but
eh. Gotta have some style. Nothing else new.
2011-07-23 20:14:47 +10:00
Tim Allen
ec69109c0b Update to v080r02 release.
byuu says:

- added qstrlower and qstrupper; mainly for the sake of others wanting
  to patch bass
- added: string sha256(const uint8_t *data, unsigned size); for easier
  hash generation
- cleaned up the NEC DSP and Hitachi DSP XML mapping code; they are
  consistent now as well
- "necdsp" in paths.cfg is now "firmware", since it also affects the
  Hitachi DSP
- XML mapping was using program= for DSP-n/ST-001n and data= for Cx4;
  they both use firmware= now instead
- fixed icd2/interface casting issue for GCC 4.6.0 (thanks for the
  reminder, vEX)
- removed the last parts of code that used string << foo; and removed
  that from nall/string entirely
  - I need to do this for the debugger as well, I'll make sure that it
    compiles before v081 though
- converted all string(...) syntax to { ... } syntax that I could
  (obviously it won't cast to a function that takes const char* instead
  of const string&)

Probably some other tiny things. Just basic maintenance here.
2011-07-07 22:59:26 +10:00
Tim Allen
8ae6444af7 Update to v080r01 release.
byuu says:

There was one unfortunate aspect of the S-DD1 module: you had to give it
the DMA length and a target buffer, and it would do the entire
decompression at once. Real hardware would work by streaming the data
byte by byte. So with that, I went ahead and rewrote the code to handle
byte-based streaming.

This WIP is an important milestone for me personally. Up until now,
bsnes has always had code that was directly copy-pasted from other
authors. With all of the DSP and Cx4 chips rewritten in LLE, and the
SPC7110 algorithm already ported over from C, and archive decompression
code removed for a long time, the S-DD1 was the only module left like
this. It's obviously not that big of a deal. The code is basically still
a copy of the original. S-DD1 decomp from Andreas Naive, SPC7110 decomp
from neviksti, and S-DSP from blargg. And the rest of the emulator is of
course only possible because of code and research before it, although
everything else has no resemblance at all to code before it. The main
advantage, really, is absolute code consistency. I always use the same
variant of K&R, for instance. I dunno, I guess I just never really liked
the "Build-a-Bear Workshop" style of emulators, like is so prominent in
the Genesis scene: "My new Genesis emu (uses Starscream/Musashi 68K
core, Marat Fayzullin's Z80 core, YM2612 core from Game_Music_Emu, VDP
core from Gens, SVP core from picodrive)", sorry, but you wrote
a front-end, not an emulator :/

I also updated the SPC7110 decompression module: I merged the class
inside the SPC7110 class (not sure why it was separate before), and
replaced the morton lookup tables with for-loops. The morton tables were
added to be a tiny bit faster when I was more interested in speed than
code clarity. It may be a tiny bit slower (or faster due to less L2
cache usage), but you won't even notice an FPS drop, and it cuts out
a good chunk of code and some tables. Lastly, I added pinput_poll() to
video_refresh(). Forgot to remove Interface::input_poll() from the C++
side, will have to do that later.
2011-06-28 21:36:00 +10:00
Tim Allen
5fc86eae6d Update to v080 release.
byuu says:

This release adds low-level emulation of the Hitachi HG51B169 DSP, which
was used in Mega Man X2 and Mega Man X3 as the Cx4 chip. It also fixes
a regression in both the sound core and cheat engine.

You will now need the HG51B169 data ROM to play MMX2/MMX3.

Once again, Cx4 LLE could not have been possible without the help of Dr.
Decapitator, Jonas Quinn, Overload and Segher. Be sure to thank them,
please!

Changelog:
* added Cx4 low-level emulation; removed Cx4 high-level emulation code
* fixed S-SMP synchronization to S-CPU on CPUIO writes
* controllers now have their own threads and classes
* serial controller is now emulated as an actual controller, rather than
  as a coprocessor
* added link coprocessor module for special chip research and homebrew
* fixed cheat codes that target mask ROM addresses [Cydrak]
* fixed compilation error with the latest GCC 4.6.0 beta releases
* added flexibility to XML memory mapping file format
* updated to mightymo's latest cheat pack (2011-06-20)
2011-06-26 22:51:37 +10:00
Tim Allen
927c97eb06 Update to v079r06 release.
byuu says:

It does add some more code to the CPU::step() function, so performance
probably went down actually, by about 1%. Removing the input.tick() call
didn't compensate as much as I'd hoped.
Hooked up Super Scope and Justifier support. The good news is that the
Justifier alignment doesn't get fucked up anymore when you go
off-screen. Never could fix that in the old version.
The bad news is that it takes a major speed hit for the time being.
I need to figure out how to run the CPU and input threads out of order.
Every time I try, the input gets thrown off by most of a scanline.
Right now, I'm forced to sync constantly to get the latching position
really accurate. But worst case, I can cut the syncs down by skipping
large chunks around the cursor position, +/-40 clock cycles. So it's
only temporarily slow.
Lastly, killed the old Input class, merged Controllers class into it.
I actually like Controllers as a name better, but it doesn't jive with
video/audio/input, so oh well.
2011-06-25 22:56:32 +10:00
Tim Allen
cf09d41669 Update to v079r05 release.
byuu says:

- Fixed GCC-4.6 casting errors in ui/input/input.cpp.
- Fixed some of the opcode mnemonics specified in the HG51B169 core (was
  unable to speed up the code)
- Started on a new core input system: snes/controller. More on that
  here:

    http://board.byuu.org/viewtopic.php?f=16&t=1761

- Have not yet attempted to add threading support to the controllers, so
  serial is still there as a coprocessor.
- I'm going to move the Controllers {} class back to Input {} once all
  individual controllers have been ported over.

Note: Super Scope and Justifier do not have counter latching support
yet, so you can't really use them. The gamepad, multitap and mouse all
work great; and the SS/Justifier cursors work at least. I also colored
the SS cursor red, so that all three (SS, Justifier, chained secondary
Justifier) all have unique R/G/B colors now. Should prevent confusion
between the SS and one Justifier.
2011-06-24 20:43:29 +10:00
Tim Allen
724747ac9e Update to v079r04 release.
byuu says:

Back from vacation. We were successful in emulating the Cx4 using LLE
during my vacation. We finished on June 15th. And now that I'm back,
I've rewritten the code and merged it into bsnes official. With that,
the very last HLE emulation code in bsnes has now been purged.

[...]

The emulation is as minimal as possible. If I don't see an opcode or
feature actually used, I don't implement it. The one exception being
that I do support the vector override functionality. And there are also
dummy handlers for ld ?,$2e + loop, so that the chip won't stall out.
But things like "byte 4" on rdram/wrram, the two-bit destination
selections for all but ld, etc are treated as invalid opcodes, since we
aren't 100% sure if they are there and work as we hypothesize. I also
only map in known registers into the 256-entry register list. This
leaves 90% of the map empty.

The chip runs at 20MHz, and it will disable the ROM while running. DMA
does transfer one byte at a time against the clock and also locks out
the ROM. rdbus won't fetch from IRAM, only from ROM. DMA transfer only
reads from ROM, and only writes to RAM. Unless someone verifies that
they can do more, I'll leave it that way. I don't yet actually buffer
the program ROM into the internal program RAM just yet, but that is on
the to-do list. We aren't entirely sure how that works either, but my
plan is to just lock the Cx4 CPU and load in 512-bytes.

There's still a few unknown registers in $7f40-5f that I don't do
anything with yet. The secondary chip disable is going to be the
weirdest one, since MMX3 only has one chip. I'd really rather not have
to specify the ROM mapping as two separate chips on MMX2 and as one on
MMX3 just to support this, so I don't know yet.

Save state support is of course there already.

Speed hit is 118fps HLE -> 109fps LLE in most scenes. Not bad, honestly.
2011-06-22 23:27:55 +10:00
Tim Allen
e1e275eb38 Update to v079r03 release.
byuu says:

This fixes the S-SMP synchronization on CPUIO writes that was broken by
improvements in v078.01. Terranigma will work now. Also adds the 'link'
coprocessor module that was added in v079.01, and improved in v079.02.
2011-06-13 22:26:48 +10:00
Tim Allen
e30fcade43 Update to v079r02 release.
byuu says:

Added "unsigned link_run();" which acts as its own thread synchronized
against the S-CPU. Specify the frequency in the configuration file.
I intend to prototype the Cx4 LLE openly using the link module, and that
required timing support, so there we go.

It's very basic, and it synchronizes the CPU to the coprocessors and
vice versa after every call to link_run(). Meaning performance won't be
super exceptional at full 21MHz or higher, but then this is for
prototyping only. I didn't want to expose cothreading, yielding, calls
back into bsnes' core, calls to sync up the S-CPU, etc.
2011-06-13 22:22:06 +10:00
Tim Allen
42dbf73d18 Update to v079r01 release.
This version adds a "link" SNES coprocessor module, which just loads
a shared library. It was posted outside the v079 WIP thread, in this
thread:

    http://board.byuu.org/viewtopic.php?f=16&t=1700
2011-06-13 22:13:30 +10:00
Tim Allen
2a90e12999 Update to v079 release.
byuu says:

This release includes Nintendo Super System DIP switch emulation and
improved PPU rendering accuracy, among other things.

Changelog:
- added Nintendo Super System DIP switch emulation [requires XML setting
  maps]
- emulated Super Game Boy $6001 VRAM offset selection port [ikari_01]
- fixed randomness initialization of S-SMP port registers [fixes
  DBZ:Hyper Dimension and Ninja Warriors]
- mosaic V-countdown caches BGOFS registers (fixes Super Turrican
  2 effect) [reported by zal16]
- non-mosaic BGOFS registers are always cached at H=60 (fixes NHL '94
  and Super Mario World flickering)
- fixed 2xSaI family of renderers on 64-bit systems
- cleaned up SMP source code
- phoenix: fixed a bug when closing bsnes while minimized

Please note that the mosaic BGOFS fix is only for the accuracy profile.
Unfortunately the older scanline-based compatibility renderer's code is
nearly unmaintainable at this point, so I haven't yet been able to
backport the fixes.

Also, I have written a new cycle-accurate SMP core that does not use
libco. The aim is to implement it into Snes9X v1.54. But it would of
course be prudent to test the new core first.

[...then in the next post...]

Decided to keep that Super Mario World part a surprise, so ... surprise!

Realized while working on the Super Turrican 2 mosaic fix, and from
looking at NHL '94 and Dai Kaijuu Monogatari 2's behavior, that BGOFS
registers must be cached between H=0 and H=88 for the entire scanline
... they can't work otherwise, and it'd be stupid for the PPU to re-add
the offset to the position on every pixel anyway. I chose H=60 for now.
Once I am set up with the RGB monitor and the North American cartridge
dumping is completed, I'll set it on getting exact timings for all these
things. It'll probably require a smallish speed hit to allow exact-cycle
timing events for everything in the PPU.
2011-06-05 13:45:04 +10:00
Tim Allen
d129b72ced Update to v078r07 release.
byuu says:

Would appreciate testing on any games with mosaic, especially Mode7
mosaic.I have tested Super Turrican 2, Sim Earth, Contra III and SNES
Test Program.

This only applies to BG modes 0-6, and technically should not affect
Mode7 at all. I am not sure if Mode7 needs the same change made or not,
but given the way it fetches that could prove quite challenging. I also
simplified the background renderer a good bit. See eg the pixel copy
stuff in Background::run().

I've only fixed this in the accuracy renderer. I'm sorry, but the
compatibility renderer is a fucking mess. I haven't really touched it in
four or five years now.

Will probably just revert to the accuracy/SMP in the performance profile
for the next release since it's not being used otherwise. People can
toggle it on if they want to try it out.
2011-06-05 13:25:24 +10:00
Tim Allen
bc0b86891a Update to v078r06 release.
byuu says:

This adds ikari_01's emulation of the ICD2 (Super Game Boy) $6001 register.

It basically removes a really ugly hack where I was intercepting the DMA
transfer destination address to determine while Game Boy tile row to
transfer.  This should make implementing SGB emulation in other
emulators easier, as said hooks were very emulator-specific.
2011-05-08 23:46:37 +10:00
Tim Allen
52443936e6 Update to v078r05 release.
byuu says:

This WIP adds Nintendo Super System emulation, at least of its DIP
switches.  This is done via XML mapping, like so:

    <?xml version="1.0" encoding="UTF-8"?>
    <cartridge region="NTSC">
      <name>ActRaiser</name>
      <rom>
	<map mode="linear" address="00-7f:8000-ffff"/>
	<map mode="linear" address="80-ff:8000-ffff"/>
      </rom>
      <nss>
	<setting name="Difficulty">
	  <option value="0000" name="Easy"/>
	  <option value="0001" name="Normal"/>
	  <option value="0002" name="Hard"/>
	  <option value="0003" name="Expert"/>
	</setting>
	<setting name="Lives">
	  <option value="0000" name="5 lives"/>
	  <option value="0004" name="4 lives"/>
	  <option value="0008" name="3 lives"/>
	  <option value="000c" name="2 lives"/>
	</setting>
      </nss>
    </cartridge>

The value field is a 16-bit value. All selected options are ORed
together to produce the final DIP switch values.  The number of options
per setting is unlimited, but there are only sixteen settings allowed
(you can't have more settings than you have switches, that's just
stupid.)

In the example above, d0-d1 controls difficulty, and d2-d3 controls # of
lives. d4-d15 appear to be unused, as far as I can tell.
2011-05-07 00:16:46 +10:00
Tim Allen
6694a1c986 Update to v078r04 release.
byuu says:

Changelog:
- file and slot load dialogs should now have perfectly square buttons
  that are based on the platform's default button height.
- cleaned up bsnes/Accuracy SMP source code (removed old !! stuff, stage
  3 timer is now uint4, memory access switch/case cleaned up,
  sSMPTimer->Timer, etc.)
- cleaned up bsnes/Accuracy memory access functions (read/writestack ->
  read/writesp, read/writeaddr -> read/write)
- minor optimization to bsnes/Performance SMP core in cycle-mode
2011-05-05 21:40:22 +10:00
Tim Allen
7ffaeb2ac1 Update to v078r03 release.
byuu says:

I apparently wasted two days writing that SMP core for nothing.  I had
a perfectly well-written and well-tested core in bsnes v045.  The old
opcode.b files that were a cycle-based markup language.

So I took that core, and wrote new parsers to generate both opcode-based
(one switch) and cycle-based (two switch) cores. Throw in a
little #define magic around CYCLE_ACCURATE, and it is compile-time
toggleable.

EWJ2's bug was due to not resetting the timer variables, and Bahamut
Lagoon's was due to dividing timer frequencies by 3, but failing to
remove the 0->1 transition phase (should have done the latter and
divided by two.)

Anyway, all fixed up.
2011-05-05 21:37:46 +10:00
Tim Allen
67e6a6e742 Update to v078r02 release.
byuu says:

New S-SMP core is feature-complete, but still buggy.  It's good enough
for perfect audio in Zelda 3 and Super Mario World, but there are plenty
of issues.  No audio in Bahamut Lagoon, deadlock in Earthworm Jim 2,
etc.

With this core, bsnes/Performance runs about 3-5% faster than with the
old one. That won't seem like much, because the S-SMP is the least
demanding portion of the SNES.  blargg's SMP core netted me a 5-8%
speedup the last time I tried it, so I'm sure there's still room to
speed things up.

The core is opcode-based, but has dummy op_io() calls (they compile to
nothing), so it is trivial to make it cycle-based if desired.  I'm not
convinced that is necessary, but we shall see once we get the opcode
bugs ironed out.
2011-05-03 19:58:12 +10:00
Tim Allen
9a3650c6ab Update to v078r01 release.
byuu says:

Started on a new SMP core for bsnes/Performance. I wanted to start
clean, and only copied over the debugger+disassembler portions from the
existing version.  I figured that if I took the existing one and tried
trimming it down, that it'd end up with too much old baggage.  But so
far, the opcodes are looking mostly the same anyway, only I'm
using #defines and a switch table in place of the template function
trickery.

I have enough written now that I can run Zelda 3 at least (although it
gets stuck in a loop immediately after.) No real point in comparing
speed yet, because it'll definitely go down as it becomes more complete.
2011-05-02 23:53:16 +10:00
493 changed files with 26171 additions and 14585 deletions

View File

@@ -1,11 +1,11 @@
include nall/Makefile
snes := snes
gameboy := gameboy
profile := accuracy
ui := ui
# debugger
options :=
# options += debugger
# compiler
c := $(compiler) -std=gnu99
@@ -14,12 +14,16 @@ flags := -O3 -fomit-frame-pointer -I.
link :=
objects := libco
# profile-guided instrumentation
# flags += -fprofile-generate
# link += -lgcov
# profile-guided optimization mode
# pgo := instrument
# pgo := optimize
# profile-guided optimization
# flags += -fprofile-use
ifeq ($(pgo),instrument)
flags += -fprofile-generate
link += -lgcov
else ifeq ($(pgo),optimize)
flags += -fprofile-use
endif
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)

1228
bsnes/data/bsnes-logo.hpp Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,7 @@ void APU::Noise::run() {
period = divisor << frequency;
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << 14);
if(narrow_lfsr) lfsr |= (bit << 6);
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
}
}
@@ -31,7 +30,6 @@ void APU::Noise::clock_envelope() {
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) {
initial_length = 64 - (data & 0x3f);
length = initial_length;
}
@@ -46,7 +44,6 @@ void APU::Noise::write(unsigned r, uint8 data) {
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
}
@@ -64,7 +61,6 @@ void APU::Noise::write(unsigned r, uint8 data) {
}
void APU::Noise::power() {
initial_length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
@@ -74,6 +70,7 @@ void APU::Noise::power() {
counter = 0;
output = 0;
initial_length = 0;
length = 0;
envelope_period = 0;
volume = 0;
@@ -82,7 +79,6 @@ void APU::Noise::power() {
}
void APU::Noise::serialize(serializer &s) {
s.integer(initial_length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
@@ -92,6 +88,7 @@ void APU::Noise::serialize(serializer &s) {
s.integer(counter);
s.integer(output);
s.integer(initial_length);
s.integer(length);
s.integer(envelope_period);
s.integer(volume);

View File

@@ -1,5 +1,4 @@
struct Noise {
unsigned initial_length;
unsigned envelope_volume;
bool envelope_direction;
unsigned envelope_frequency;
@@ -9,6 +8,7 @@ struct Noise {
bool counter;
int16 output;
unsigned initial_length;
unsigned length;
unsigned envelope_period;
unsigned volume;

View File

@@ -66,7 +66,8 @@ void APU::Square1::write(unsigned r, uint8 data) {
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
initial_length = 64 - (data & 0x3f);
length = initial_length;
}
if(r == 2) {
@@ -85,6 +86,7 @@ void APU::Square1::write(unsigned r, uint8 data) {
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
frequency_shadow = frequency;
@@ -102,6 +104,7 @@ void APU::Square1::power() {
sweep_direction = 0;
sweep_shift = 0;
duty = 0;
initial_length = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
@@ -125,6 +128,7 @@ void APU::Square1::serialize(serializer &s) {
s.integer(sweep_direction);
s.integer(sweep_shift);
s.integer(duty);
s.integer(initial_length);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);

View File

@@ -3,6 +3,7 @@ struct Square1 {
unsigned sweep_direction;
unsigned sweep_shift;
unsigned duty;
unsigned initial_length;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;

View File

@@ -33,7 +33,8 @@ void APU::Square2::clock_envelope() {
void APU::Square2::write(unsigned r, uint8 data) {
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
initial_length = 64 - (data & 0x3f);
length = initial_length;
}
if(r == 2) {
@@ -52,6 +53,7 @@ void APU::Square2::write(unsigned r, uint8 data) {
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
}
@@ -62,6 +64,7 @@ void APU::Square2::write(unsigned r, uint8 data) {
void APU::Square2::power() {
duty = 0;
initial_length = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
@@ -79,6 +82,7 @@ void APU::Square2::power() {
void APU::Square2::serialize(serializer &s) {
s.integer(duty);
s.integer(initial_length);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);

View File

@@ -1,5 +1,6 @@
struct Square2 {
unsigned duty;
unsigned initial_length;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;

View File

@@ -28,7 +28,6 @@ void APU::Wave::write(unsigned r, uint8 data) {
if(r == 1) {
initial_length = 256 - data;
length = initial_length;
}
@@ -68,7 +67,6 @@ void APU::Wave::write_pattern(unsigned p, uint8 data) {
void APU::Wave::power() {
dac_enable = 0;
initial_length = 0;
volume = 0;
frequency = 0;
counter = 0;
@@ -78,6 +76,7 @@ void APU::Wave::power() {
output = 0;
enable = 0;
initial_length = 0;
length = 0;
period = 0;
pattern_offset = 0;
@@ -86,7 +85,6 @@ void APU::Wave::power() {
void APU::Wave::serialize(serializer &s) {
s.integer(dac_enable);
s.integer(initial_length);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
@@ -94,6 +92,7 @@ void APU::Wave::serialize(serializer &s) {
s.integer(output);
s.integer(enable);
s.integer(initial_length);
s.integer(length);
s.integer(period);
s.integer(pattern_offset);

View File

@@ -1,6 +1,5 @@
struct Wave {
bool dac_enable;
unsigned initial_length;
unsigned volume;
unsigned frequency;
bool counter;
@@ -8,6 +7,7 @@ struct Wave {
int16 output;
bool enable;
unsigned initial_length;
unsigned length;
unsigned period;
unsigned pattern_offset;

View File

@@ -55,7 +55,7 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
void Cartridge::MMM01::power() {
rom_mode = 0;
rom_base = 0x00;
rom_base = 0;
ram_enable = false;
rom_select = 0x01;

View File

@@ -580,7 +580,8 @@ void CPU::op_di() {
}
void CPU::op_ei() {
status.ime = 1;
status.ei = true;
//status.ime = 1;
}
//jump commands

View File

@@ -114,6 +114,7 @@ void CPU::power() {
status.clock = 0;
status.halt = false;
status.stop = false;
status.ei = false;
status.ime = 0;
status.p15 = 0;

View File

@@ -17,6 +17,7 @@ struct CPU : Processor, MMIO {
unsigned clock;
bool halt;
bool stop;
bool ei;
bool ime;
//$ff00 JOYP

View File

@@ -21,6 +21,7 @@ void CPU::serialize(serializer &s) {
s.integer(status.clock);
s.integer(status.halt);
s.integer(status.stop);
s.integer(status.ei);
s.integer(status.ime);
s.integer(status.p15);

View File

@@ -1,18 +1,28 @@
#ifdef CPU_CPP
void CPU::op_io() {
cycle_edge();
add_clocks(4);
}
uint8 CPU::op_read(uint16 addr) {
cycle_edge();
uint8 r = bus.read(addr);
add_clocks(4);
return r;
}
void CPU::op_write(uint16 addr, uint8 data) {
cycle_edge();
bus.write(addr, data);
add_clocks(4);
}
void CPU::cycle_edge() {
if(status.ei) {
status.ei = false;
status.ime = 1;
}
}
#endif

View File

@@ -9,3 +9,4 @@ void timer_4096hz();
void op_io();
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);
void cycle_edge();

View File

@@ -5,8 +5,8 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000.19";
static unsigned SerializerVersion = 1;
static const char Version[] = "000.21";
static unsigned SerializerVersion = 2;
}
}

View File

@@ -1,5 +1,6 @@
class Interface {
public:
virtual void lcd_scanline() {}
virtual void joyp_write(bool p15, bool p14) {}
virtual void video_refresh(const uint8_t *data) {}

View File

@@ -18,21 +18,20 @@ void LCD::main() {
}
add_clocks(4);
status.lx += 4;
if(status.lx >= 456) scanline();
if(status.lx == 0) {
if(status.display_enable && status.lx == 0) {
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.lx == 252) {
if(status.display_enable && status.lx == 252) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
}
void LCD::add_clocks(unsigned clocks) {
status.lx += clocks;
if(status.lx >= 456) scanline();
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
@@ -43,13 +42,13 @@ void LCD::scanline() {
status.lx -= 456;
if(++status.ly == 154) frame();
if(status.interrupt_lyc == true) {
if(status.display_enable && status.interrupt_lyc == true) {
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly < 144) render();
if(status.ly == 144) {
if(status.display_enable && status.ly == 144) {
cpu.interrupt_raise(CPU::Interrupt::Vblank);
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
@@ -65,7 +64,10 @@ void LCD::frame() {
}
void LCD::render() {
for(unsigned n = 0; n < 160; n++) line[n] = 0x00;
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x00;
origin[n] = Origin::None;
}
if(status.display_enable == true) {
if(status.bg_enable == true) render_bg();
@@ -75,6 +77,7 @@ void LCD::render() {
uint8_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
system.interface->lcd_scanline();
}
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
@@ -97,7 +100,9 @@ void LCD::render_bg() {
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = status.bgp[palette];
origin[ox] = Origin::BG;
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
@@ -109,13 +114,16 @@ void LCD::render_bg() {
void LCD::render_window() {
if(status.ly - status.wy >= 144U) return;
unsigned iy = status.ly - status.wy;
unsigned ix = (status.wx - 7) & 255, tx = ix & 7;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned data = read_tile(status.window_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160U) line[ox] = status.bgp[palette];
if(ox - (status.wx - 7) < 160U) {
line[ox] = status.bgp[palette];
origin[ox] = Origin::Window;
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
@@ -125,6 +133,8 @@ void LCD::render_window() {
}
void LCD::render_obj() {
enum : unsigned { Priority = 0x80, YFlip = 0x40, XFlip = 0x20, Palette = 0x10 };
unsigned obj_size = (status.obj_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
@@ -164,26 +174,29 @@ void LCD::render_obj() {
sy = status.ly - sy;
if(sy >= obj_size) continue;
if(attribute & 0x40) sy ^= (obj_size - 1);
if(attribute & YFlip) sy ^= (obj_size - 1);
unsigned tdaddr = (tile << 4) + (sy << 1);
uint8 d0 = vram[tdaddr + 0];
uint8 d1 = vram[tdaddr + 1];
unsigned xflip = attribute & 0x20 ? 7 : 0;
unsigned xflip = attribute & XFlip ? 7 : 0;
for(unsigned tx = 0; tx < 8; tx++) {
uint8 palette = ((d0 & (0x80 >> tx)) ? 1 : 0)
| ((d1 & (0x80 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = status.obp[(bool)(attribute & 0x10)][palette];
palette = status.obp[(bool)(attribute & Palette)][palette];
unsigned ox = sx + (tx ^ xflip);
if(ox <= 159) {
if(attribute & 0x80) {
if(line[ox] > 0) continue;
if(attribute & Priority) {
if(origin[ox] == Origin::BG || origin[ox] == Origin::Window) {
if(line[ox] > 0) continue;
}
}
line[ox] = palette;
origin[ox] = Origin::OBJ;
}
}
}

View File

@@ -51,6 +51,9 @@ struct LCD : Processor, MMIO {
uint8 oam[160];
uint8 line[160];
struct Origin { enum : unsigned { None, BG, Window, OBJ }; };
uint8 origin[160];
static void Main();
void main();
void add_clocks(unsigned clocks);

View File

@@ -17,10 +17,10 @@ uint8 LCD::mmio_read(uint16 addr) {
if(addr == 0xff41) { //STAT
unsigned mode;
if(status.ly >= 144) mode = 1; //Vblank
else if(status.lx < 80) mode = 2; //OAM
if(status.ly >= 144) mode = 1; //Vblank
else if(status.lx < 80) mode = 2; //OAM
else if(status.lx < 252) mode = 3; //LCD
else mode = 0; //Hblank
else mode = 0; //Hblank
return (status.interrupt_lyc << 6)
| (status.interrupt_oam << 5)
@@ -83,6 +83,10 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
if(addr == 0xff40) { //LCDC
if(status.display_enable == false && (data & 0x80)) {
status.lx = 0; //unverified behavior; fixes Super Mario Land 2 - Tree Zone
}
status.display_enable = data & 0x80;
status.window_tilemap_select = data & 0x40;
status.window_display_enable = data & 0x20;

View File

@@ -33,6 +33,7 @@ void LCD::serialize(serializer &s) {
s.array(vram);
s.array(oam);
s.array(line);
s.array(origin);
}
#endif

View File

@@ -54,6 +54,10 @@ namespace nall {
operator[](buffersize) = data;
}
void remove() {
if(size > 0) resize(size - 1); //remove last element only
}
template<typename U> void insert(unsigned index, const U list) {
unsigned listsize = container_size(list);
resize(buffersize + listsize);
@@ -133,6 +137,12 @@ namespace nall {
if(index >= buffersize) throw "array[] out of bounds";
return pool[index];
}
//iteration
T* begin() { return &pool[0]; }
T* end() { return &pool[buffersize]; }
const T* begin() const { return &pool[0]; }
const T* end() const { return &pool[buffersize]; }
};
template<typename T> struct has_size<array<T>> { enum { value = true }; };

View File

@@ -72,6 +72,7 @@ namespace nall {
private:
static char enc(uint8_t n) {
//base64 for URL encodings
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}

101
bsnes/nall/bmp.hpp Executable file
View File

@@ -0,0 +1,101 @@
#ifndef NALL_BMP_HPP
#define NALL_BMP_HPP
#include <nall/file.hpp>
//BMP reader / writer
//author: byuu
//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
namespace nall {
struct bmp {
inline static bool read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height);
inline static bool write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha = false);
};
bool bmp::read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height) {
file fp;
if(fp.open(filename, file::mode::read) == false) return false;
if(fp.size() < 0x36) return false;
if(fp.readm(2) != 0x424d) return false;
fp.seek(0x000a);
unsigned offset = fp.readl(4);
unsigned dibsize = fp.readl(4);
if(dibsize != 40) return false;
signed headerWidth = fp.readl(4);
if(headerWidth < 0) return false;
signed headerHeight = fp.readl(4);
fp.readl(2);
unsigned bitsPerPixel = fp.readl(2);
if(bitsPerPixel != 24 && bitsPerPixel != 32) return false;
unsigned compression = fp.readl(4);
if(compression != 0) return false;
fp.seek(offset);
bool noFlip = headerHeight < 0;
width = headerWidth, height = abs(headerHeight);
data = new uint32_t[width * height];
unsigned bytesPerPixel = bitsPerPixel / 8;
unsigned alignedWidth = width * bytesPerPixel;
unsigned paddingLength = 0;
while(alignedWidth % 4) alignedWidth++, paddingLength++;
for(unsigned y = 0; y < height; y++) {
uint32_t *p = noFlip ? data + y * width : data + (height - 1 - y) * width;
for(unsigned x = 0; x < width; x++, p++) {
*p = fp.readl(bytesPerPixel);
if(bytesPerPixel == 3) *p |= 255 << 24;
}
if(paddingLength) fp.readl(paddingLength);
}
fp.close();
return true;
}
bool bmp::write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha) {
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
unsigned bitsPerPixel = alpha ? 32 : 24;
unsigned bytesPerPixel = bitsPerPixel / 8;
unsigned alignedWidth = width * bytesPerPixel;
unsigned paddingLength = 0;
unsigned imageSize = alignedWidth * height;
unsigned fileSize = 0x36 + imageSize;
while(alignedWidth % 4) alignedWidth++, paddingLength++;
fp.writem(0x424d, 2); //signature
fp.writel(fileSize, 4); //file size
fp.writel(0, 2); //reserved
fp.writel(0, 2); //reserved
fp.writel(0x36, 4); //offset
fp.writel(40, 4); //DIB size
fp.writel(width, 4); //width
fp.writel(-height, 4); //height
fp.writel(1, 2); //color planes
fp.writel(bitsPerPixel, 2); //bits per pixel
fp.writel(0, 4); //compression method (BI_RGB)
fp.writel(imageSize, 4); //image data size
fp.writel(3780, 4); //horizontal resolution
fp.writel(3780, 4); //vertical resolution
fp.writel(0, 4); //palette size
fp.writel(0, 4); //important color count
for(unsigned y = 0; y < height; y++) {
const uint32_t *p = (const uint32_t*)((const uint8_t*)data + y * pitch);
for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel);
if(paddingLength) fp.writel(0, paddingLength);
}
fp.close();
return true;
}
}
#endif

214
bsnes/nall/bps/delta.hpp Executable file
View File

@@ -0,0 +1,214 @@
#ifndef NALL_BPS_DELTA_HPP
#define NALL_BPS_DELTA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpsdelta {
inline void source(const uint8_t *data, unsigned size);
inline void target(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
enum : unsigned { Granularity = 1 };
struct Node {
unsigned offset;
Node *next;
inline Node() : offset(0), next(0) {}
inline ~Node() { if(next) delete next; }
};
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
const uint8_t *targetData;
unsigned targetSize;
};
void bpsdelta::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpsdelta::target(const uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpsdelta::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpsdelta::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
bool bpsdelta::create(const string &filename, const string &metadata) {
file modifyFile;
if(modifyFile.open(filename, file::mode::write) == false) return false;
uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
auto write = [&](uint8_t data) {
modifyFile.write(data);
modifyChecksum = crc32_adjust(modifyChecksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
write('B');
write('P');
write('S');
write('1');
encode(sourceSize);
encode(targetSize);
unsigned markupSize = metadata.length();
encode(markupSize);
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
Node *sourceTree[65536], *targetTree[65536];
for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
//source tree creation
for(unsigned offset = 0; offset < sourceSize; offset++) {
uint16_t symbol = sourceData[offset + 0];
sourceChecksum = crc32_adjust(sourceChecksum, symbol);
if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
Node *node = new Node;
node->offset = offset;
node->next = sourceTree[symbol];
sourceTree[symbol] = node;
}
unsigned targetReadLength = 0;
auto targetReadFlush = [&]() {
if(targetReadLength) {
encode(TargetRead | ((targetReadLength - 1) << 2));
unsigned offset = outputOffset - targetReadLength;
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
}
};
while(outputOffset < targetSize) {
unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
uint16_t symbol = targetData[outputOffset + 0];
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
{ //source read
unsigned length = 0, offset = outputOffset;
while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
length++;
offset++;
}
if(length > maxLength) maxLength = length, mode = SourceRead;
}
{ //source copy
Node *node = sourceTree[symbol];
while(node) {
unsigned length = 0, x = node->offset, y = outputOffset;
while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
node = node->next;
}
}
{ //target copy
Node *node = targetTree[symbol];
while(node) {
unsigned length = 0, x = node->offset, y = outputOffset;
while(y < targetSize && targetData[x++] == targetData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
node = node->next;
}
//target tree append
node = new Node;
node->offset = outputOffset;
node->next = targetTree[symbol];
targetTree[symbol] = node;
}
{ //target read
if(maxLength < 4) {
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
mode = TargetRead;
}
}
if(mode != TargetRead) targetReadFlush();
switch(mode) {
case SourceRead:
encode(SourceRead | ((maxLength - 1) << 2));
break;
case TargetRead:
//delay write to group sequential TargetRead commands into one
targetReadLength += maxLength;
break;
case SourceCopy:
case TargetCopy:
encode(mode | ((maxLength - 1) << 2));
signed relativeOffset;
if(mode == SourceCopy) {
relativeOffset = maxOffset - sourceRelativeOffset;
sourceRelativeOffset = maxOffset + maxLength;
} else {
relativeOffset = maxOffset - targetRelativeOffset;
targetRelativeOffset = maxOffset + maxLength;
}
encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
break;
}
outputOffset += maxLength;
}
targetReadFlush();
sourceChecksum = ~sourceChecksum;
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
uint32_t outputChecksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
modifyFile.close();
return true;
}
}
#endif

152
bsnes/nall/bps/linear.hpp Executable file
View File

@@ -0,0 +1,152 @@
#ifndef NALL_BPS_LINEAR_HPP
#define NALL_BPS_LINEAR_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpslinear {
inline void source(const uint8_t *data, unsigned size);
inline void target(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
enum : unsigned { Granularity = 1 };
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
const uint8_t *targetData;
unsigned targetSize;
};
void bpslinear::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpslinear::target(const uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpslinear::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpslinear::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
bool bpslinear::create(const string &filename, const string &metadata) {
file modifyFile;
if(modifyFile.open(filename, file::mode::write) == false) return false;
uint32_t modifyChecksum = ~0;
unsigned targetRelativeOffset = 0, outputOffset = 0;
auto write = [&](uint8_t data) {
modifyFile.write(data);
modifyChecksum = crc32_adjust(modifyChecksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
unsigned targetReadLength = 0;
auto targetReadFlush = [&]() {
if(targetReadLength) {
encode(TargetRead | ((targetReadLength - 1) << 2));
unsigned offset = outputOffset - targetReadLength;
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
}
};
write('B');
write('P');
write('S');
write('1');
encode(sourceSize);
encode(targetSize);
unsigned markupSize = metadata.length();
encode(markupSize);
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
while(outputOffset < targetSize) {
unsigned sourceLength = 0;
for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
sourceLength++;
}
unsigned rleLength = 0;
for(unsigned n = 1; outputOffset + n < targetSize; n++) {
if(targetData[outputOffset] != targetData[outputOffset + n]) break;
rleLength++;
}
if(rleLength >= 4) {
//write byte to repeat
targetReadLength++;
outputOffset++;
targetReadFlush();
//copy starting from repetition byte
encode(TargetCopy | ((rleLength - 1) << 2));
unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset;
encode(relativeOffset << 1);
outputOffset += rleLength;
targetRelativeOffset = outputOffset - 1;
} else if(sourceLength >= 4) {
targetReadFlush();
encode(SourceRead | ((sourceLength - 1) << 2));
outputOffset += sourceLength;
} else {
targetReadLength += Granularity;
outputOffset += Granularity;
}
}
targetReadFlush();
uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
uint32_t outputChecksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
modifyFile.close();
return true;
}
}
#endif

121
bsnes/nall/bps/metadata.hpp Executable file
View File

@@ -0,0 +1,121 @@
#ifndef NALL_BPS_METADATA_HPP
#define NALL_BPS_METADATA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpsmetadata {
inline bool load(const string &filename);
inline bool save(const string &filename, const string &metadata);
inline string metadata() const;
protected:
file sourceFile;
string metadataString;
};
bool bpsmetadata::load(const string &filename) {
if(sourceFile.open(filename, file::mode::read) == false) return false;
auto read = [&]() -> uint8_t {
return sourceFile.read();
};
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
if(read() != 'B') return false;
if(read() != 'P') return false;
if(read() != 'S') return false;
if(read() != '1') return false;
decode();
decode();
unsigned metadataSize = decode();
char data[metadataSize + 1];
for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
data[metadataSize] = 0;
metadataString = (const char*)data;
return true;
}
bool bpsmetadata::save(const string &filename, const string &metadata) {
file targetFile;
if(targetFile.open(filename, file::mode::write) == false) return false;
if(sourceFile.open() == false) return false;
sourceFile.seek(0);
auto read = [&]() -> uint8_t {
return sourceFile.read();
};
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
uint32_t checksum = ~0;
auto write = [&](uint8_t data) {
targetFile.write(data);
checksum = crc32_adjust(checksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
for(unsigned n = 0; n < 4; n++) write(read());
encode(decode());
encode(decode());
unsigned sourceLength = decode();
unsigned targetLength = metadata.length();
encode(targetLength);
sourceFile.seek(sourceLength, file::index::relative);
for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
unsigned length = sourceFile.size() - sourceFile.offset() - 4;
for(unsigned n = 0; n < length; n++) write(read());
uint32_t outputChecksum = ~checksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
targetFile.close();
return true;
}
string bpsmetadata::metadata() const {
return metadataString;
}
}
#endif

219
bsnes/nall/bps/patch.hpp Executable file
View File

@@ -0,0 +1,219 @@
#ifndef NALL_BPS_PATCH_HPP
#define NALL_BPS_PATCH_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpspatch {
inline bool modify(const uint8_t *data, unsigned size);
inline void source(const uint8_t *data, unsigned size);
inline void target(uint8_t *data, unsigned size);
inline bool modify(const string &filename);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline string metadata() const;
inline unsigned size() const;
enum result : unsigned {
unknown,
success,
patch_too_small,
patch_invalid_header,
source_too_small,
target_too_small,
source_checksum_invalid,
target_checksum_invalid,
patch_checksum_invalid,
};
inline result apply();
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
filemap modifyFile;
const uint8_t *modifyData;
unsigned modifySize;
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
uint8_t *targetData;
unsigned targetSize;
unsigned modifySourceSize;
unsigned modifyTargetSize;
unsigned modifyMarkupSize;
string metadataString;
};
bool bpspatch::modify(const uint8_t *data, unsigned size) {
if(size < 19) return false;
modifyData = data;
modifySize = size;
unsigned offset = 4;
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = modifyData[offset++];
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
modifySourceSize = decode();
modifyTargetSize = decode();
modifyMarkupSize = decode();
char buffer[modifyMarkupSize + 1];
for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
buffer[modifyMarkupSize] = 0;
metadataString = (const char*)buffer;
return true;
}
void bpspatch::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpspatch::target(uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpspatch::modify(const string &filename) {
if(modifyFile.open(filename, filemap::mode::read) == false) return false;
return modify(modifyFile.data(), modifyFile.size());
}
bool bpspatch::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpspatch::target(const string &filename) {
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
fp.truncate(modifyTargetSize);
fp.close();
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
string bpspatch::metadata() const {
return metadataString;
}
unsigned bpspatch::size() const {
return modifyTargetSize;
}
bpspatch::result bpspatch::apply() {
if(modifySize < 19) return result::patch_too_small;
uint32_t modifyChecksum = ~0, targetChecksum = ~0;
unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
auto read = [&]() -> uint8_t {
uint8_t data = modifyData[modifyOffset++];
modifyChecksum = crc32_adjust(modifyChecksum, data);
return data;
};
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
auto write = [&](uint8_t data) {
targetData[outputOffset++] = data;
targetChecksum = crc32_adjust(targetChecksum, data);
};
if(read() != 'B') return result::patch_invalid_header;
if(read() != 'P') return result::patch_invalid_header;
if(read() != 'S') return result::patch_invalid_header;
if(read() != '1') return result::patch_invalid_header;
modifySourceSize = decode();
modifyTargetSize = decode();
modifyMarkupSize = decode();
for(unsigned n = 0; n < modifyMarkupSize; n++) read();
if(modifySourceSize > sourceSize) return result::source_too_small;
if(modifyTargetSize > targetSize) return result::target_too_small;
while(modifyOffset < modifySize - 12) {
unsigned length = decode();
unsigned mode = length & 3;
length = (length >> 2) + 1;
switch(mode) {
case SourceRead:
while(length--) write(sourceData[outputOffset]);
break;
case TargetRead:
while(length--) write(read());
break;
case SourceCopy:
case TargetCopy:
signed offset = decode();
bool negative = offset & 1;
offset >>= 1;
if(negative) offset = -offset;
if(mode == SourceCopy) {
sourceRelativeOffset += offset;
while(length--) write(sourceData[sourceRelativeOffset++]);
} else {
targetRelativeOffset += offset;
while(length--) write(targetData[targetRelativeOffset++]);
}
break;
}
}
uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
uint32_t checksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
targetChecksum = ~targetChecksum;
if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
return result::success;
}
}
#endif

79
bsnes/nall/compositor.hpp Executable file
View File

@@ -0,0 +1,79 @@
#ifndef NALL_COMPOSITOR_HPP
#define NALL_COMPOSITOR_HPP
#include <nall/detect.hpp>
namespace nall {
struct compositor {
inline static bool enabled();
inline static bool enable(bool status);
};
#if defined(PLATFORM_X)
bool compositor::enabled() {
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
if(fp == 0) return false;
char buffer[512];
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable(bool status) {
FILE *fp;
if(status) {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
} else {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
}
if(fp == 0) return false;
pclose(fp);
return true;
}
#elif defined(PLATFORM_WIN)
bool compositor::enabled() {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == 0) module = LoadLibraryW(L"dwmapi");
if(module == 0) return false;
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
if(pDwmIsCompositionEnabled == 0) return false;
BOOL result;
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
return result;
}
bool compositor::enable(bool status) {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == 0) module = LoadLibraryW(L"dwmapi");
if(module == 0) return false;
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
if(pDwmEnableComposition == 0) return false;
if(pDwmEnableComposition(status) != S_OK) return false;
return true;
}
#else
bool compositor::enabled() {
return false;
}
bool compositor::enable(bool) {
return false;
}
#endif
}
#endif

View File

@@ -34,11 +34,11 @@ namespace nall {
string get() const {
switch(type) {
case boolean_t: return string() << *(bool*)data;
case signed_t: return string() << *(signed*)data;
case unsigned_t: return string() << *(unsigned*)data;
case double_t: return string() << *(double*)data;
case string_t: return string() << "\"" << *(string*)data << "\"";
case boolean_t: return { *(bool*)data };
case signed_t: return { *(signed*)data };
case unsigned_t: return { *(unsigned*)data };
case double_t: return { *(double*)data };
case string_t: return { "\"", *(string*)data, "\"" };
}
return "???";
}
@@ -105,9 +105,9 @@ namespace nall {
if(fp.open(filename, file::mode::write)) {
for(unsigned i = 0; i < list.size(); i++) {
string output;
output << list[i].name << " = " << list[i].get();
if(list[i].desc != "") output << " # " << list[i].desc;
output << "\r\n";
output.append(list[i].name, " = ", list[i].get());
if(list[i].desc != "") output.append(" # ", list[i].desc);
output.append("\r\n");
fp.print(output);
}

View File

@@ -1,75 +0,0 @@
#ifndef NALL_DICTIONARY_HPP
#define NALL_DICTIONARY_HPP
#include <nall/array.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
namespace nall {
class dictionary {
public:
string operator[](const char *input) {
for(unsigned i = 0; i < index_input.size(); i++) {
if(index_input[i] == input) return index_output[i];
}
//no match, use input; remove input identifier, if one exists
if(strbegin(input, "{{")) {
if(auto pos = strpos(input, "}}")) {
string temp = substr(input, pos() + 2);
return temp;
}
}
return input;
}
bool import(const char *filename) {
string data;
if(data.readfile(filename) == false) return false;
data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
data.replace("\r", "");
lstring line;
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
lstring part;
//format: "Input" = "Output"
part.qsplit("=", line[i]);
if(part.size() != 2) continue;
//remove whitespace
part[0].trim();
part[1].trim();
//remove quotes
part[0].trim<1>("\"");
part[1].trim<1>("\"");
unsigned n = index_input.size();
index_input[n] = part[0];
index_output[n] = part[1];
}
return true;
}
void reset() {
index_input.reset();
index_output.reset();
}
~dictionary() {
reset();
}
dictionary& operator=(const dictionary&) = delete;
dictionary(const dictionary&) = delete;
protected:
lstring index_input;
lstring index_output;
};
}
#endif

View File

@@ -6,7 +6,7 @@
#include <nall/string.hpp>
#if defined(_WIN32)
#include <nall/utf8.hpp>
#include <nall/windows/utf8.hpp>
#else
#include <dirent.h>
#include <stdio.h>
@@ -42,20 +42,21 @@ struct directory {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(string(name, "/"));
if(wildcard(name, pattern)) list.append(name);
}
}
while(FindNextFile(handle, &data) != false) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(string(name, "/"));
if(wildcard(name, pattern)) list.append(name);
}
}
}
FindClose(handle);
}
if(list.size() > 0) sort(&list[0], list.size());
foreach(name, list) name.append("/"); //must append after sorting
return list;
}
@@ -109,14 +110,14 @@ struct directory {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if(ep->d_type & DT_DIR) {
if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/"));
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
}
}
closedir(dp);
}
if(list.size() > 0) sort(&list[0], list.size());
foreach(name, list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::files(const string &pathname, const string &pattern) {

View File

@@ -12,7 +12,7 @@
#include <dlfcn.h>
#elif defined(PLATFORM_WIN)
#include <windows.h>
#include <nall/utf8.hpp>
#include <nall/windows/utf8.hpp>
#endif
namespace nall {

8
bsnes/nall/dsp.hpp Executable file
View File

@@ -0,0 +1,8 @@
#ifndef NALL_DSP_HPP
#define NALL_DSP_HPP
#define NALL_DSP_INTERNAL_HPP
#include <nall/dsp/core.hpp>
#undef NALL_DSP_INTERNAL_HPP
#endif

36
bsnes/nall/dsp/buffer.hpp Executable file
View File

@@ -0,0 +1,36 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct Buffer {
double *sample[2];
uint16_t rdoffset;
uint16_t wroffset;
inline double& read(bool channel, signed offset = 0) {
return sample[channel][(uint16_t)(rdoffset + offset)];
}
inline double& write(bool channel, signed offset = 0) {
return sample[channel][(uint16_t)(wroffset + offset)];
}
inline void clear() {
for(unsigned n = 0; n < 65536; n++) {
sample[0][n] = 0;
sample[1][n] = 0;
}
rdoffset = 0;
wroffset = 0;
}
Buffer() {
sample[0] = new double[65536];
sample[1] = new double[65536];
}
~Buffer() {
delete[] sample[0];
delete[] sample[1];
}
};
#endif

154
bsnes/nall/dsp/core.hpp Executable file
View File

@@ -0,0 +1,154 @@
#ifdef NALL_DSP_INTERNAL_HPP
#include <math.h>
#include <nall/stdint.hpp>
namespace nall {
struct DSP {
enum class Resampler : unsigned {
Point,
Linear,
Cosine,
Cubic,
Hermite,
Average,
};
inline void setPrecision(unsigned precision);
inline void setFrequency(double frequency); //inputFrequency
inline void setVolume(double volume);
inline void setBalance(double balance);
inline void setResampler(Resampler resampler);
inline void setResamplerFrequency(double frequency); //outputFrequency
inline void sample(signed lchannel, signed rchannel);
inline bool pending();
inline void read(signed &lchannel, signed &rchannel);
inline void clear();
inline DSP();
inline ~DSP();
protected:
struct Settings {
unsigned precision;
double frequency;
double volume;
double balance;
//internal
double intensity;
} settings;
struct ResamplerSettings {
Resampler engine;
double frequency;
//internal
double fraction;
double step;
} resampler;
inline void resamplerRun();
inline void resamplerWrite(double lchannel, double rchannel);
inline void resamplePoint();
inline void resampleLinear();
inline void resampleCosine();
inline void resampleCubic();
inline void resampleHermite();
inline void resampleAverage();
#include "buffer.hpp"
Buffer buffer;
Buffer output;
inline void adjustVolume();
inline void adjustBalance();
inline signed clamp(const unsigned bits, const signed x);
};
#include "settings.hpp"
void DSP::sample(signed lchannel, signed rchannel) {
buffer.write(0) = (double)lchannel / settings.intensity;
buffer.write(1) = (double)rchannel / settings.intensity;
buffer.wroffset++;
resamplerRun();
}
bool DSP::pending() {
return output.rdoffset != output.wroffset;
}
void DSP::read(signed &lchannel, signed &rchannel) {
adjustVolume();
adjustBalance();
lchannel = clamp(settings.precision, output.read(0) * settings.intensity);
rchannel = clamp(settings.precision, output.read(1) * settings.intensity);
output.rdoffset++;
}
void DSP::resamplerRun() {
switch(resampler.engine) {
case Resampler::Point: return resamplePoint();
case Resampler::Linear: return resampleLinear();
case Resampler::Cosine: return resampleCosine();
case Resampler::Cubic: return resampleCubic();
case Resampler::Hermite: return resampleHermite();
case Resampler::Average: return resampleAverage();
}
}
void DSP::resamplerWrite(double lchannel, double rchannel) {
output.write(0) = lchannel;
output.write(1) = rchannel;
output.wroffset++;
}
#include "resample/point.hpp"
#include "resample/linear.hpp"
#include "resample/cosine.hpp"
#include "resample/cubic.hpp"
#include "resample/hermite.hpp"
#include "resample/average.hpp"
void DSP::adjustVolume() {
output.read(0) *= settings.volume;
output.read(1) *= settings.volume;
}
void DSP::adjustBalance() {
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
}
signed DSP::clamp(const unsigned bits, const signed x) {
const signed b = 1U << (bits - 1);
const signed m = (1U << (bits - 1)) - 1;
return (x > m) ? m : (x < -b) ? -b : x;
}
void DSP::clear() {
resampler.fraction = 0.0;
buffer.clear();
output.clear();
}
DSP::DSP() {
setPrecision(16);
setFrequency(44100.0);
setVolume(1.0);
setBalance(0.0);
setResampler(Resampler::Hermite);
setResamplerFrequency(44100.0);
clear();
}
DSP::~DSP() {
}
}
#endif

View File

@@ -0,0 +1,28 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleAverage() {
//can only average if input frequency >= output frequency
if(resampler.step < 1.0) return resampleHermite();
resampler.fraction += 1.0;
double scalar = 1.0;
if(resampler.fraction > resampler.step) scalar = 1.0 - (resampler.fraction - resampler.step);
output.write(0) += buffer.read(0) * scalar;
output.write(1) += buffer.read(1) * scalar;
if(resampler.fraction >= resampler.step) {
output.write(0) /= resampler.step;
output.write(1) /= resampler.step;
output.wroffset++;
resampler.fraction -= resampler.step;
output.write(0) = buffer.read(0) * resampler.fraction;
output.write(1) = buffer.read(1) * resampler.fraction;
}
buffer.rdoffset++;
}
#endif

View File

@@ -0,0 +1,25 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleCosine() {
while(resampler.fraction <= 1.0) {
double channel[2];
for(unsigned n = 0; n < 2; n++) {
double a = buffer.read(n, -1);
double b = buffer.read(n, -0);
double mu = resampler.fraction;
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
channel[n] = a * (1.0 - mu) + b * mu;
}
resamplerWrite(channel[0], channel[1]);
resampler.fraction += resampler.step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,31 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleCubic() {
while(resampler.fraction <= 1.0) {
double channel[2];
for(unsigned n = 0; n < 2; n++) {
double a = buffer.read(n, -3);
double b = buffer.read(n, -2);
double c = buffer.read(n, -1);
double d = buffer.read(n, -0);
double mu = resampler.fraction;
double A = d - c - a + b;
double B = a - b - A;
double C = c - a;
double D = b;
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
}
resamplerWrite(channel[0], channel[1]);
resampler.fraction += resampler.step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,43 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleHermite() {
while(resampler.fraction <= 1.0) {
double channel[2];
for(unsigned n = 0; n < 2; n++) {
double a = buffer.read(n, -3);
double b = buffer.read(n, -2);
double c = buffer.read(n, -1);
double d = buffer.read(n, -0);
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
double mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
mu1 = resampler.fraction;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
resamplerWrite(channel[0], channel[1]);
resampler.fraction += resampler.step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,24 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleLinear() {
while(resampler.fraction <= 1.0) {
double channel[2];
for(unsigned n = 0; n < 2; n++) {
double a = buffer.read(n, -1);
double b = buffer.read(n, -0);
double mu = resampler.fraction;
channel[n] = a * (1.0 - mu) + b * mu;
}
resamplerWrite(channel[0], channel[1]);
resampler.fraction += resampler.step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,24 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resamplePoint() {
while(resampler.fraction <= 1.0) {
double channel[2];
for(unsigned n = 0; n < 2; n++) {
double a = buffer.read(n, -1);
double b = buffer.read(n, -0);
double mu = resampler.fraction;
channel[n] = mu < 0.5 ? a : b;
}
resamplerWrite(channel[0], channel[1]);
resampler.fraction += resampler.step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
}
#endif

32
bsnes/nall/dsp/settings.hpp Executable file
View File

@@ -0,0 +1,32 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::setPrecision(unsigned precision) {
settings.precision = precision;
settings.intensity = 1 << (settings.precision - 1);
}
void DSP::setFrequency(double frequency) {
settings.frequency = frequency;
resampler.fraction = 0;
resampler.step = settings.frequency / resampler.frequency;
}
void DSP::setVolume(double volume) {
settings.volume = volume;
}
void DSP::setBalance(double balance) {
settings.balance = balance;
}
void DSP::setResampler(Resampler engine) {
resampler.engine = engine;
}
void DSP::setResamplerFrequency(double frequency) {
resampler.frequency = frequency;
resampler.fraction = 0;
resampler.step = settings.frequency / resampler.frequency;
}
#endif

View File

@@ -1,22 +1,14 @@
#ifndef NALL_FILE_HPP
#define NALL_FILE_HPP
#include <stdio.h>
#include <string.h>
#if !defined(_WIN32)
#include <unistd.h>
#else
#include <io.h>
#endif
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utf8.hpp>
#include <nall/utility.hpp>
#include <nall/windows/utf8.hpp>
namespace nall {
inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
#if !defined(_WIN32)
return fopen(utf8_filename, mode);
#else
@@ -28,6 +20,29 @@ namespace nall {
public:
enum class mode : unsigned { read, write, readwrite, writeread };
enum class index : unsigned { absolute, relative };
enum class time : unsigned { create, modify, access };
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
file fp;
if(fp.open(filename, mode::read) == false) return false;
size = fp.size();
data = new uint8_t[size];
fp.read(data, size);
fp.close();
return true;
}
static bool read(const string &filename, const uint8_t *&data, unsigned &size) {
return file::read(filename, (uint8_t*&)data, size);
}
static bool write(const string &filename, const uint8_t *data, unsigned size) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.write(data, size);
fp.close();
return true;
}
uint8_t read() {
if(!fp) return 0xff; //file not open
@@ -142,52 +157,60 @@ namespace nall {
return file_offset >= file_size;
}
static bool exists(const char *fn) {
static bool exists(const string &filename) {
#if !defined(_WIN32)
FILE *fp = fopen(fn, "rb");
struct stat64 data;
return stat64(filename, &data) == 0;
#else
FILE *fp = _wfopen(utf16_t(fn), L"rb");
struct __stat64 data;
return _wstat64(utf16_t(filename), &data) == 0;
#endif
if(fp) {
fclose(fp);
return true;
}
return false;
}
static unsigned size(const char *fn) {
static uintmax_t size(const string &filename) {
#if !defined(_WIN32)
FILE *fp = fopen(fn, "rb");
struct stat64 data;
stat64(filename, &data);
#else
FILE *fp = _wfopen(utf16_t(fn), L"rb");
struct __stat64 data;
_wstat64(utf16_t(filename), &data);
#endif
unsigned filesize = 0;
if(fp) {
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
fclose(fp);
return S_ISREG(data.st_mode) ? data.st_size : 0u;
}
static time_t timestamp(const string &filename, file::time mode = file::time::create) {
#if !defined(_WIN32)
struct stat64 data;
stat64(filename, &data);
#else
struct __stat64 data;
_wstat64(utf16_t(filename), &data);
#endif
switch(mode) { default:
case file::time::create: return data.st_ctime;
case file::time::modify: return data.st_mtime;
case file::time::access: return data.st_atime;
}
return filesize;
}
bool open() {
return fp;
}
bool open(const char *fn, mode mode_) {
bool open(const string &filename, mode mode_) {
if(fp) return false;
switch(file_mode = mode_) {
#if !defined(_WIN32)
case mode::read: fp = fopen(fn, "rb"); break;
case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering
case mode::readwrite: fp = fopen(fn, "rb+"); break;
case mode::writeread: fp = fopen(fn, "wb+"); break;
case mode::read: fp = fopen(filename, "rb" ); break;
case mode::write: fp = fopen(filename, "wb+"); break; //need read permission for buffering
case mode::readwrite: fp = fopen(filename, "rb+"); break;
case mode::writeread: fp = fopen(filename, "wb+"); break;
#else
case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break;
case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break;
case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
case mode::read: fp = _wfopen(utf16_t(filename), L"rb" ); break;
case mode::write: fp = _wfopen(utf16_t(filename), L"wb+"); break;
case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break;
case mode::writeread: fp = _wfopen(utf16_t(filename), L"wb+"); break;
#endif
}
if(!fp) return false;

View File

@@ -2,7 +2,7 @@
#define NALL_FILEMAP_HPP
#include <nall/stdint.hpp>
#include <nall/utf8.hpp>
#include <nall/windows/utf8.hpp>
#include <stdio.h>
#include <stdlib.h>
@@ -47,6 +47,12 @@ namespace nall {
}
bool p_open(const char *filename, mode mode_) {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = 0;
p_size = 0;
return true;
}
int desired_access, creation_disposition, flprotect, map_access;
switch(mode_) {
@@ -133,6 +139,12 @@ namespace nall {
}
bool p_open(const char *filename, mode mode_) {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = 0;
p_size = 0;
return true;
}
int open_flags, mmap_flags;
switch(mode_) {

View File

@@ -6,7 +6,7 @@ namespace nall {
class GameBoyCartridge {
public:
string xml;
inline GameBoyCartridge(const uint8_t *data, unsigned size);
inline GameBoyCartridge(uint8_t *data, unsigned size);
//private:
struct Information {
@@ -21,7 +21,7 @@ public:
} info;
};
GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
if(romsize < 0x4000) return;
@@ -34,6 +34,20 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
info.romsize = 0;
info.ramsize = 0;
unsigned base = romsize - 0x8000;
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
) {
//MMM01 stores header at bottom of image
//flip this around for consistency with all other mappers
uint8_t header[0x8000];
memcpy(header, romdata + base, 0x8000);
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
memcpy(romdata, header, 0x8000);
}
switch(romdata[0x0147]) {
case 0x00: info.mapper = "none"; break;
case 0x01: info.mapper = "MBC1"; break;
@@ -86,17 +100,17 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
xml << "<cartridge mapper='" << info.mapper << "'";
if(info.rtc) xml << " rtc='true'";
if(info.rumble) xml << " rumble='true'";
xml << ">\n";
xml.append("<cartridge mapper='", info.mapper, "'");
if(info.rtc) xml.append(" rtc='true'");
if(info.rumble) xml.append(" rumble='true'");
xml.append(">\n");
xml << " <rom size='" << hex(romsize) << "'/>\n"; //TODO: trust/check info.romsize?
xml.append(" <rom size='", hex(romsize), "'/>\n"); //TODO: trust/check info.romsize?
if(info.ramsize > 0)
xml << " <ram size='" << hex(info.ramsize) << "' battery='" << info.battery << "'/>\n";
xml.append(" <ram size='", hex(info.ramsize), "' battery='", info.battery, "'/>\n");
xml << "</cartridge>\n";
xml.append("</cartridge>\n");
xml.transform("'", "\"");
}

87
bsnes/nall/gzip.hpp Executable file
View File

@@ -0,0 +1,87 @@
#ifndef NALL_GZIP_HPP
#define NALL_GZIP_HPP
#include <nall/file.hpp>
#include <nall/inflate.hpp>
namespace nall {
struct gzip {
string filename;
uint8_t *data;
unsigned size;
bool decompress(const string &filename);
bool decompress(const uint8_t *data, unsigned size);
gzip();
~gzip();
};
bool gzip::decompress(const string &filename) {
uint8_t *data;
unsigned size;
if(file::read(filename, data, size) == false) return false;
bool result = decompress(data, size);
delete[] data;
return result;
}
bool gzip::decompress(const uint8_t *data, unsigned size) {
if(size < 18) return false;
if(data[0] != 0x1f) return false;
if(data[1] != 0x8b) return false;
unsigned cm = data[2];
unsigned flg = data[3];
unsigned mtime = data[4];
mtime |= data[5] << 8;
mtime |= data[6] << 16;
mtime |= data[7] << 24;
unsigned xfl = data[8];
unsigned os = data[9];
unsigned p = 10;
unsigned isize = data[size - 4];
isize |= data[size - 3] << 8;
isize |= data[size - 2] << 16;
isize |= data[size - 1] << 24;
filename = "";
if(flg & 0x04) { //FEXTRA
unsigned xlen = data[p + 0];
xlen |= data[p + 1] << 8;
p += 2 + xlen;
}
if(flg & 0x08) { //FNAME
char buffer[PATH_MAX];
for(unsigned n = 0; n < PATH_MAX; n++, p++) {
buffer[n] = data[p];
if(data[p] == 0) break;
}
if(data[p++]) return false;
filename = buffer;
}
if(flg & 0x10) { //FCOMMENT
while(data[p++]);
}
if(flg & 0x02) { //FHCRC
p += 2;
}
this->size = isize;
this->data = new uint8_t[this->size];
return inflate(this->data, this->size, data + p, size - p - 8);
}
gzip::gzip() : data(0) {
}
gzip::~gzip() {
if(data) delete[] data;
}
}
#endif

176
bsnes/nall/http.hpp Executable file
View File

@@ -0,0 +1,176 @@
#ifndef NALL_HTTP_HPP
#define NALL_HTTP_HPP
#if !defined(_WIN32)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#else
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include <nall/platform.hpp>
#include <nall/string.hpp>
namespace nall {
struct http {
string hostname;
addrinfo *serverinfo;
int serversocket;
string header;
inline void download(const string &path, uint8_t *&data, unsigned &size) {
data = 0;
size = 0;
send({
"GET ", path, " HTTP/1.1\r\n"
"Host: ", hostname, "\r\n"
"Connection: close\r\n"
"\r\n"
});
header = downloadHeader();
downloadContent(data, size);
}
inline bool connect(string host, unsigned port) {
hostname = host;
addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int status = getaddrinfo(hostname, string(port), &hints, &serverinfo);
if(status != 0) return false;
serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol);
if(serversocket == -1) return false;
int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen);
if(result == -1) return false;
return true;
}
inline bool send(const string &data) {
return send((const uint8_t*)(const char*)data, data.length());
}
inline bool send(const uint8_t *data, unsigned size) {
while(size) {
int length = ::send(serversocket, (const char*)data, size, 0);
if(length == -1) return false;
data += length;
size -= length;
}
return true;
}
inline string downloadHeader() {
string output;
do {
char buffer[2];
int length = recv(serversocket, buffer, 1, 0);
if(length <= 0) return output;
buffer[1] = 0;
output.append(buffer);
} while(output.endswith("\r\n\r\n") == false);
return output;
}
inline string downloadChunkLength() {
string output;
do {
char buffer[2];
int length = recv(serversocket, buffer, 1, 0);
if(length <= 0) return output;
buffer[1] = 0;
output.append(buffer);
} while(output.endswith("\r\n") == false);
return output;
}
inline void downloadContent(uint8_t *&data, unsigned &size) {
unsigned capacity = 0;
if(header.iposition("\r\nTransfer-Encoding: chunked\r\n")) {
while(true) {
unsigned length = hex(downloadChunkLength());
if(length == 0) break;
capacity += length;
data = (uint8_t*)realloc(data, capacity);
char buffer[length];
while(length) {
int packetlength = recv(serversocket, buffer, length, 0);
if(packetlength <= 0) break;
memcpy(data + size, buffer, packetlength);
size += packetlength;
length -= packetlength;
}
}
} else if(auto position = header.iposition("\r\nContent-Length: ")) {
unsigned length = decimal((const char*)header + position() + 16);
while(length) {
char buffer[256];
int packetlength = recv(serversocket, buffer, min(256, length), 0);
if(packetlength <= 0) break;
capacity += packetlength;
data = (uint8_t*)realloc(data, capacity);
memcpy(data + size, buffer, packetlength);
size += packetlength;
length -= packetlength;
}
} else {
while(true) {
char buffer[256];
int packetlength = recv(serversocket, buffer, 256, 0);
if(packetlength <= 0) break;
capacity += packetlength;
data = (uint8_t*)realloc(data, capacity);
memcpy(data + size, buffer, packetlength);
size += packetlength;
}
}
data = (uint8_t*)realloc(data, capacity + 1);
data[capacity] = 0;
}
inline void disconnect() {
close(serversocket);
freeaddrinfo(serverinfo);
serverinfo = 0;
serversocket = -1;
}
#ifdef _WIN32
inline int close(int sock) {
return closesocket(sock);
}
inline http() {
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
WSACleanup();
return;
}
} else {
close(sock);
}
}
#endif
};
}
#endif

358
bsnes/nall/inflate.hpp Executable file
View File

@@ -0,0 +1,358 @@
#ifndef NALL_INFLATE_HPP
#define NALL_INFLATE_HPP
#include <setjmp.h>
namespace nall {
namespace puff {
inline int puff(
unsigned char *dest, unsigned long *destlen,
unsigned char *source, unsigned long *sourcelen
);
}
inline bool inflate(
uint8_t *target, unsigned targetLength,
const uint8_t *source, unsigned sourceLength
) {
unsigned long tl = targetLength, sl = sourceLength;
int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl);
return result == 0;
}
namespace puff {
//zlib/contrib/puff.c
//version 2.1*
//author: Mark Adler
//license: zlib
//ported by: byuu
//* I have corrected a bug in fixed(), where it was accessing uninitialized
// memory: calling construct() with lencode prior to initializing lencode.count
enum {
MAXBITS = 15,
MAXLCODES = 286,
MAXDCODES = 30,
FIXLCODES = 288,
MAXCODES = MAXLCODES + MAXDCODES,
};
struct state {
unsigned char *out;
unsigned long outlen;
unsigned long outcnt;
unsigned char *in;
unsigned long inlen;
unsigned long incnt;
int bitbuf;
int bitcnt;
jmp_buf env;
};
struct huffman {
short *count;
short *symbol;
};
inline int bits(state *s, int need) {
long val;
val = s->bitbuf;
while(s->bitcnt < need) {
if(s->incnt == s->inlen) longjmp(s->env, 1);
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
s->bitcnt += 8;
}
s->bitbuf = (int)(val >> need);
s->bitcnt -= need;
return (int)(val & ((1L << need) - 1));
}
inline int stored(state *s) {
unsigned len;
s->bitbuf = 0;
s->bitcnt = 0;
if(s->incnt + 4 > s->inlen) return 2;
len = s->in[s->incnt++];
len |= s->in[s->incnt++] << 8;
if(s->in[s->incnt++] != (~len & 0xff) ||
s->in[s->incnt++] != ((~len >> 8) & 0xff)
) return 2;
if(s->incnt + len > s->inlen) return 2;
if(s->out != 0) {
if(s->outcnt + len > s->outlen) return 1;
while(len--) s->out[s->outcnt++] = s->in[s->incnt++];
} else {
s->outcnt += len;
s->incnt += len;
}
return 0;
}
inline int decode(state *s, huffman *h) {
int len, code, first, count, index, bitbuf, left;
short *next;
bitbuf = s->bitbuf;
left = s->bitcnt;
code = first = index = 0;
len = 1;
next = h->count + 1;
while(true) {
while(left--) {
code |= bitbuf & 1;
bitbuf >>= 1;
count = *next++;
if(code - count < first) {
s->bitbuf = bitbuf;
s->bitcnt = (s->bitcnt - len) & 7;
return h->symbol[index + (code - first)];
}
index += count;
first += count;
first <<= 1;
code <<= 1;
len++;
}
left = (MAXBITS + 1) - len;
if(left == 0) break;
if(s->incnt == s->inlen) longjmp(s->env, 1);
bitbuf = s->in[s->incnt++];
if(left > 8) left = 8;
}
return -10;
}
inline int construct(huffman *h, short *length, int n) {
int symbol, len, left;
short offs[MAXBITS + 1];
for(len = 0; len <= MAXBITS; len++) h->count[len] = 0;
for(symbol = 0; symbol < n; symbol++) h->count[length[symbol]]++;
if(h->count[0] == n) return 0;
left = 1;
for(len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= h->count[len];
if(left < 0) return left;
}
offs[1] = 0;
for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len];
for(symbol = 0; symbol < n; symbol++) {
if(length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol;
}
return left;
}
inline int codes(state *s, huffman *lencode, huffman *distcode) {
int symbol, len;
unsigned dist;
static const short lens[29] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
};
static const short lext[29] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
static const short dists[30] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577
};
static const short dext[30] = {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13
};
do {
symbol = decode(s, lencode);
if(symbol < 0) return symbol;
if(symbol < 256) {
if(s->out != 0) {
if(s->outcnt == s->outlen) return 1;
s->out[s->outcnt] = symbol;
}
s->outcnt++;
} else if(symbol > 256) {
symbol -= 257;
if(symbol >= 29) return -10;
len = lens[symbol] + bits(s, lext[symbol]);
symbol = decode(s, distcode);
if(symbol < 0) return symbol;
dist = dists[symbol] + bits(s, dext[symbol]);
#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
if(dist > s->outcnt) return -11;
#endif
if(s->out != 0) {
if(s->outcnt + len > s->outlen) return 1;
while(len--) {
s->out[s->outcnt] =
#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
dist > s->outcnt ? 0 :
#endif
s->out[s->outcnt - dist];
s->outcnt++;
}
} else {
s->outcnt += len;
}
}
} while(symbol != 256);
return 0;
}
inline int fixed(state *s) {
static int virgin = 1;
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
static huffman lencode, distcode;
if(virgin) {
int symbol = 0;
short lengths[FIXLCODES];
lencode.count = lencnt;
lencode.symbol = lensym;
distcode.count = distcnt;
distcode.symbol = distsym;
for(; symbol < 144; symbol++) lengths[symbol] = 8;
for(; symbol < 256; symbol++) lengths[symbol] = 9;
for(; symbol < 280; symbol++) lengths[symbol] = 7;
for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
construct(&lencode, lengths, FIXLCODES);
for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
construct(&distcode, lengths, MAXDCODES);
virgin = 0;
}
return codes(s, &lencode, &distcode);
}
inline int dynamic(state *s) {
int nlen, ndist, ncode, index, err;
short lengths[MAXCODES];
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
huffman lencode, distcode;
static const short order[19] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
lencode.count = lencnt;
lencode.symbol = lensym;
distcode.count = distcnt;
distcode.symbol = distsym;
nlen = bits(s, 5) + 257;
ndist = bits(s, 5) + 1;
ncode = bits(s, 4) + 4;
if(nlen > MAXLCODES || ndist > MAXDCODES) return -3;
for(index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3);
for(; index < 19; index++) lengths[order[index]] = 0;
err = construct(&lencode, lengths, 19);
if(err != 0) return -4;
index = 0;
while(index < nlen + ndist) {
int symbol, len;
symbol = decode(s, &lencode);
if(symbol < 16) {
lengths[index++] = symbol;
} else {
len = 0;
if(symbol == 16) {
if(index == 0) return -5;
len = lengths[index - 1];
symbol = 3 + bits(s, 2);
} else if(symbol == 17) {
symbol = 3 + bits(s, 3);
} else {
symbol = 11 + bits(s, 7);
}
if(index + symbol > nlen + ndist) return -6;
while(symbol--) lengths[index++] = len;
}
}
if(lengths[256] == 0) return -9;
err = construct(&lencode, lengths, nlen);
if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7;
err = construct(&distcode, lengths + nlen, ndist);
if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) return -8;
return codes(s, &lencode, &distcode);
}
inline int puff(
unsigned char *dest, unsigned long *destlen,
unsigned char *source, unsigned long *sourcelen
) {
state s;
int last, type, err;
s.out = dest;
s.outlen = *destlen;
s.outcnt = 0;
s.in = source;
s.inlen = *sourcelen;
s.incnt = 0;
s.bitbuf = 0;
s.bitcnt = 0;
if(setjmp(s.env) != 0) {
err = 2;
} else {
do {
last = bits(&s, 1);
type = bits(&s, 2);
err = type == 0 ? stored(&s)
: type == 1 ? fixed(&s)
: type == 2 ? dynamic(&s)
: -1;
if(err != 0) break;
} while(!last);
}
if(err <= 0) {
*destlen = s.outcnt;
*sourcelen = s.incnt;
}
return err;
}
}
}
#endif

View File

@@ -110,7 +110,7 @@ struct Keyboard {
break;
}
}
return string() << "KB" << ID << "::" << KeyboardScancodeName[index];
return { "KB", ID, "::", KeyboardScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
@@ -207,7 +207,7 @@ struct Mouse {
break;
}
}
return string() << "MS" << ID << "::" << MouseScancodeName[index];
return { "MS", ID, "::", MouseScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
@@ -330,7 +330,7 @@ struct Joypad {
index = code - (Base + Size * i);
}
}
return string() << "JP" << ID << "::" << JoypadScancodeName[index];
return { "JP", ID, "::", JoypadScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }

110
bsnes/nall/ips.hpp Executable file
View File

@@ -0,0 +1,110 @@
#ifndef NALL_IPS_HPP
#define NALL_IPS_HPP
#include <nall/file.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct ips {
inline bool apply();
inline void source(const uint8_t *data, unsigned size);
inline void modify(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool modify(const string &filename);
inline ips();
inline ~ips();
uint8_t *data;
unsigned size;
const uint8_t *sourceData;
unsigned sourceSize;
const uint8_t *modifyData;
unsigned modifySize;
};
bool ips::apply() {
if(modifySize < 8) return false;
if(modifyData[0] != 'P') return false;
if(modifyData[1] != 'A') return false;
if(modifyData[2] != 'T') return false;
if(modifyData[3] != 'C') return false;
if(modifyData[4] != 'H') return false;
if(data) delete[] data;
data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding
size = sourceSize;
memcpy(data, sourceData, sourceSize);
unsigned offset = 5;
while(true) {
unsigned address, length;
if(offset > modifySize - 3) break;
address = modifyData[offset++] << 16;
address |= modifyData[offset++] << 8;
address |= modifyData[offset++] << 0;
if(address == 0x454f46) { //EOF
if(offset == modifySize) return true;
if(offset == modifySize - 3) {
size = modifyData[offset++] << 16;
size |= modifyData[offset++] << 8;
size |= modifyData[offset++] << 0;
return true;
}
}
if(offset > modifySize - 2) break;
length = modifyData[offset++] << 8;
length |= modifyData[offset++] << 0;
if(length) { //Copy
if(offset > modifySize - length) break;
while(length--) data[address++] = modifyData[offset++];
} else { //RLE
if(offset > modifySize - 3) break;
length = modifyData[offset++] << 8;
length |= modifyData[offset++] << 0;
if(length == 0) break; //illegal
while(length--) data[address++] = modifyData[offset];
offset++;
}
size = max(size, address);
}
delete[] data;
data = 0;
return false;
}
void ips::source(const uint8_t *data, unsigned size) {
sourceData = data, sourceSize = size;
}
void ips::modify(const uint8_t *data, unsigned size) {
modifyData = data, modifySize = size;
}
bool ips::source(const string &filename) {
return file::read(filename, sourceData, sourceSize);
}
bool ips::modify(const string &filename) {
return file::read(filename, modifyData, modifySize);
}
ips::ips() : data(0), sourceData(0), modifyData(0) {
}
ips::~ips() {
if(data) delete[] data;
if(sourceData) delete[] sourceData;
if(modifyData) delete[] modifyData;
}
}
#endif

View File

@@ -1,81 +1,165 @@
#ifndef NALL_LZSS_HPP
#define NALL_LZSS_HPP
#include <nall/array.hpp>
#include <nall/new.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
class lzss {
public:
static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) {
output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9];
unsigned i = 0, o = 0;
while(i < inlength) {
unsigned flagoffset = o++;
uint8_t flag = 0x00;
//19:5 pulldown
//8:1 marker: d7-d0
//length: { 4 - 35 }, offset: { 1 - 0x80000 }
//4-byte file size header
//little-endian encoding
struct lzss {
inline void source(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline unsigned size() const;
inline bool compress(const string &filename);
inline bool decompress(uint8_t *targetData, unsigned targetSize);
inline bool decompress(const string &filename);
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++;
}
protected:
struct Node {
unsigned offset;
Node *next;
inline Node() : offset(0), next(0) {}
inline ~Node() { if(next) delete next; }
} *tree[65536];
if(count > longest) {
longest = count;
pointer = index;
}
}
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
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;
}
public:
inline lzss() : sourceData(0), sourceSize(0) {}
};
void lzss::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
bool lzss::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
sourceData = sourceFile.data();
sourceSize = sourceFile.size();
return true;
}
unsigned lzss::size() const {
unsigned size = 0;
if(sourceSize < 4) return size;
for(unsigned n = 0; n < 32; n += 8) size |= sourceData[n >> 3] << n;
return size;
}
bool lzss::compress(const string &filename) {
file targetFile;
if(targetFile.open(filename, file::mode::write) == false) return false;
for(unsigned n = 0; n < 32; n += 8) targetFile.write(sourceSize >> n);
for(unsigned n = 0; n < 65536; n++) tree[n] = 0;
uint8_t buffer[25];
unsigned sourceOffset = 0;
while(sourceOffset < sourceSize) {
uint8_t mask = 0x00;
unsigned bufferOffset = 1;
for(unsigned iteration = 0; iteration < 8; iteration++) {
if(sourceOffset >= sourceSize) break;
uint16_t symbol = sourceData[sourceOffset + 0];
if(sourceOffset < sourceSize - 1) symbol |= sourceData[sourceOffset + 1] << 8;
Node *node = tree[symbol];
unsigned maxLength = 0, maxOffset = 0;
while(node) {
if(node->offset < sourceOffset - 0x80000) {
//out-of-range: all subsequent nodes will also be, so free up their memory
if(node->next) { delete node->next; node->next = 0; }
break;
}
output[flagoffset] = flag;
unsigned length = 0, x = sourceOffset, y = node->offset;
while(length < 35 && x < sourceSize && sourceData[x++] == sourceData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset;
if(length == 35) break;
node = node->next;
}
outlength = o;
return true;
}
//attach current symbol to top of tree for subsequent searches
node = new Node;
node->offset = sourceOffset;
node->next = tree[symbol];
tree[symbol] = node;
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;
}
}
if(maxLength < 4) {
buffer[bufferOffset++] = sourceData[sourceOffset++];
} else {
unsigned output = ((maxLength - 4) << 19) | (sourceOffset - 1 - maxOffset);
for(unsigned n = 0; n < 24; n += 8) buffer[bufferOffset++] = output >> n;
mask |= 0x80 >> iteration;
sourceOffset += maxLength;
}
return true;
}
};
buffer[0] = mask;
targetFile.write(buffer, bufferOffset);
}
sourceFile.close();
targetFile.close();
return true;
}
bool lzss::decompress(uint8_t *targetData, unsigned targetSize) {
if(targetSize < size()) return false;
unsigned sourceOffset = 4, targetOffset = 0;
while(sourceOffset < sourceSize) {
uint8_t mask = sourceData[sourceOffset++];
for(unsigned iteration = 0; iteration < 8; iteration++) {
if(sourceOffset >= sourceSize) break;
if((mask & (0x80 >> iteration)) == 0) {
targetData[targetOffset++] = sourceData[sourceOffset++];
} else {
unsigned code = 0;
for(unsigned n = 0; n < 24; n += 8) code |= sourceData[sourceOffset++] << n;
unsigned length = (code >> 19) + 4;
unsigned offset = targetOffset - 1 - (code & 0x7ffff);
while(length--) targetData[targetOffset++] = targetData[offset++];
}
}
}
}
bool lzss::decompress(const string &filename) {
if(sourceSize < 4) return false;
unsigned targetSize = size();
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
fp.truncate(targetSize);
fp.close();
filemap targetFile;
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
uint8_t *targetData = targetFile.data();
bool result = decompress(targetData, targetSize);
sourceFile.close();
targetFile.close();
return result;
}
}
#endif

View File

@@ -1,7 +1,12 @@
#ifndef NALL_PLATFORM_HPP
#define NALL_PLATFORM_HPP
#include <nall/utf8.hpp>
#if defined(_WIN32)
//minimum version needed for _wstat64, etc
#undef __MSVCRT_VERSION__
#define __MSVCRT_VERSION__ 0x0601
#include <nall/windows/utf8.hpp>
#endif
//=========================
//standard platform headers
@@ -18,16 +23,19 @@
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(_WIN32)
#include <io.h>
#include <direct.h>
#include <shlobj.h>
#include <wchar.h>
#undef interface
#define dllexport __declspec(dllexport)
#else
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#define dllexport
#endif
@@ -53,11 +61,11 @@
#if defined(_WIN32)
#define getcwd _getcwd
#define ftruncate _chsize
#define putenv _putenv
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
#define putenv _putenv
#define rmdir _rmdir
#define vsnprintf _vsnprintf
#define usleep(n) Sleep(n / 1000)
#define vsnprintf _vsnprintf
#endif
//================
@@ -87,6 +95,7 @@
wchar_t fn[_MAX_PATH] = L"";
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
strcpy(resolvedname, nall::utf8_t(fn));
for(unsigned n = 0; resolvedname[n]; n++) if(resolvedname[n] == '\\') resolvedname[n] = '/';
return resolvedname;
}
@@ -94,6 +103,7 @@
wchar_t fp[_MAX_PATH] = L"";
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
strcpy(path, nall::utf8_t(fp));
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
@@ -101,6 +111,7 @@
wchar_t fp[_MAX_PATH] = L"";
_wgetcwd(fp, _MAX_PATH);
strcpy(path, nall::utf8_t(fp));
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
#else

423
bsnes/nall/png.hpp Executable file
View File

@@ -0,0 +1,423 @@
#ifndef NALL_PNG_HPP
#define NALL_PNG_HPP
//PNG image decoder
//author: byuu
#include <nall/inflate.hpp>
#include <nall/string.hpp>
namespace nall {
struct png {
uint32_t *data;
unsigned size;
struct Info {
unsigned width;
unsigned height;
unsigned bitDepth;
unsigned colorType;
unsigned compressionMethod;
unsigned filterType;
unsigned interlaceMethod;
unsigned bytesPerPixel;
unsigned pitch;
uint8_t palette[256][3];
} info;
uint8_t *rawData;
unsigned rawSize;
inline bool decode(const string &filename);
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
inline void transform();
inline void alphaTransform(uint32_t rgb = 0xffffff);
inline png();
inline ~png();
protected:
enum class FourCC : unsigned {
IHDR = 0x49484452,
PLTE = 0x504c5445,
IDAT = 0x49444154,
IEND = 0x49454e44,
};
static const unsigned interlace[7][4];
unsigned bitpos;
inline unsigned inflateSize();
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
inline unsigned read(const uint8_t *data, unsigned length);
inline unsigned decode(const uint8_t *&data);
inline unsigned readbits(const uint8_t *&data);
inline unsigned scale(unsigned n);
};
bool png::decode(const string &filename) {
uint8_t *data;
unsigned size;
if(file::read(filename, data, size) == false) return false;
bool result = decode(data, size);
delete[] data;
return result;
}
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
if(sourceSize < 8) return false;
if(read(sourceData + 0, 4) != 0x89504e47) return false;
if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false;
uint8_t *compressedData = 0;
unsigned compressedSize = 0;
unsigned offset = 8;
while(offset < sourceSize) {
unsigned length = read(sourceData + offset + 0, 4);
unsigned fourCC = read(sourceData + offset + 4, 4);
unsigned checksum = read(sourceData + offset + 8 + length, 4);
if(fourCC == (unsigned)FourCC::IHDR) {
info.width = read(sourceData + offset + 8, 4);
info.height = read(sourceData + offset + 12, 4);
info.bitDepth = read(sourceData + offset + 16, 1);
info.colorType = read(sourceData + offset + 17, 1);
info.compressionMethod = read(sourceData + offset + 18, 1);
info.filterType = read(sourceData + offset + 19, 1);
info.interlaceMethod = read(sourceData + offset + 20, 1);
if(info.bitDepth == 0 || info.bitDepth > 16) return false;
if(info.bitDepth & (info.bitDepth - 1)) return false; //not a power of two
if(info.compressionMethod != 0) return false;
if(info.filterType != 0) return false;
if(info.interlaceMethod != 0 && info.interlaceMethod != 1) return false;
switch(info.colorType) {
case 0: info.bytesPerPixel = info.bitDepth * 1; break; //L
case 2: info.bytesPerPixel = info.bitDepth * 3; break; //R,G,B
case 3: info.bytesPerPixel = info.bitDepth * 1; break; //P
case 4: info.bytesPerPixel = info.bitDepth * 2; break; //L,A
case 6: info.bytesPerPixel = info.bitDepth * 4; break; //R,G,B,A
default: return false;
}
if(info.colorType == 2 || info.colorType == 4 || info.colorType == 6)
if(info.bitDepth != 8 && info.bitDepth != 16) return false;
if(info.colorType == 3 && info.bitDepth == 16) return false;
info.bytesPerPixel = (info.bytesPerPixel + 7) / 8;
info.pitch = (int)info.width * info.bytesPerPixel;
}
if(fourCC == (unsigned)FourCC::PLTE) {
if(length % 3) return false;
for(unsigned n = 0, p = offset + 8; n < length / 3; n++) {
info.palette[n][0] = sourceData[p++];
info.palette[n][1] = sourceData[p++];
info.palette[n][2] = sourceData[p++];
}
}
if(fourCC == (unsigned)FourCC::IDAT) {
compressedData = (uint8_t*)realloc(compressedData, compressedSize + length);
memcpy(compressedData + compressedSize, sourceData + offset + 8, length);
compressedSize += length;
}
if(fourCC == (unsigned)FourCC::IEND) {
break;
}
offset += 4 + 4 + length + 4;
}
unsigned interlacedSize = inflateSize();
uint8_t *interlacedData = new uint8_t[interlacedSize];
bool result = inflate(interlacedData, interlacedSize, compressedData + 2, compressedSize - 6);
delete[] compressedData;
if(result == false) {
delete[] interlacedData;
return false;
}
rawSize = info.width * info.height * info.bytesPerPixel;
rawData = new uint8_t[rawSize];
if(info.interlaceMethod == 0) {
if(filter(rawData, interlacedData, info.width, info.height) == false) {
delete[] interlacedData;
delete[] rawData;
rawData = 0;
return false;
}
} else {
const uint8_t *passData = interlacedData;
for(unsigned pass = 0; pass < 7; pass++) {
if(deinterlace(passData, pass) == false) {
delete[] interlacedData;
delete[] rawData;
rawData = 0;
return false;
}
}
}
delete[] interlacedData;
return true;
}
const unsigned png::interlace[7][4] = {
//x-distance, y-distance, x-origin, y-origin
{ 8, 8, 0, 0 },
{ 8, 8, 4, 0 },
{ 4, 8, 0, 4 },
{ 4, 4, 2, 0 },
{ 2, 4, 0, 2 },
{ 2, 2, 1, 0 },
{ 1, 2, 0, 1 },
};
unsigned png::inflateSize() {
if(info.interlaceMethod == 0) {
return info.width * info.height * info.bytesPerPixel + info.height;
}
unsigned size = 0;
for(unsigned pass = 0; pass < 7; pass++) {
unsigned xd = interlace[pass][0], yd = interlace[pass][1];
unsigned xo = interlace[pass][2], yo = interlace[pass][3];
unsigned width = (info.width + (xd - xo - 1)) / xd;
unsigned height = (info.height + (yd - yo - 1)) / yd;
if(width == 0 || height == 0) continue;
size += width * height * info.bytesPerPixel + height;
}
return size;
}
bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
unsigned xd = interlace[pass][0], yd = interlace[pass][1];
unsigned xo = interlace[pass][2], yo = interlace[pass][3];
unsigned width = (info.width + (xd - xo - 1)) / xd;
unsigned height = (info.height + (yd - yo - 1)) / yd;
if(width == 0 || height == 0) return true;
unsigned outputSize = width * height * info.bytesPerPixel;
uint8_t *outputData = new uint8_t[outputSize];
bool result = filter(outputData, inputData, width, height);
const uint8_t *rd = outputData;
for(unsigned y = yo; y < info.height; y += yd) {
uint8_t *wr = rawData + y * info.pitch;
for(unsigned x = xo; x < info.width; x += xd) {
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
wr[x * info.bytesPerPixel + b] = *rd++;
}
}
}
inputData += outputSize + height;
delete[] outputData;
return result;
}
bool png::filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height) {
uint8_t *wr = outputData;
const uint8_t *rd = inputData;
int bpp = info.bytesPerPixel, pitch = width * bpp;
for(int y = 0; y < height; y++) {
uint8_t filter = *rd++;
switch(filter) {
case 0x00: //None
for(int x = 0; x < pitch; x++) {
wr[x] = rd[x];
}
break;
case 0x01: //Subtract
for(int x = 0; x < pitch; x++) {
wr[x] = rd[x] + (x - bpp < 0 ? 0 : wr[x - bpp]);
}
break;
case 0x02: //Above
for(int x = 0; x < pitch; x++) {
wr[x] = rd[x] + (y - 1 < 0 ? 0 : wr[x - pitch]);
}
break;
case 0x03: //Average
for(int x = 0; x < pitch; x++) {
short a = x - bpp < 0 ? 0 : wr[x - bpp];
short b = y - 1 < 0 ? 0 : wr[x - pitch];
wr[x] = rd[x] + (uint8_t)((a + b) / 2);
}
break;
case 0x04: //Paeth
for(int x = 0; x < pitch; x++) {
short a = x - bpp < 0 ? 0 : wr[x - bpp];
short b = y - 1 < 0 ? 0 : wr[x - pitch];
short c = x - bpp < 0 || y - 1 < 0 ? 0 : wr[x - pitch - bpp];
short p = a + b - c;
short pa = p > a ? p - a : a - p;
short pb = p > b ? p - b : b - p;
short pc = p > c ? p - c : c - p;
uint8_t paeth = (uint8_t)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
wr[x] = rd[x] + paeth;
}
break;
default: //Invalid
return false;
}
rd += pitch;
wr += pitch;
}
return true;
}
unsigned png::read(const uint8_t *data, unsigned length) {
unsigned result = 0;
while(length--) result = (result << 8) | (*data++);
return result;
}
unsigned png::decode(const uint8_t *&data) {
unsigned p, r, g, b, a;
switch(info.colorType) {
case 0: //L
r = g = b = scale(readbits(data));
a = 0xff;
break;
case 2: //R,G,B
r = scale(readbits(data));
g = scale(readbits(data));
b = scale(readbits(data));
a = 0xff;
break;
case 3: //P
p = readbits(data);
r = info.palette[p][0];
g = info.palette[p][1];
b = info.palette[p][2];
a = 0xff;
break;
case 4: //L,A
r = g = b = scale(readbits(data));
a = scale(readbits(data));
break;
case 6: //R,G,B,A
r = scale(readbits(data));
g = scale(readbits(data));
b = scale(readbits(data));
a = scale(readbits(data));
break;
}
return (a << 24) | (r << 16) | (g << 8) | (b << 0);
}
unsigned png::readbits(const uint8_t *&data) {
unsigned result = 0;
switch(info.bitDepth) {
case 1:
result = (*data >> bitpos) & 1;
bitpos++;
if(bitpos == 8) { data++; bitpos = 0; }
break;
case 2:
result = (*data >> bitpos) & 3;
bitpos += 2;
if(bitpos == 8) { data++; bitpos = 0; }
break;
case 4:
result = (*data >> bitpos) & 15;
bitpos += 4;
if(bitpos == 8) { data++; bitpos = 0; }
break;
case 8:
result = *data++;
break;
case 16:
result = (data[0] << 8) | (data[1] << 0);
data += 2;
break;
}
return result;
}
unsigned png::scale(unsigned n) {
switch(info.bitDepth) {
case 1: return n ? 0xff : 0x00;
case 2: return n * 0x55;
case 4: return n * 0x11;
case 8: return n;
case 16: return n >> 8;
}
return 0;
}
void png::transform() {
if(data) delete[] data;
data = new uint32_t[info.width * info.height];
bitpos = 0;
const uint8_t *rd = rawData;
for(unsigned y = 0; y < info.height; y++) {
uint32_t *wr = data + y * info.width;
for(unsigned x = 0; x < info.width; x++) {
wr[x] = decode(rd);
}
}
}
void png::alphaTransform(uint32_t rgb) {
transform();
uint8_t ir = rgb >> 16;
uint8_t ig = rgb >> 8;
uint8_t ib = rgb >> 0;
uint32_t *p = data;
for(unsigned y = 0; y < info.height; y++) {
for(unsigned x = 0; x < info.width; x++) {
uint32_t pixel = *p;
uint8_t a = pixel >> 24;
uint8_t r = pixel >> 16;
uint8_t g = pixel >> 8;
uint8_t b = pixel >> 0;
r = (r * a) + (ir * (255 - a)) >> 8;
g = (g * a) + (ig * (255 - a)) >> 8;
b = (b * a) + (ib * (255 - a)) >> 8;
*p++ = (255 << 24) | (r << 16) | (g << 8) | (b << 0);
}
}
}
png::png() : data(0), rawData(0) {
}
png::~png() {
if(data) delete[] data;
if(rawData) delete[] rawData;
}
}
#endif

61
bsnes/nall/resource.hpp Executable file
View File

@@ -0,0 +1,61 @@
#ifndef NALL_RESOURCE_HPP
#define NALL_RESOURCE_HPP
#include <nall/file.hpp>
#include <nall/zip.hpp>
namespace nall {
struct resource {
//create resource with "zip -9 resource.zip resource"
static bool encode(const char *outputFilename, const char *inputFilename) {
file fp;
if(fp.open(inputFilename, file::mode::read) == false) return false;
unsigned size = fp.size();
uint8_t *data = new uint8_t[size];
fp.read(data, size);
fp.close();
fp.open(outputFilename, file::mode::write);
fp.print("static const uint8_t data[", size, "] = {\n");
uint8_t *p = data;
while(size) {
fp.print(" ");
for(unsigned n = 0; n < 32 && size; n++, size--) {
fp.print((unsigned)*p++, ",");
}
fp.print("\n");
}
fp.print("};\n");
fp.close();
delete[] data;
}
uint8_t *data;
unsigned size;
//extract first file from ZIP archive
bool decode(const uint8_t *cdata, unsigned csize) {
if(data) delete[] data;
zip archive;
if(archive.open(cdata, csize) == false) return false;
if(archive.file.size() == 0) return false;
bool result = archive.extract(archive.file[0], data, size);
archive.close();
return result;
}
resource() : data(0), size(0) {
}
~resource() {
if(data) delete[] data;
}
};
}
#endif

View File

@@ -3,6 +3,8 @@
//author: vladitx
#include <nall/stdint.hpp>
namespace nall {
#define PTR(t, a) ((t*)(a))
@@ -49,7 +51,7 @@ namespace nall {
uint64_t len;
};
void sha256_init(sha256_ctx *p) {
inline void sha256_init(sha256_ctx *p) {
memset(p, 0, sizeof(sha256_ctx));
memcpy(p->h, T_H, sizeof(T_H));
}
@@ -90,7 +92,7 @@ namespace nall {
p->inlen = 0;
}
void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
inline void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
unsigned l;
p->len += len;
@@ -107,7 +109,7 @@ namespace nall {
}
}
void sha256_final(sha256_ctx *p) {
inline void sha256_final(sha256_ctx *p) {
uint64_t len;
p->in[p->inlen++] = 0x80;
@@ -124,7 +126,7 @@ namespace nall {
sha256_block(p);
}
void sha256_hash(sha256_ctx *p, uint8_t *s) {
inline void sha256_hash(sha256_ctx *p, uint8_t *s) {
uint32_t *t = (uint32_t*)s;
for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
}

View File

@@ -111,422 +111,426 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
string xml = "<?xml version='1.0' encoding='UTF-8'?>\n";
if(type == TypeBsx) {
xml << "<cartridge/>";
xml.append("<cartridge/>");
xmlMemoryMap = xml.transform("'", "\"");
return;
}
if(type == TypeSufamiTurbo) {
xml << "<cartridge/>";
xml.append("<cartridge/>");
xmlMemoryMap = xml.transform("'", "\"");
return;
}
if(type == TypeGameBoy) {
xml << "<cartridge rtc='" << gameboy_has_rtc(data, size) << "'>\n";
xml.append("<cartridge rtc='", gameboy_has_rtc(data, size), "'>\n");
if(gameboy_ram_size(data, size) > 0) {
xml << " <ram size='" << hex(gameboy_ram_size(data, size)) << "'/>\n";
xml.append(" <ram size='0x", hex(gameboy_ram_size(data, size)), "'/>\n");
}
xml << "</cartridge>\n";
xml.append("</cartridge>\n");
xmlMemoryMap = xml.transform("'", "\"");
return;
}
xml << "<cartridge";
xml.append("<cartridge");
if(region == NTSC) {
xml << " region='NTSC'";
xml.append(" region='NTSC'");
} else {
xml << " region='PAL'";
xml.append(" region='PAL'");
}
xml << ">\n";
xml.append(">\n");
if(type == TypeSuperGameBoy1Bios) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <icd2 revision='1'>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </icd2>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-7f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-ff:8000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <icd2 revision='1'>\n");
xml.append(" <map address='00-3f:6000-7fff'/>\n");
xml.append(" <map address='80-bf:6000-7fff'/>\n");
xml.append(" </icd2>\n");
} else if(type == TypeSuperGameBoy2Bios) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <icd2 revision='2'>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </icd2>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-7f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-ff:8000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <icd2 revision='2'>\n");
xml.append(" <map address='00-3f:6000-7fff'/>\n");
xml.append(" <map address='80-bf:6000-7fff'/>\n");
xml.append(" </icd2>\n");
} else if(has_cx4) {
xml.append(" <hitachidsp model='HG51B169' frequency='20000000' firmware='cx4.bin' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n");
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-7f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-ff:8000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <mmio>\n");
xml.append(" <map address='00-3f:6000-7fff'/>\n");
xml.append(" <map address='80-bf:6000-7fff'/>\n");
xml.append(" </mmio>\n");
xml.append(" </hitachidsp>\n");
} else if(has_spc7110) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-0f:8000-ffff'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-cf:0000-ffff'/>\n";
xml << " </rom>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='shadow' address='00-0f:8000-ffff'/>\n");
xml.append(" <map mode='shadow' address='80-bf:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='c0-cf:0000-ffff'/>\n");
xml.append(" </rom>\n");
xml << " <spc7110>\n";
xml << " <mcu>\n";
xml << " <map address='d0-ff:0000-ffff' offset='100000' size='" << hex(size - 0x100000) << "'/>\n";
xml << " </mcu>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='00:6000-7fff'/>\n";
xml << " <map mode='linear' address='30:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:4800-483f'/>\n";
xml << " <map address='80-bf:4800-483f'/>\n";
xml << " </mmio>\n";
xml.append(" <spc7110>\n");
xml.append(" <mcu>\n");
xml.append(" <map address='d0-ff:0000-ffff' offset='0x100000' size='0x", hex(size - 0x100000), "'/>\n");
xml.append(" </mcu>\n");
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='00:6000-7fff'/>\n");
xml.append(" <map mode='linear' address='30:6000-7fff'/>\n");
xml.append(" </ram>\n");
xml.append(" <mmio>\n");
xml.append(" <map address='00-3f:4800-483f'/>\n");
xml.append(" <map address='80-bf:4800-483f'/>\n");
xml.append(" </mmio>\n");
if(has_spc7110rtc) {
xml << " <rtc>\n";
xml << " <map address='00-3f:4840-4842'/>\n";
xml << " <map address='80-bf:4840-4842'/>\n";
xml << " </rtc>\n";
xml.append(" <rtc>\n");
xml.append(" <map address='00-3f:4840-4842'/>\n");
xml.append(" <map address='80-bf:4840-4842'/>\n");
xml.append(" </rtc>\n");
}
xml << " <dcu>\n";
xml << " <map address='50:0000-ffff'/>\n";
xml << " </dcu>\n";
xml << " </spc7110>\n";
xml.append(" <dcu>\n");
xml.append(" <map address='50:0000-ffff'/>\n");
xml.append(" </dcu>\n");
xml.append(" </spc7110>\n");
} else if(mapper == LoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-7f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-ff:8000-ffff'/>\n");
xml.append(" </rom>\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='20-3f:6000-7fff'/>\n");
xml.append(" <map mode='linear' address='a0-bf:6000-7fff'/>\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
xml.append(" <map mode='linear' address='70-7f:0000-7fff'/>\n");
xml.append(" <map mode='linear' address='f0-ff:0000-7fff'/>\n");
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-ffff'/>\n";
xml.append(" <map mode='linear' address='70-7f:0000-ffff'/>\n");
xml.append(" <map mode='linear' address='f0-ff:0000-ffff'/>\n");
}
xml << " </ram>\n";
xml.append(" </ram>\n");
}
} else if(mapper == HiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='shadow' address='00-3f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='40-7f:0000-ffff'/>\n");
xml.append(" <map mode='shadow' address='80-bf:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='c0-ff:0000-ffff'/>\n");
xml.append(" </rom>\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='20-3f:6000-7fff'/>\n");
xml.append(" <map mode='linear' address='a0-bf:6000-7fff'/>\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml.append(" <map mode='linear' address='70-7f:0000-7fff'/>\n");
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
xml.append(" <map mode='linear' address='70-7f:0000-ffff'/>\n");
}
xml << " </ram>\n";
xml.append(" </ram>\n");
}
} else if(mapper == ExLoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " </rom>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-3f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='40-7f:0000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-bf:8000-ffff'/>\n");
xml.append(" </rom>\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " </ram>\n";
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='20-3f:6000-7fff'/>\n");
xml.append(" <map mode='linear' address='a0-bf:6000-7fff'/>\n");
xml.append(" <map mode='linear' address='70-7f:0000-7fff'/>\n");
xml.append(" </ram>\n");
}
} else if(mapper == ExHiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-3f:8000-ffff' offset='400000'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff' offset='400000'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff' offset='000000'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff' offset='000000'/>\n";
xml << " </rom>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='shadow' address='00-3f:8000-ffff' offset='0x400000'/>\n");
xml.append(" <map mode='linear' address='40-7f:0000-ffff' offset='0x400000'/>\n");
xml.append(" <map mode='shadow' address='80-bf:8000-ffff' offset='0x000000'/>\n");
xml.append(" <map mode='linear' address='c0-ff:0000-ffff' offset='0x000000'/>\n");
xml.append(" </rom>\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='20-3f:6000-7fff'/>\n");
xml.append(" <map mode='linear' address='a0-bf:6000-7fff'/>\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml.append(" <map mode='linear' address='70-7f:0000-7fff'/>\n");
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
xml.append(" <map mode='linear' address='70-7f:0000-ffff'/>\n");
}
xml << " </ram>\n";
xml.append(" </ram>\n");
}
} else if(mapper == SuperFXROM) {
xml << " <superfx revision='2'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='00-3f:6000-7fff' size='2000'/>\n";
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:6000-7fff' size='2000'/>\n";
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
xml << " </ram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:3000-32ff'/>\n";
xml << " <map address='80-bf:3000-32ff'/>\n";
xml << " </mmio>\n";
xml << " </superfx>\n";
xml.append(" <superfx revision='2'>\n");
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-3f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='40-5f:0000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-bf:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='c0-df:0000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='00-3f:6000-7fff' size='0x2000'/>\n");
xml.append(" <map mode='linear' address='60-7f:0000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-bf:6000-7fff' size='0x2000'/>\n");
xml.append(" <map mode='linear' address='e0-ff:0000-ffff'/>\n");
xml.append(" </ram>\n");
xml.append(" <mmio>\n");
xml.append(" <map address='00-3f:3000-32ff'/>\n");
xml.append(" <map address='80-bf:3000-32ff'/>\n");
xml.append(" </mmio>\n");
xml.append(" </superfx>\n");
} else if(mapper == SA1ROM) {
xml << " <sa1>\n";
xml << " <mcu>\n";
xml << " <rom>\n";
xml << " <map mode='direct' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='direct' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='direct' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram>\n";
xml << " <map mode='direct' address='00-3f:6000-7fff'/>\n";
xml << " <map mode='direct' address='80-bf:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " </mcu>\n";
xml << " <iram size='800'>\n";
xml << " <map mode='linear' address='00-3f:3000-37ff'/>\n";
xml << " <map mode='linear' address='80-bf:3000-37ff'/>\n";
xml << " </iram>\n";
xml << " <bwram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='40-4f:0000-ffff'/>\n";
xml << " </bwram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:2200-23ff'/>\n";
xml << " <map address='80-bf:2200-23ff'/>\n";
xml << " </mmio>\n";
xml << " </sa1>\n";
xml.append(" <sa1>\n");
xml.append(" <mcu>\n");
xml.append(" <rom>\n");
xml.append(" <map mode='direct' address='00-3f:8000-ffff'/>\n");
xml.append(" <map mode='direct' address='80-bf:8000-ffff'/>\n");
xml.append(" <map mode='direct' address='c0-ff:0000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <ram>\n");
xml.append(" <map mode='direct' address='00-3f:6000-7fff'/>\n");
xml.append(" <map mode='direct' address='80-bf:6000-7fff'/>\n");
xml.append(" </ram>\n");
xml.append(" </mcu>\n");
xml.append(" <iram size='0x800'>\n");
xml.append(" <map mode='linear' address='00-3f:3000-37ff'/>\n");
xml.append(" <map mode='linear' address='80-bf:3000-37ff'/>\n");
xml.append(" </iram>\n");
xml.append(" <bwram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='40-4f:0000-ffff'/>\n");
xml.append(" </bwram>\n");
xml.append(" <mmio>\n");
xml.append(" <map address='00-3f:2200-23ff'/>\n");
xml.append(" <map address='80-bf:2200-23ff'/>\n");
xml.append(" </mmio>\n");
xml.append(" </sa1>\n");
} else if(mapper == BSCLoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-1f:8000-ffff' offset='000000'/>\n";
xml << " <map mode='linear' address='20-3f:8000-ffff' offset='100000'/>\n";
xml << " <map mode='linear' address='80-9f:8000-ffff' offset='200000'/>\n";
xml << " <map mode='linear' address='a0-bf:8000-ffff' offset='100000'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
xml << " </ram>\n";
xml << " <bsx>\n";
xml << " <slot>\n";
xml << " <map mode='linear' address='c0-ef:0000-ffff'/>\n";
xml << " </slot>\n";
xml << " </bsx>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-1f:8000-ffff' offset='0x000000'/>\n");
xml.append(" <map mode='linear' address='20-3f:8000-ffff' offset='0x100000'/>\n");
xml.append(" <map mode='linear' address='80-9f:8000-ffff' offset='0x200000'/>\n");
xml.append(" <map mode='linear' address='a0-bf:8000-ffff' offset='0x100000'/>\n");
xml.append(" </rom>\n");
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='70-7f:0000-7fff'/>\n");
xml.append(" <map mode='linear' address='f0-ff:0000-7fff'/>\n");
xml.append(" </ram>\n");
xml.append(" <bsx>\n");
xml.append(" <slot>\n");
xml.append(" <map mode='linear' address='c0-ef:0000-ffff'/>\n");
xml.append(" </slot>\n");
xml.append(" </bsx>\n");
} else if(mapper == BSCHiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-1f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='80-9f:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " <bsx>\n";
xml << " <slot>\n";
xml << " <map mode='shadow' address='20-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='a0-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
xml << " </slot>\n";
xml << " </bsx>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='shadow' address='00-1f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='40-5f:0000-ffff'/>\n");
xml.append(" <map mode='shadow' address='80-9f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='c0-df:0000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <ram size='0x", hex(ram_size), "'>\n");
xml.append(" <map mode='linear' address='20-3f:6000-7fff'/>\n");
xml.append(" <map mode='linear' address='a0-bf:6000-7fff'/>\n");
xml.append(" </ram>\n");
xml.append(" <bsx>\n");
xml.append(" <slot>\n");
xml.append(" <map mode='shadow' address='20-3f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='60-7f:0000-ffff'/>\n");
xml.append(" <map mode='shadow' address='a0-bf:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='e0-ff:0000-ffff'/>\n");
xml.append(" </slot>\n");
xml.append(" </bsx>\n");
} else if(mapper == BSXROM) {
xml << " <bsx>\n";
xml << " <mcu>\n";
xml << " <map address='00-3f:8000-ffff'/>\n";
xml << " <map address='80-bf:8000-ffff'/>\n";
xml << " <map address='40-7f:0000-ffff'/>\n";
xml << " <map address='c0-ff:0000-ffff'/>\n";
xml << " <map address='20-3f:6000-7fff'/>\n";
xml << " </mcu>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:5000-5fff'/>\n";
xml << " <map address='80-bf:5000-5fff'/>\n";
xml << " </mmio>\n";
xml << " </bsx>\n";
xml.append(" <bsx>\n");
xml.append(" <mcu>\n");
xml.append(" <map address='00-3f:8000-ffff'/>\n");
xml.append(" <map address='80-bf:8000-ffff'/>\n");
xml.append(" <map address='40-7f:0000-ffff'/>\n");
xml.append(" <map address='c0-ff:0000-ffff'/>\n");
xml.append(" <map address='20-3f:6000-7fff'/>\n");
xml.append(" </mcu>\n");
xml.append(" <mmio>\n");
xml.append(" <map address='00-3f:5000-5fff'/>\n");
xml.append(" <map address='80-bf:5000-5fff'/>\n");
xml.append(" </mmio>\n");
xml.append(" </bsx>\n");
} else if(mapper == STROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-1f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-9f:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <sufamiturbo>\n";
xml << " <slot id='A'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='20-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='a0-bf:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='20000'>\n";
xml << " <map mode='linear' address='60-63:8000-ffff'/>\n";
xml << " <map mode='linear' address='e0-e3:8000-ffff'/>\n";
xml << " </ram>\n";
xml << " </slot>\n";
xml << " <slot id='B'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='40-5f:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='20000'>\n";
xml << " <map mode='linear' address='70-73:8000-ffff'/>\n";
xml << " <map mode='linear' address='f0-f3:8000-ffff'/>\n";
xml << " </ram>\n";
xml << " </slot>\n";
xml << " </sufamiturbo>\n";
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='00-1f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='80-9f:8000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <sufamiturbo>\n");
xml.append(" <slot id='A'>\n");
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='20-3f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='a0-bf:8000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <ram size='0x20000'>\n");
xml.append(" <map mode='linear' address='60-63:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='e0-e3:8000-ffff'/>\n");
xml.append(" </ram>\n");
xml.append(" </slot>\n");
xml.append(" <slot id='B'>\n");
xml.append(" <rom>\n");
xml.append(" <map mode='linear' address='40-5f:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='c0-df:8000-ffff'/>\n");
xml.append(" </rom>\n");
xml.append(" <ram size='0x20000'>\n");
xml.append(" <map mode='linear' address='70-73:8000-ffff'/>\n");
xml.append(" <map mode='linear' address='f0-f3:8000-ffff'/>\n");
xml.append(" </ram>\n");
xml.append(" </slot>\n");
xml.append(" </sufamiturbo>\n");
}
if(has_srtc) {
xml << " <srtc>\n";
xml << " <map address='00-3f:2800-2801'/>\n";
xml << " <map address='80-bf:2800-2801'/>\n";
xml << " </srtc>\n";
xml.append(" <srtc>\n");
xml.append(" <map address='00-3f:2800-2801'/>\n");
xml.append(" <map address='80-bf:2800-2801'/>\n");
xml.append(" </srtc>\n");
}
if(has_sdd1) {
xml << " <sdd1>\n";
xml << " <mcu>\n";
xml << " <map address='c0-ff:0000-ffff'/>\n";
xml << " </mcu>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:4800-4807'/>\n";
xml << " <map address='80-bf:4800-4807'/>\n";
xml << " </mmio>\n";
xml << " </sdd1>\n";
}
if(has_cx4) {
xml << " <cx4>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </cx4>\n";
xml.append(" <sdd1>\n");
xml.append(" <mcu>\n");
xml.append(" <map address='c0-ff:0000-ffff'/>\n");
xml.append(" </mcu>\n");
xml.append(" <mmio>\n");
xml.append(" <map address='00-3f:4800-4807'/>\n");
xml.append(" <map address='80-bf:4800-4807'/>\n");
xml.append(" </mmio>\n");
xml.append(" </sdd1>\n");
}
if(has_dsp1) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
xml.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n");
if(dsp1_mapper == DSP1LoROM1MB) {
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml.append(" <dr>\n");
xml.append(" <map address='20-3f:8000-bfff'/>\n");
xml.append(" <map address='a0-bf:8000-bfff'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='20-3f:c000-ffff'/>\n");
xml.append(" <map address='a0-bf:c000-ffff'/>\n");
xml.append(" </sr>\n");
} else if(dsp1_mapper == DSP1LoROM2MB) {
xml << " <dr>\n";
xml << " <map address='60-6f:0000-3fff'/>\n";
xml << " <map address='e0-ef:0000-3fff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60-6f:4000-7fff'/>\n";
xml << " <map address='e0-ef:4000-7fff'/>\n";
xml << " </sr>\n";
xml.append(" <dr>\n");
xml.append(" <map address='60-6f:0000-3fff'/>\n");
xml.append(" <map address='e0-ef:0000-3fff'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='60-6f:4000-7fff'/>\n");
xml.append(" <map address='e0-ef:4000-7fff'/>\n");
xml.append(" </sr>\n");
} else if(dsp1_mapper == DSP1HiROM) {
xml << " <dr>\n";
xml << " <map address='00-1f:6000-6fff'/>\n";
xml << " <map address='80-9f:6000-6fff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='00-1f:7000-7fff'/>\n";
xml << " <map address='80-9f:7000-7fff'/>\n";
xml << " </sr>\n";
xml.append(" <dr>\n");
xml.append(" <map address='00-1f:6000-6fff'/>\n");
xml.append(" <map address='80-9f:6000-6fff'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='00-1f:7000-7fff'/>\n");
xml.append(" <map address='80-9f:7000-7fff'/>\n");
xml.append(" </sr>\n");
}
xml << " </necdsp>\n";
xml.append(" </necdsp>\n");
}
if(has_dsp2) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </necdsp>\n";
xml.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n");
xml.append(" <dr>\n");
xml.append(" <map address='20-3f:8000-bfff'/>\n");
xml.append(" <map address='a0-bf:8000-bfff'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='20-3f:c000-ffff'/>\n");
xml.append(" <map address='a0-bf:c000-ffff'/>\n");
xml.append(" </sr>\n");
xml.append(" </necdsp>\n");
}
if(has_dsp3) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </necdsp>\n";
xml.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n");
xml.append(" <dr>\n");
xml.append(" <map address='20-3f:8000-bfff'/>\n");
xml.append(" <map address='a0-bf:8000-bfff'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='20-3f:c000-ffff'/>\n");
xml.append(" <map address='a0-bf:c000-ffff'/>\n");
xml.append(" </sr>\n");
xml.append(" </necdsp>\n");
}
if(has_dsp4) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
xml << " <dr>\n";
xml << " <map address='30-3f:8000-bfff'/>\n";
xml << " <map address='b0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='30-3f:c000-ffff'/>\n";
xml << " <map address='b0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </necdsp>\n";
xml.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n");
xml.append(" <dr>\n");
xml.append(" <map address='30-3f:8000-bfff'/>\n");
xml.append(" <map address='b0-bf:8000-bfff'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='30-3f:c000-ffff'/>\n");
xml.append(" <map address='b0-bf:c000-ffff'/>\n");
xml.append(" </sr>\n");
xml.append(" </necdsp>\n");
}
if(has_obc1) {
xml << " <obc1>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </obc1>\n";
xml.append(" <obc1>\n");
xml.append(" <map address='00-3f:6000-7fff'/>\n");
xml.append(" <map address='80-bf:6000-7fff'/>\n");
xml.append(" </obc1>\n");
}
if(has_st010) {
xml << " <necdsp revision='upd96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
xml << " <dr>\n";
xml << " <map address='60:0000'/>\n";
xml << " <map address='e0:0000'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60:0001'/>\n";
xml << " <map address='e0:0001'/>\n";
xml << " </sr>\n";
xml << " <dp>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </dp>\n";
xml << " </necdsp>\n";
xml.append(" <necdsp model='uPD96050' frequency='10000000' firmware='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n");
xml.append(" <dr>\n");
xml.append(" <map address='60:0000'/>\n");
xml.append(" <map address='e0:0000'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='60:0001'/>\n");
xml.append(" <map address='e0:0001'/>\n");
xml.append(" </sr>\n");
xml.append(" <dp>\n");
xml.append(" <map address='68-6f:0000-0fff'/>\n");
xml.append(" <map address='e8-ef:0000-0fff'/>\n");
xml.append(" </dp>\n");
xml.append(" </necdsp>\n");
}
if(has_st011) {
xml << " <necdsp revision='upd96050' frequency='15000000' program='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n";
xml << " <dr>\n";
xml << " <map address='60:0000'/>\n";
xml << " <map address='e0:0000'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60:0001'/>\n";
xml << " <map address='e0:0001'/>\n";
xml << " </sr>\n";
xml << " <dp>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </dp>\n";
xml << " </necdsp>\n";
xml.append(" <necdsp model='uPD96050' frequency='15000000' firmware='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n");
xml.append(" <dr>\n");
xml.append(" <map address='60:0000'/>\n");
xml.append(" <map address='e0:0000'/>\n");
xml.append(" </dr>\n");
xml.append(" <sr>\n");
xml.append(" <map address='60:0001'/>\n");
xml.append(" <map address='e0:0001'/>\n");
xml.append(" </sr>\n");
xml.append(" <dp>\n");
xml.append(" <map address='68-6f:0000-0fff'/>\n");
xml.append(" <map address='e8-ef:0000-0fff'/>\n");
xml.append(" </dp>\n");
xml.append(" </necdsp>\n");
}
if(has_st018) {
xml << " <setarisc program='ST-0018'>\n";
xml << " <map address='00-3f:3800-38ff'/>\n";
xml << " <map address='80-bf:3800-38ff'/>\n";
xml << " </setarisc>\n";
xml.append(" <setarisc firmware='ST-0018'>\n");
xml.append(" <map address='00-3f:3800-38ff'/>\n");
xml.append(" <map address='80-bf:3800-38ff'/>\n");
xml.append(" </setarisc>\n");
}
xml << "</cartridge>\n";
xml.append("</cartridge>\n");
xmlMemoryMap = xml.transform("'", "\"");
}

29
bsnes/nall/stack.hpp Executable file
View File

@@ -0,0 +1,29 @@
#ifndef NALL_STACK_HPP
#define NALL_STACK_HPP
#include <nall/concept.hpp>
#include <nall/vector.hpp>
namespace nall {
template<typename T> struct stack : public linear_vector<T> {
void push(const T &value) {
linear_vector<T>::append(value);
}
T pull() {
if(linear_vector<T>::size() == 0) throw;
T value = linear_vector<T>::operator[](linear_vector<T>::size() - 1);
linear_vector<T>::remove(linear_vector<T>::size() - 1);
return value;
}
T& operator()() {
if(linear_vector<T>::size() == 0) throw;
return linear_vector<T>::operator[](linear_vector<T>::size() - 1);
}
};
template<typename T> struct has_size<stack<T>> { enum { value = true }; };
}
#endif

View File

@@ -2,7 +2,9 @@
#define NALL_STRING_HPP
#include <initializer_list>
#include <nall/array.hpp>
#include <nall/platform.hpp>
#include <nall/sha256.hpp>
#include <nall/utility.hpp>
#include <nall/string/base.hpp>

View File

@@ -6,12 +6,14 @@
#include <stdlib.h>
#include <string.h>
#include <nall/concept.hpp>
#include <nall/function.hpp>
#include <nall/stdint.hpp>
#include <nall/utf8.hpp>
#include <nall/vector.hpp>
#include <nall/windows/utf8.hpp>
namespace nall {
class string;
class lstring;
template<typename T> inline const char* to_string(T);
class string {
@@ -20,13 +22,13 @@ namespace nall {
template<typename... Args> inline string& assign(Args&&... args);
template<typename... Args> inline string& append(Args&&... args);
inline string& assign_(const char*);
inline string& append_(const char*);
inline bool readfile(const string&);
inline string& replace (const char*, const char*);
inline string& qreplace(const char*, const char*);
template<unsigned Limit = 0> inline string& replace(const char*, const char*);
template<unsigned Limit = 0> inline string& ireplace(const char*, const char*);
template<unsigned Limit = 0> inline string& qreplace(const char*, const char*);
template<unsigned Limit = 0> inline string& iqreplace(const char*, const char*);
inline unsigned length() const;
@@ -43,17 +45,18 @@ namespace nall {
inline string& lower();
inline string& upper();
inline string& qlower();
inline string& qupper();
inline string& transform(const char *before, const char *after);
template<unsigned limit = 0> inline string& ltrim(const char *key = " ");
template<unsigned limit = 0> inline string& rtrim(const char *key = " ");
template<unsigned limit = 0> inline string& trim (const char *key = " ");
template<unsigned limit = 0> inline string& trim(const char *key = " ", const char *rkey = 0);
inline optional<unsigned> position(const char *key) const;
inline optional<unsigned> iposition(const char *key) const;
inline optional<unsigned> qposition(const char *key) const;
template<typename T> inline string& operator= (T value);
template<typename T> inline string& operator<<(T value);
inline optional<unsigned> iqposition(const char *key) const;
inline operator const char*() const;
inline char* operator()();
@@ -74,10 +77,16 @@ namespace nall {
inline string(string&&);
inline ~string();
//internal functions
inline string& assign_(const char*);
inline string& append_(const char*);
protected:
char *data;
unsigned size;
template<unsigned Limit, bool Insensitive, bool Quoted> inline string& ureplace(const char*, const char*);
#if defined(QSTRING_H)
public:
inline operator QString() const;
@@ -89,36 +98,43 @@ namespace nall {
template<typename T> inline lstring& operator<<(T value);
inline optional<unsigned> find(const char*) const;
template<unsigned limit = 0> inline void split (const char*, const char*);
template<unsigned limit = 0> inline void qsplit(const char*, const char*);
template<unsigned Limit = 0> inline lstring& split(const char*, const char*);
template<unsigned Limit = 0> inline lstring& isplit(const char*, const char*);
template<unsigned Limit = 0> inline lstring& qsplit(const char*, const char*);
template<unsigned Limit = 0> inline lstring& iqsplit(const char*, const char*);
lstring();
lstring(std::initializer_list<string>);
protected:
template<unsigned Limit, bool Insensitive, bool Quoted> inline lstring& usplit(const char*, const char*);
};
//compare.hpp
inline char chrlower(char c);
inline char chrupper(char c);
inline int stricmp(const char *str1, const char *str2);
inline int istrcmp(const char *str1, const char *str2);
inline bool wildcard(const char *str, const char *pattern);
inline bool iwildcard(const char *str, const char *pattern);
inline bool strbegin (const char *str, const char *key);
inline bool stribegin(const char *str, const char *key);
inline bool strend (const char *str, const char *key);
inline bool striend(const char *str, const char *key);
inline bool strbegin(const char *str, const char *key);
inline bool istrbegin(const char *str, const char *key);
inline bool strend(const char *str, const char *key);
inline bool istrend(const char *str, const char *key);
//convert.hpp
inline char* strlower(char *str);
inline char* strupper(char *str);
inline char* qstrlower(char *str);
inline char* qstrupper(char *str);
inline char* strtr(char *dest, const char *before, const char *after);
inline uintmax_t hex (const char *str);
inline intmax_t integer(const char *str);
inline uintmax_t hex(const char *str);
inline intmax_t integer(const char *str);
inline uintmax_t decimal(const char *str);
inline uintmax_t binary (const char *str);
inline double fp (const char *str);
inline uintmax_t binary(const char *str);
inline double fp(const char *str);
//math.hpp
inline bool strint (const char *str, int &result);
inline bool strint(const char *str, int &result);
inline bool strmath(const char *str, int &result);
//platform.hpp
@@ -132,26 +148,31 @@ namespace nall {
//strpos.hpp
inline optional<unsigned> strpos(const char *str, const char *key);
inline optional<unsigned> istrpos(const char *str, const char *key);
inline optional<unsigned> qstrpos(const char *str, const char *key);
inline optional<unsigned> iqstrpos(const char *str, const char *key);
template<bool Insensitive = false, bool Quoted = false> inline optional<unsigned> ustrpos(const char *str, const char *key);
//trim.hpp
template<unsigned limit = 0> inline char* ltrim(char *str, const char *key = " ");
template<unsigned limit = 0> inline char* rtrim(char *str, const char *key = " ");
template<unsigned limit = 0> inline char* trim (char *str, const char *key = " ");
template<unsigned limit = 0> inline char* trim(char *str, const char *key = " ", const char *rkey = 0);
//utility.hpp
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
template<bool Quoted, typename T> alwaysinline bool quoteskip(T *&p);
template<bool Quoted, typename T> alwaysinline bool quotecopy(char *&t, T *&p);
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
inline unsigned strlcat(string &dest, const char *src, unsigned length);
inline string substr(const char *src, unsigned start = 0, unsigned length = 0);
inline string substr(const char *src, unsigned start = 0, unsigned length = ~0u);
inline string sha256(const uint8_t *data, unsigned size);
inline string integer(intmax_t value);
template<unsigned length = 0> inline string linteger(intmax_t value);
template<unsigned length = 0> inline string rinteger(intmax_t value);
inline string decimal(uintmax_t value);
template<unsigned length = 0> inline string ldecimal(uintmax_t value);
template<unsigned length = 0> inline string rdecimal(uintmax_t value);
template<unsigned length = 0> inline string hex(uintmax_t value);
template<unsigned length = 0> inline string binary(uintmax_t value);
template<unsigned length = 0, char padding = ' '> inline string integer(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string linteger(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string decimal(uintmax_t value);
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
inline unsigned fp(char *str, double value);
inline string fp(double value);

View File

@@ -6,16 +6,15 @@ namespace nall {
//this is needed, as C++0x does not support explicit template specialization inside classes
template<> inline const char* to_string<bool> (bool v) { return v ? "true" : "false"; }
template<> inline const char* to_string<signed int> (signed int v) { static char temp[256]; snprintf(temp, 255, "%+d", v); return temp; }
template<> inline const char* to_string<unsigned int> (unsigned int v) { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
template<> inline const char* to_string<double> (double v) { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
template<> inline const char* to_string<unsigned int> (unsigned int v) { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
template<> inline const char* to_string<intmax_t> (intmax_t v) { static char temp[256]; snprintf(temp, 255, "%+lld", (long long)v); return temp; }
template<> inline const char* to_string<uintmax_t> (uintmax_t v) { static char temp[256]; snprintf(temp, 255, "%llu", (unsigned long long)v); return temp; }
template<> inline const char* to_string<double> (double v) { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
template<> inline const char* to_string<char*> (char *v) { return v; }
template<> inline const char* to_string<const char*> (const char *v) { return v; }
template<> inline const char* to_string<string> (string v) { return v; }
template<> inline const char* to_string<const string&>(const string &v) { return v; }
template<typename T> string& string::operator= (T value) { return assign(to_string<T>(value)); }
template<typename T> string& string::operator<<(T value) { return append(to_string<T>(value)); }
template<typename T> lstring& lstring::operator<<(T value) {
operator[](size()).assign(to_string<T>(value));
return *this;

View File

@@ -11,7 +11,7 @@ char chrupper(char c) {
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
}
int stricmp(const char *str1, const char *str2) {
int istrcmp(const char *str1, const char *str2) {
while(*str1) {
if(chrlower(*str1) != chrlower(*str2)) break;
str1++, str2++;
@@ -66,7 +66,7 @@ bool strbegin(const char *str, const char *key) {
return (!memcmp(str, key, ksl));
}
bool stribegin(const char *str, const char *key) {
bool istrbegin(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
@@ -89,7 +89,7 @@ bool strend(const char *str, const char *key) {
return (!memcmp(str + ssl - ksl, key, ksl));
}
bool striend(const char *str, const char *key) {
bool istrend(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;

View File

@@ -23,6 +23,26 @@ char* strupper(char *str) {
return str;
}
char* qstrlower(char *s) {
if(!s) return 0;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
s++;
}
}
char* qstrupper(char *s) {
if(!s) return 0;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
s++;
}
}
char* strtr(char *dest, const char *before, const char *after) {
if(!dest || !before || !after) return dest;
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);

View File

@@ -3,6 +3,8 @@
namespace nall {
static function<int64_t (const char *&)> eval_fallback;
static int eval_integer(const char *&s) {
if(!*s) throw "unrecognized_integer";
int value = 0, x = *s, y = *(s + 1);
@@ -58,7 +60,7 @@ static int eval_integer(const char *&s) {
}
static int eval(const char *&s, int depth = 0) {
while(*s == ' ' || *s == '\t') s++; //trim whitespace
while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) throw "unrecognized_token";
int value = 0, x = *s, y = *(s + 1);
@@ -74,10 +76,12 @@ static int eval(const char *&s, int depth = 0) {
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
else throw "unrecognized_token";
while(true) {
while(*s == ' ' || *s == '\t') s++; //trim whitespace
while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) break;
x = *s, y = *(s + 1);

View File

@@ -3,100 +3,49 @@
namespace nall {
string& string::replace(const char *key, const char *token) {
int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
unsigned int replace_count = 0, size = ssl;
char *buffer;
template<unsigned Limit, bool Insensitive, bool Quoted>
string& string::ureplace(const char *key, const char *token) {
if(!key || !*key) return *this;
enum : unsigned { limit = Limit ? Limit : ~0u };
if(ksl <= ssl) {
if(tsl > ksl) { //the new string may be longer than the old string...
for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need...
if(!memcmp(data + i, key, ksl)) {
replace_count++;
i += ksl;
} else i++;
}
size = ssl + ((tsl - ksl) * replace_count);
reserve(size);
const char *p = data;
unsigned counter = 0, keyLength = 0;
while(*p) {
if(quoteskip<Quoted>(p)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) { counter++; p += n; keyLength = n; break; }
if(!chrequal<Insensitive>(key[n], p[n])) { p++; break; }
}
buffer = new char[size + 1];
for(i = z = 0; i < ssl;) {
if(i <= ssl - ksl) {
if(!memcmp(data + i, key, ksl)) {
memcpy(buffer + z, token, tsl);
z += tsl;
i += ksl;
} else buffer[z++] = data[i++];
} else buffer[z++] = data[i++];
}
buffer[z] = 0;
assign(buffer);
delete[] buffer;
}
if(counter == 0) return *this;
if(Limit) counter = min(counter, Limit);
char *t = data, *base;
unsigned tokenLength = strlen(token);
if(tokenLength > keyLength) {
t = base = strdup(data);
reserve((unsigned)(p - data) + ((tokenLength - keyLength) * counter));
}
char *o = data;
while(*t && counter) {
if(quotecopy<Quoted>(o, t)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) { counter--; memcpy(o, token, tokenLength); t += keyLength; o += tokenLength; break; }
if(!chrequal<Insensitive>(key[n], t[n])) { *o++ = *t++; break; }
}
}
do *o++ = *t; while(*t++);
if(tokenLength > keyLength) free(base);
return *this;
}
string& string::qreplace(const char *key, const char *token) {
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
unsigned int replace_count = 0, size = ssl;
uint8_t x;
char *buffer;
if(ksl <= ssl) {
if(tsl > ksl) {
for(i = 0; i <= ssl - ksl;) {
x = data[i];
if(x == '\"' || x == '\'') {
l = i;
i++;
while(data[i++] != x) {
if(i == ssl) {
i = l;
break;
}
}
}
if(!memcmp(data + i, key, ksl)) {
replace_count++;
i += ksl;
} else i++;
}
size = ssl + ((tsl - ksl) * replace_count);
reserve(size);
}
buffer = new char[size + 1];
for(i = z = 0; i < ssl;) {
x = data[i];
if(x == '\"' || x == '\'') {
l = i++;
while(data[i] != x && i < ssl)i++;
if(i >= ssl)i = l;
else {
memcpy(buffer + z, data + l, i - l);
z += i - l;
}
}
if(i <= ssl - ksl) {
if(!memcmp(data + i, key, ksl)) {
memcpy(buffer + z, token, tsl);
z += tsl;
i += ksl;
replace_count++;
} else buffer[z++] = data[i++];
} else buffer[z++] = data[i++];
}
buffer[z] = 0;
assign(buffer);
delete[] buffer;
}
return *this;
}
template<unsigned Limit> string &string::replace(const char *key, const char *token) { return ureplace<Limit, false, false>(key, token); }
template<unsigned Limit> string &string::ireplace(const char *key, const char *token) { return ureplace<Limit, true, false>(key, token); }
template<unsigned Limit> string &string::qreplace(const char *key, const char *token) { return ureplace<Limit, false, true>(key, token); }
template<unsigned Limit> string &string::iqreplace(const char *key, const char *token) { return ureplace<Limit, true, true>(key, token); }
};

View File

@@ -3,56 +3,36 @@
namespace nall {
template<unsigned Limit> void lstring::split(const char *key, const char *src) {
unsigned limit = Limit;
template<unsigned Limit, bool Insensitive, bool Quoted> lstring& lstring::usplit(const char *key, const char *base) {
reset();
if(!key || !*key) return *this;
int ssl = strlen(src), ksl = strlen(key);
int lp = 0, split_count = 0;
const char *p = base;
unsigned counter = 0;
for(int i = 0; i <= ssl - ksl;) {
if(!memcmp(src + i, key, ksl)) {
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
i += ksl;
lp = i;
if(!--limit) break;
} else i++;
}
operator[](split_count++) = src + lp;
}
template<unsigned Limit> void lstring::qsplit(const char *key, const char *src) {
unsigned limit = Limit;
reset();
int ssl = strlen(src), ksl = strlen(key);
int lp = 0, split_count = 0;
for(int i = 0; i <= ssl - ksl;) {
uint8_t x = src[i];
if(x == '\"' || x == '\'') {
int z = i++; //skip opening quote
while(i < ssl && src[i] != x) i++;
if(i >= ssl) i = z; //failed match, rewind i
else {
i++; //skip closing quote
continue; //restart in case next char is also a quote
while(*p) {
if(Limit) if(counter >= Limit) break;
if(quoteskip<Quoted>(p)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) {
strlcpy(operator[](counter++), base, (unsigned)(p - base + 1));
p += n;
base = p;
break;
}
if(!chrequal<Insensitive>(key[n], p[n])) { p++; break; }
}
if(!memcmp(src + i, key, ksl)) {
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
i += ksl;
lp = i;
if(!--limit) break;
} else i++;
}
operator[](split_count++) = src + lp;
operator[](counter) = base;
return *this;
}
template<unsigned Limit> lstring& lstring::split(const char *key, const char *src) { return usplit<Limit, false, false>(key, src); }
template<unsigned Limit> lstring& lstring::isplit(const char *key, const char *src) { return usplit<Limit, true, false>(key, src); }
template<unsigned Limit> lstring& lstring::qsplit(const char *key, const char *src) { return usplit<Limit, false, true>(key, src); }
template<unsigned Limit> lstring& lstring::iqsplit(const char *key, const char *src) { return usplit<Limit, true, true>(key, src); }
};
#endif

View File

@@ -2,40 +2,33 @@
#define NALL_STRING_STRPOS_HPP
//usage example:
//if(auto pos = strpos(str, key)) print(pos(), "\n");
//prints position of key within str, only if it is found
//if(auto position = strpos(str, key)) print(position(), "\n");
//prints position of key within str; but only if it is found
namespace nall {
optional<unsigned> strpos(const char *str, const char *key) {
unsigned ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return { false, 0 };
template<bool Insensitive, bool Quoted>
optional<unsigned> ustrpos(const char *str, const char *key) {
const char *base = str;
for(unsigned i = 0; i <= ssl - ksl; i++) {
if(!memcmp(str + i, key, ksl)) return { true, i };
}
return { false, 0 };
}
optional<unsigned> qstrpos(const char *str, const char *key) {
unsigned ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return { false, 0 };
for(unsigned i = 0; i <= ssl - ksl;) {
uint8_t x = str[i];
if(x == '\"' || x == '\'') {
uint8_t z = i++;
while(str[i] != x && i < ssl) i++;
if(i >= ssl) i = z;
while(*str) {
if(quoteskip<Quoted>(str)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) return { true, (unsigned)(str - base) };
if(str[n] == 0) return { false, 0 };
if(!chrequal<Insensitive>(str[n], key[n])) break;
}
if(!memcmp(str + i, key, ksl)) return { true, i };
i++;
str++;
}
return { false, 0 };
}
optional<unsigned> strpos(const char *str, const char *key) { return ustrpos<false, false>(str, key); }
optional<unsigned> istrpos(const char *str, const char *key) { return ustrpos<true, false>(str, key); }
optional<unsigned> qstrpos(const char *str, const char *key) { return ustrpos<false, true>(str, key); }
optional<unsigned> iqstrpos(const char *str, const char *key) { return ustrpos<true, true>(str, key); }
}
#endif

View File

@@ -29,7 +29,8 @@ template<unsigned Limit> char* rtrim(char *str, const char *key) {
return str;
}
template<unsigned limit> char* trim(char *str, const char *key) {
template<unsigned limit> char* trim(char *str, const char *key, const char *rkey) {
if(rkey) return ltrim<limit>(rtrim<limit>(str, rkey), key);
return ltrim<limit>(rtrim<limit>(str, key), key);
}

View File

@@ -3,6 +3,38 @@
namespace nall {
template<bool Insensitive>
bool chrequal(char x, char y) {
if(Insensitive) return chrlower(x) == chrlower(y);
return x == y;
}
template<bool Quoted, typename T>
bool quoteskip(T *&p) {
if(Quoted == false) return false;
if(*p != '\'' && *p != '\"') return false;
while(*p == '\'' || *p == '\"') {
char x = *p++;
while(*p && *p++ != x);
}
return true;
}
template<bool Quoted, typename T>
bool quotecopy(char *&t, T *&p) {
if(Quoted == false) return false;
if(*p != '\'' && *p != '\"') return false;
while(*p == '\'' || *p == '\"') {
char x = *p++;
*t++ = x;
while(*p && *p != x) *t++ = *p++;
*t++ = *p++;
}
return true;
}
unsigned strlcpy(string &dest, const char *src, unsigned length) {
dest.reserve(length);
return strlcpy(dest(), src, length);
@@ -15,7 +47,7 @@ unsigned strlcat(string &dest, const char *src, unsigned length) {
string substr(const char *src, unsigned start, unsigned length) {
string dest;
if(length == 0) {
if(length == ~0u) {
//copy entire string
dest = src + start;
} else {
@@ -25,35 +57,21 @@ string substr(const char *src, unsigned start, unsigned length) {
return dest;
}
string sha256(const uint8_t *data, unsigned size) {
sha256_ctx sha;
uint8_t hash[32];
sha256_init(&sha);
sha256_chunk(&sha, data, size);
sha256_final(&sha);
sha256_hash(&sha, hash);
string result;
foreach(byte, hash) result.append(hex<2>(byte));
return result;
}
/* arithmetic <> string */
string integer(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
char result[size + 1];
memset(result, '0', size);
result[size] = 0;
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string linteger(intmax_t value) {
template<unsigned length_, char padding> string integer(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
@@ -70,34 +88,7 @@ template<unsigned length_> string linteger(intmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string rinteger(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
@@ -107,29 +98,10 @@ template<unsigned length_> string rinteger(intmax_t value) {
return (const char*)result;
}
string decimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size] = 0;
char result[size + 1];
memset(result, '0', size);
result[size] = 0;
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string ldecimal(uintmax_t value) {
template<unsigned length_, char padding> string linteger(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
char buffer[64];
unsigned size = 0;
@@ -138,11 +110,12 @@ template<unsigned length_> string ldecimal(uintmax_t value) {
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
memset(result, padding, length);
result[length] = 0;
for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
@@ -152,7 +125,7 @@ template<unsigned length_> string ldecimal(uintmax_t value) {
return (const char*)result;
}
template<unsigned length_> string rdecimal(uintmax_t value) {
template<unsigned length_, char padding> string decimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
@@ -165,7 +138,7 @@ template<unsigned length_> string rdecimal(uintmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
@@ -175,7 +148,30 @@ template<unsigned length_> string rdecimal(uintmax_t value) {
return (const char*)result;
}
template<unsigned length_> string hex(uintmax_t value) {
template<unsigned length_, char padding> string ldecimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, padding, length);
result[length] = 0;
for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_, char padding> string hex(uintmax_t value) {
char buffer[64];
unsigned size = 0;
@@ -187,7 +183,7 @@ template<unsigned length_> string hex(uintmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
@@ -197,7 +193,7 @@ template<unsigned length_> string hex(uintmax_t value) {
return (const char*)result;
}
template<unsigned length_> string binary(uintmax_t value) {
template<unsigned length_, char padding> string binary(uintmax_t value) {
char buffer[256];
unsigned size = 0;
@@ -209,7 +205,7 @@ template<unsigned length_> string binary(uintmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {

View File

@@ -6,27 +6,31 @@ namespace nall {
unsigned string::length() const { return strlen(data); }
bool string::equals(const char *str) const { return !strcmp(data, str); }
bool string::iequals(const char *str) const { return !stricmp(data, str); }
bool string::iequals(const char *str) const { return !istrcmp(data, str); }
bool string::wildcard(const char *str) const { return nall::wildcard(data, str); }
bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); }
bool string::beginswith(const char *str) const { return strbegin(data, str); }
bool string::ibeginswith(const char *str) const { return stribegin(data, str); }
bool string::ibeginswith(const char *str) const { return istrbegin(data, str); }
bool string::endswith(const char *str) const { return strend(data, str); }
bool string::iendswith(const char *str) const { return striend(data, str); }
bool string::iendswith(const char *str) const { return istrend(data, str); }
string& string::lower() { nall::strlower(data); return *this; }
string& string::upper() { nall::strupper(data); return *this; }
string& string::qlower() { nall::qstrlower(data); return *this; }
string& string::qupper() { nall::qstrupper(data); return *this; }
string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; }
template<unsigned limit> string& string::ltrim(const char *key) { nall::ltrim<limit>(data, key); return *this; }
template<unsigned limit> string& string::rtrim(const char *key) { nall::rtrim<limit>(data, key); return *this; }
template<unsigned limit> string& string::trim (const char *key) { nall::trim <limit>(data, key); return *this; }
template<unsigned limit> string& string::trim(const char *key, const char *rkey) { nall::trim <limit>(data, key, rkey); return *this; }
optional<unsigned> string::position(const char *key) const { return strpos(data, key); }
optional<unsigned> string::iposition(const char *key) const { return istrpos(data, key); }
optional<unsigned> string::qposition(const char *key) const { return qstrpos(data, key); }
optional<unsigned> string::iqposition(const char *key) const { return iqstrpos(data, key); }
}

View File

@@ -77,7 +77,7 @@ inline string xml_element::parse() const {
if(auto pos = strpos(source, "]]>")) {
if(pos() - 9 > 0) {
string cdata = substr(source, 9, pos() - 9);
data << cdata;
data.append(cdata);
offset += strlen(cdata);
}
source += 9 + offset + 3;

View File

@@ -26,6 +26,7 @@ namespace nall {
public:
inline operator bool() const { return valid; }
inline const T& operator()() const { if(!valid) throw; return value; }
inline optional<T>& operator=(const optional<T> &source) { valid = source.valid; value = source.value; return *this; }
inline optional(bool valid, const T &value) : valid(valid), value(value) {}
};

View File

@@ -94,14 +94,15 @@ namespace nall {
else resize(objectsize - count);
}
inline T& operator[](unsigned index) {
if(index >= objectsize) resize(index + 1);
return pool[index];
linear_vector() : pool(0), poolsize(0), objectsize(0) {
}
inline const T& operator[](unsigned index) const {
if(index >= objectsize) throw "vector[] out of bounds";
return pool[index];
linear_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
}
~linear_vector() {
reset();
}
//copy
@@ -132,17 +133,22 @@ namespace nall {
operator=(std::move(source));
}
//construction
linear_vector() : pool(0), poolsize(0), objectsize(0) {
//index
inline T& operator[](unsigned index) {
if(index >= objectsize) resize(index + 1);
return pool[index];
}
linear_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
inline const T& operator[](unsigned index) const {
if(index >= objectsize) throw "vector[] out of bounds";
return pool[index];
}
~linear_vector() {
reset();
}
//iteration
T* begin() { return &pool[0]; }
T* end() { return &pool[objectsize]; }
const T* begin() const { return &pool[0]; }
const T* end() const { return &pool[objectsize]; }
};
//pointer_vector
@@ -222,15 +228,15 @@ namespace nall {
else resize(objectsize - count);
}
inline T& operator[](unsigned index) {
if(index >= objectsize) resize(index + 1);
if(!pool[index]) pool[index] = new T;
return *pool[index];
pointer_vector() : pool(0), poolsize(0), objectsize(0) {
}
inline const T& operator[](unsigned index) const {
if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
return *pool[index];
pointer_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
}
~pointer_vector() {
reset();
}
//copy
@@ -261,17 +267,31 @@ namespace nall {
operator=(std::move(source));
}
//construction
pointer_vector() : pool(0), poolsize(0), objectsize(0) {
//index
inline T& operator[](unsigned index) {
if(index >= objectsize) resize(index + 1);
if(!pool[index]) pool[index] = new T;
return *pool[index];
}
pointer_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
inline const T& operator[](unsigned index) const {
if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
return *pool[index];
}
~pointer_vector() {
reset();
}
//iteration
struct iterator {
bool operator!=(const iterator &source) const { return index != source.index; }
T& operator*() { return vector.operator[](index); }
iterator& operator++() { index++; return *this; }
iterator(pointer_vector &vector, unsigned index) : vector(vector), index(index) {}
private:
pointer_vector &vector;
unsigned index;
};
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, objectsize); }
};
template<typename T> struct has_size<linear_vector<T>> { enum { value = true }; };

192
bsnes/nall/windows/detour.hpp Executable file
View File

@@ -0,0 +1,192 @@
#ifndef NALL_WINDOWS_DETOUR_HPP
#define NALL_WINDOWS_DETOUR_HPP
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utf8.hpp>
namespace nall {
#define Copy 0
#define RelNear 1
struct detour {
static bool insert(const string &moduleName, const string &functionName, void *&source, void *target);
static bool remove(const string &moduleName, const string &functionName, void *&source);
protected:
static unsigned length(const uint8_t *function);
static unsigned mirror(uint8_t *target, const uint8_t *source);
struct opcode {
uint16_t prefix;
unsigned length;
unsigned mode;
uint16_t modify;
};
static opcode opcodes[];
};
//TODO:
//* fs:, gs: should force another opcode copy
//* conditional branches within +5-byte range should fail
detour::opcode detour::opcodes[] = {
{ 0x50, 1 }, //push eax
{ 0x51, 1 }, //push ecx
{ 0x52, 1 }, //push edx
{ 0x53, 1 }, //push ebx
{ 0x54, 1 }, //push esp
{ 0x55, 1 }, //push ebp
{ 0x56, 1 }, //push esi
{ 0x57, 1 }, //push edi
{ 0x58, 1 }, //pop eax
{ 0x59, 1 }, //pop ecx
{ 0x5a, 1 }, //pop edx
{ 0x5b, 1 }, //pop ebx
{ 0x5c, 1 }, //pop esp
{ 0x5d, 1 }, //pop ebp
{ 0x5e, 1 }, //pop esi
{ 0x5f, 1 }, //pop edi
{ 0x64, 1 }, //fs:
{ 0x65, 1 }, //gs:
{ 0x68, 5 }, //push dword
{ 0x6a, 2 }, //push byte
{ 0x74, 2, RelNear, 0x0f84 }, //je near -> je far
{ 0x75, 2, RelNear, 0x0f85 }, //jne near -> jne far
{ 0x89, 2 }, //mov reg,reg
{ 0x8b, 2 }, //mov reg,reg
{ 0x90, 1 }, //nop
{ 0xa1, 5 }, //mov eax,[dword]
{ 0xeb, 2, RelNear, 0xe9 }, //jmp near -> jmp far
};
bool detour::insert(const string &moduleName, const string &functionName, void *&source, void *target) {
HMODULE module = GetModuleHandleW(utf16_t(moduleName));
if(!module) return false;
uint8_t *sourceData = (uint8_t*)GetProcAddress(module, functionName);
if(!sourceData) return false;
unsigned sourceLength = detour::length(sourceData);
if(sourceLength < 5) {
//unable to clone enough bytes to insert hook
#if 1
string output = { "detour::insert(", moduleName, "::", functionName, ") failed: " };
for(unsigned n = 0; n < 16; n++) output.append(hex<2>(sourceData[n]), " ");
output.rtrim<1>(" ");
MessageBoxA(0, output, "nall::detour", MB_OK);
#endif
return false;
}
uint8_t *mirrorData = new uint8_t[512]();
detour::mirror(mirrorData, sourceData);
DWORD privileges;
VirtualProtect((void*)mirrorData, 512, PAGE_EXECUTE_READWRITE, &privileges);
VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
uintmax_t address = (uintmax_t)target - ((uintmax_t)sourceData + 5);
sourceData[0] = 0xe9; //jmp target
sourceData[1] = address >> 0;
sourceData[2] = address >> 8;
sourceData[3] = address >> 16;
sourceData[4] = address >> 24;
VirtualProtect((void*)sourceData, 256, privileges, &privileges);
source = (void*)mirrorData;
return true;
}
bool detour::remove(const string &moduleName, const string &functionName, void *&source) {
HMODULE module = GetModuleHandleW(utf16_t(moduleName));
if(!module) return false;
uint8_t *sourceData = (uint8_t*)GetProcAddress(module, functionName);
if(!sourceData) return false;
uint8_t *mirrorData = (uint8_t*)source;
if(mirrorData == sourceData) return false; //hook was never installed
unsigned length = detour::length(256 + mirrorData);
if(length < 5) return false;
DWORD privileges;
VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
for(unsigned n = 0; n < length; n++) sourceData[n] = mirrorData[256 + n];
VirtualProtect((void*)sourceData, 256, privileges, &privileges);
source = (void*)sourceData;
delete[] mirrorData;
return true;
}
unsigned detour::length(const uint8_t *function) {
unsigned length = 0;
while(length < 5) {
detour::opcode *opcode = 0;
foreach(op, detour::opcodes) {
if(function[length] == op.prefix) {
opcode = &op;
break;
}
}
if(opcode == 0) break;
length += opcode->length;
}
return length;
}
unsigned detour::mirror(uint8_t *target, const uint8_t *source) {
const uint8_t *entryPoint = source;
for(unsigned n = 0; n < 256; n++) target[256 + n] = source[n];
unsigned size = detour::length(source);
while(size) {
detour::opcode *opcode = 0;
foreach(op, detour::opcodes) {
if(*source == op.prefix) {
opcode = &op;
break;
}
}
switch(opcode->mode) {
case Copy:
for(unsigned n = 0; n < opcode->length; n++) *target++ = *source++;
break;
case RelNear: {
source++;
uintmax_t sourceAddress = (uintmax_t)source + 1 + (int8_t)*source;
*target++ = opcode->modify;
if(opcode->modify >> 8) *target++ = opcode->modify >> 8;
uintmax_t targetAddress = (uintmax_t)target + 4;
uintmax_t address = sourceAddress - targetAddress;
*target++ = address >> 0;
*target++ = address >> 8;
*target++ = address >> 16;
*target++ = address >> 24;
source += 2;
} break;
}
size -= opcode->length;
}
uintmax_t address = (entryPoint + detour::length(entryPoint)) - (target + 5);
*target++ = 0xe9; //jmp entryPoint
*target++ = address >> 0;
*target++ = address >> 8;
*target++ = address >> 16;
*target++ = address >> 24;
return source - entryPoint;
}
#undef Implied
#undef RelNear
}
#endif

94
bsnes/nall/windows/launcher.hpp Executable file
View File

@@ -0,0 +1,94 @@
#ifndef NALL_WINDOWS_LAUNCHER_HPP
#define NALL_WINDOWS_LAUNCHER_HPP
namespace nall {
//launch a new process and inject specified DLL into it
bool launch(const char *applicationName, const char *libraryName, uint32_t entryPoint) {
//if a launcher does not send at least one message, a wait cursor will appear
PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0);
MSG msg;
GetMessage(&msg, 0, 0, 0);
STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(STARTUPINFOW));
BOOL result = CreateProcessW(
utf16_t(applicationName), GetCommandLineW(), NULL, NULL, TRUE,
DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, //do not break if application creates its own processes
NULL, NULL, &si, &pi
);
if(result == false) return false;
uint8_t entryData[1024], entryHook[1024] = {
0x68, 0x00, 0x00, 0x00, 0x00, //push libraryName
0xb8, 0x00, 0x00, 0x00, 0x00, //mov eax,LoadLibraryW
0xff, 0xd0, //call eax
0xcd, 0x03, //int 3
};
entryHook[1] = (uint8_t)((entryPoint + 14) >> 0);
entryHook[2] = (uint8_t)((entryPoint + 14) >> 8);
entryHook[3] = (uint8_t)((entryPoint + 14) >> 16);
entryHook[4] = (uint8_t)((entryPoint + 14) >> 24);
uint32_t pLoadLibraryW = (uint32_t)GetProcAddress(GetModuleHandleW(L"kernel32"), "LoadLibraryW");
entryHook[6] = pLoadLibraryW >> 0;
entryHook[7] = pLoadLibraryW >> 8;
entryHook[8] = pLoadLibraryW >> 16;
entryHook[9] = pLoadLibraryW >> 24;
utf16_t buffer = utf16_t(libraryName);
memcpy(entryHook + 14, buffer, 2 * wcslen(buffer) + 2);
while(true) {
DEBUG_EVENT event;
WaitForDebugEvent(&event, INFINITE);
if(event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break;
if(event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
if(event.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) {
if(event.u.Exception.ExceptionRecord.ExceptionAddress == (void*)(entryPoint + 14 - 1)) {
HANDLE hProcess = OpenProcess(0, FALSE, event.dwProcessId);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, event.dwThreadId);
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
GetThreadContext(hThread, &context);
WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL);
context.Eip = entryPoint;
SetThreadContext(hThread, &context);
CloseHandle(hThread);
CloseHandle(hProcess);
}
ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE);
continue;
}
ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
continue;
}
if(event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
ReadProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL);
WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryHook, sizeof entryHook, NULL);
ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE);
continue;
}
ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE);
}
return true;
}
}
#endif

124
bsnes/nall/zip.hpp Executable file
View File

@@ -0,0 +1,124 @@
#ifndef NALL_UNZIP_HPP
#define NALL_UNZIP_HPP
#include <nall/filemap.hpp>
#include <nall/inflate.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
struct zip {
struct File {
string name;
const uint8_t *data;
unsigned size;
unsigned csize;
unsigned cmode; //0 = uncompressed, 8 = deflate
unsigned crc32;
};
inline bool open(const string &filename) {
close();
if(fm.open(filename, filemap::mode::read) == false) return false;
if(open(fm.data(), fm.size()) == false) {
fm.close();
return false;
}
return true;
}
inline bool open(const uint8_t *data, unsigned size) {
if(size < 22) return false;
filedata = data;
filesize = size;
file.reset();
const uint8_t *footer = data + size - 22;
const uint8_t *directory = data + read(footer + 16, 4);
while(true) {
unsigned signature = read(directory + 0, 4);
if(signature != 0x02014b50) break;
File file;
file.cmode = read(directory + 10, 2);
file.crc32 = read(directory + 16, 4);
file.csize = read(directory + 20, 4);
file.size = read(directory + 24, 4);
unsigned namelength = read(directory + 28, 2);
unsigned extralength = read(directory + 30, 2);
unsigned commentlength = read(directory + 32, 2);
char *filename = new char[namelength + 1];
memcpy(filename, directory + 46, namelength);
filename[namelength] = 0;
file.name = filename;
delete[] filename;
unsigned offset = read(directory + 42, 4);
unsigned offsetNL = read(data + offset + 26, 2);
unsigned offsetEL = read(data + offset + 28, 2);
file.data = data + offset + 30 + offsetNL + offsetEL;
directory += 46 + namelength + extralength + commentlength;
this->file.append(file);
}
return true;
}
inline bool extract(File &file, uint8_t *&data, unsigned &size) {
data = 0, size = 0;
if(file.cmode == 0) {
size = file.size;
data = new uint8_t[size];
memcpy(data, file.data, size);
return true;
}
if(file.cmode == 8) {
size = file.size;
data = new uint8_t[size];
if(inflate(data, size, file.data, file.csize) == false) {
delete[] data;
size = 0;
return false;
}
return true;
}
return false;
}
inline void close() {
if(fm.open()) fm.close();
}
~zip() {
close();
}
protected:
filemap fm;
const uint8_t *filedata;
unsigned filesize;
unsigned read(const uint8_t *data, unsigned size) {
unsigned result = 0, shift = 0;
while(size--) { result |= *data++ << shift; shift += 8; }
return result;
}
public:
linear_vector<File> file;
};
}
#endif

View File

@@ -34,6 +34,10 @@ void Font::setSize(unsigned size) { state.size = size; return p.setSize(size); }
void Font::setUnderline(bool underline) { state.underline = underline; return p.setUnderline(underline); }
Font::Font() : state(*new State), p(*new pFont(*this)) { p.constructor(); }
void Timer::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Timer::setInterval(unsigned milliseconds) { state.milliseconds = milliseconds; return p.setInterval(milliseconds); }
Timer::Timer() : state(*new State), p(*new pTimer(*this)) { p.constructor(); }
MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::information(parent, text, buttons); }
MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::question(parent, text, buttons); }
MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::warning(parent, text, buttons); }
@@ -43,11 +47,12 @@ Window Window::None;
void Window::append(Layout &layout) { state.layout.append(layout); return p.append(layout); }
void Window::append(Menu &menu) { state.menu.append(menu); ((Action&)menu).state.parent = this; return p.append(menu); }
void Window::append(Widget &widget) { state.widget.append(widget); return p.append(widget); }
Color Window::backgroundColor() { return p.backgroundColor(); }
Geometry Window::frameGeometry() { Geometry geometry = p.geometry(), margin = p.frameMargin(); return { geometry.x - margin.x, geometry.y - margin.y, geometry.width + margin.width, geometry.height + margin.height }; }
Geometry Window::frameMargin() { return p.frameMargin(); }
bool Window::focused() { return p.focused(); }
Geometry Window::geometry() { return p.geometry(); }
void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { state.backgroundColor = true; state.backgroundColorRed = red; state.backgroundColorGreen = green; state.backgroundColorBlue = blue; return p.setBackgroundColor(red, green, blue); }
void Window::setBackgroundColor(const Color &color) { state.backgroundColorOverride = true; state.backgroundColor = color; return p.setBackgroundColor(color); }
void Window::setFrameGeometry(const Geometry &geometry) { Geometry margin = p.frameMargin(); return setGeometry({ geometry.x + margin.x, geometry.y + margin.y, geometry.width - margin.width, geometry.height - margin.height }); }
void Window::setFocused() { return p.setFocused(); }
void Window::setFullScreen(bool fullScreen) { state.fullScreen = fullScreen; return p.setFullScreen(fullScreen); }
@@ -89,6 +94,7 @@ RadioItem::RadioItem() : state(*new State), base_from_member<pRadioItem&>(*new p
bool Widget::enabled() { return state.enabled; }
Font& Widget::font() { return p.font(); }
Geometry Widget::geometry() { return state.geometry; }
Geometry Widget::minimumGeometry() { return p.minimumGeometry(); }
void Widget::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Widget::setFocused() { return p.setFocused(); }
@@ -124,6 +130,11 @@ void HexEdit::setRows(unsigned rows) { state.rows = rows; return p.setRows(rows)
void HexEdit::update() { return p.update(); }
HexEdit::HexEdit() : state(*new State), base_from_member<pHexEdit&>(*new pHexEdit(*this)), Widget(base_from_member<pHexEdit&>::value), p(base_from_member<pHexEdit&>::value) { p.constructor(); }
unsigned HorizontalScrollBar::position() { return p.position(); }
void HorizontalScrollBar::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HorizontalScrollBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
HorizontalScrollBar::HorizontalScrollBar() : state(*new State), base_from_member<pHorizontalScrollBar&>(*new pHorizontalScrollBar(*this)), Widget(base_from_member<pHorizontalScrollBar&>::value), p(base_from_member<pHorizontalScrollBar&>::value) { p.constructor(); }
unsigned HorizontalSlider::position() { return p.position(); }
void HorizontalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HorizontalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
@@ -168,6 +179,11 @@ void TextEdit::setWordWrap(bool wordWrap) { state.wordWrap = wordWrap; return p.
string TextEdit::text() { return p.text(); }
TextEdit::TextEdit() : state(*new State), base_from_member<pTextEdit&>(*new pTextEdit(*this)), Widget(base_from_member<pTextEdit&>::value), p(base_from_member<pTextEdit&>::value) { p.constructor(); }
unsigned VerticalScrollBar::position() { return p.position(); }
void VerticalScrollBar::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void VerticalScrollBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
VerticalScrollBar::VerticalScrollBar() : state(*new State), base_from_member<pVerticalScrollBar&>(*new pVerticalScrollBar(*this)), Widget(base_from_member<pVerticalScrollBar&>::value), p(base_from_member<pVerticalScrollBar&>::value) { p.constructor(); }
unsigned VerticalSlider::position() { return p.position(); }
void VerticalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void VerticalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }

View File

@@ -6,6 +6,7 @@ struct Widget;
struct pOS;
struct pFont;
struct pTimer;
struct pWindow;
struct pAction;
struct pMenu;
@@ -20,6 +21,7 @@ struct pCanvas;
struct pCheckBox;
struct pComboBox;
struct pHexEdit;
struct pHorizontalScrollBar;
struct pHorizontalSlider;
struct pLabel;
struct pLineEdit;
@@ -27,6 +29,7 @@ struct pListView;
struct pProgressBar;
struct pRadioBox;
struct pTextEdit;
struct pVerticalScrollBar;
struct pVerticalSlider;
struct pViewport;
@@ -42,6 +45,12 @@ struct Geometry {
inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
};
struct Color {
uint8_t red, green, blue, alpha;
inline Color() : red(0), green(0), blue(0), alpha(255) {}
inline Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255) : red(red), green(green), blue(blue), alpha(alpha) {}
};
struct Object {
Object();
Object& operator=(const Object&) = delete;
@@ -82,6 +91,18 @@ struct Font : Object {
pFont &p;
};
struct Timer : Object {
nall::function<void ()> onTimeout;
void setEnabled(bool enabled = true);
void setInterval(unsigned milliseconds);
Timer();
struct State;
State &state;
pTimer &p;
};
struct MessageWindow : Object {
enum class Buttons : unsigned {
Ok,
@@ -111,11 +132,12 @@ struct Window : Object {
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
Color backgroundColor();
Geometry frameGeometry();
Geometry frameMargin();
bool focused();
Geometry geometry();
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setBackgroundColor(const Color &color);
void setFrameGeometry(const Geometry &geometry);
void setFocused();
void setFullScreen(bool fullScreen = true);
@@ -210,6 +232,7 @@ struct Layout : Object {
struct Widget : Object {
bool enabled();
Font& font();
Geometry geometry();
Geometry minimumGeometry();
void setEnabled(bool enabled = true);
void setFocused();
@@ -287,6 +310,19 @@ struct HexEdit : private nall::base_from_member<pHexEdit&>, Widget {
pHexEdit &p;
};
struct HorizontalScrollBar : private nall::base_from_member<pHorizontalScrollBar&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
HorizontalScrollBar();
struct State;
State &state;
pHorizontalScrollBar &p;
};
struct HorizontalSlider : private nall::base_from_member<pHorizontalSlider&>, Widget {
nall::function<void ()> onChange;
@@ -393,6 +429,19 @@ struct TextEdit : private nall::base_from_member<pTextEdit&>, Widget {
pTextEdit &p;
};
struct VerticalScrollBar : private nall::base_from_member<pVerticalScrollBar&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
VerticalScrollBar();
struct State;
State &state;
pVerticalScrollBar &p;
};
struct VerticalSlider : private nall::base_from_member<pVerticalSlider&>, Widget {
nall::function<void ()> onChange;

View File

@@ -13,9 +13,19 @@ struct Font::State {
}
};
struct Timer::State {
bool enabled;
unsigned milliseconds;
State() {
enabled = false;
milliseconds = 0;
}
};
struct Window::State {
bool backgroundColor;
unsigned backgroundColorRed, backgroundColorGreen, backgroundColorBlue;
bool backgroundColorOverride;
Color backgroundColor;
bool fullScreen;
Geometry geometry;
reference_array<Layout&> layout;
@@ -32,10 +42,8 @@ struct Window::State {
Font *widgetFont;
State() {
backgroundColor = false;
backgroundColorRed = 0;
backgroundColorGreen = 0;
backgroundColorBlue = 0;
backgroundColorOverride = false;
backgroundColor = { 0, 0, 0, 255 };
fullScreen = false;
geometry = { 128, 128, 256, 256 };
menuFont = 0;
@@ -142,6 +150,16 @@ struct HexEdit::State {
}
};
struct HorizontalScrollBar::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};
struct HorizontalSlider::State {
unsigned length;
unsigned position;
@@ -213,6 +231,16 @@ struct TextEdit::State {
}
};
struct VerticalScrollBar::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};
struct VerticalSlider::State {
unsigned length;
unsigned position;

View File

@@ -29,4 +29,6 @@ void pFont::constructor() {
gtkFont = pango_font_description_new();
PangoContext *context = gdk_pango_context_get_for_screen(gdk_screen_get_default());
gtkLayout = pango_layout_new(context);
font.setFamily("Sans");
font.setSize(8);
}

View File

@@ -2,6 +2,7 @@
#include "settings.cpp"
#include "font.cpp"
#include "timer.cpp"
#include "message-window.cpp"
#include "window.cpp"
@@ -18,6 +19,7 @@
#include "widget/check-box.cpp"
#include "widget/combo-box.cpp"
#include "widget/hex-edit.cpp"
#include "widget/horizontal-scroll-bar.cpp"
#include "widget/horizontal-slider.cpp"
#include "widget/label.cpp"
#include "widget/line-edit.cpp"
@@ -25,16 +27,36 @@
#include "widget/progress-bar.cpp"
#include "widget/radio-box.cpp"
#include "widget/text-edit.cpp"
#include "widget/vertical-scroll-bar.cpp"
#include "widget/vertical-slider.cpp"
#include "widget/viewport.cpp"
Font pOS::defaultFont;
Geometry pOS::availableGeometry() {
//TODO: is there a GTK+ function for this?
//should return desktopGeometry() sans panels, toolbars, docks, etc.
Geometry geometry = desktopGeometry();
return { geometry.x + 64, geometry.y + 64, geometry.width - 128, geometry.height - 128 };
Display *display = XOpenDisplay(0);
int screen = DefaultScreen(display);
static Atom atom = X11None;
if(atom == X11None) atom = XInternAtom(display, "_NET_WORKAREA", True);
int format;
unsigned char *data = 0;
unsigned long items, after;
Atom returnAtom;
int result = XGetWindowProperty(
display, RootWindow(display, screen), atom, 0, 4, False, XA_CARDINAL, &returnAtom, &format, &items, &after, &data
);
XCloseDisplay(display);
if(result == Success && returnAtom == XA_CARDINAL && format == 32 && items == 4) {
unsigned long *workarea = (unsigned long*)data;
return { (signed)workarea[0], (signed)workarea[1], (unsigned)workarea[2], (unsigned)workarea[3] };
}
return desktopGeometry();
}
Geometry pOS::desktopGeometry() {
@@ -149,7 +171,7 @@ void pOS::initialize() {
" GtkComboBox::appears-as-list = 1\n"
" GtkTreeView::vertical-separator = 0\n"
"}\n"
"class \"GtkComboBox\" style \"phoenix-gtk\"\n"
//"class \"GtkComboBox\" style \"phoenix-gtk\"\n"
"class \"GtkTreeView\" style \"phoenix-gtk\"\n"
);
}

View File

@@ -57,6 +57,16 @@ struct pFont : public pObject {
void constructor();
};
struct pTimer : public pObject {
Timer &timer;
void setEnabled(bool enabled);
void setInterval(unsigned milliseconds);
pTimer(Timer &timer) : timer(timer) {}
void constructor();
};
struct pMessageWindow : public pObject {
static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons);
static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons);
@@ -72,14 +82,16 @@ struct pWindow : public pObject {
GtkWidget *statusContainer;
GtkWidget *menu;
GtkWidget *status;
GdkEventConfigure lastConfigure;
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
Color backgroundColor();
bool focused();
Geometry frameMargin();
Geometry geometry();
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setBackgroundColor(const Color &color);
void setFocused();
void setFullScreen(bool fullScreen);
void setGeometry(const Geometry &geometry);
@@ -192,8 +204,7 @@ struct pButton : public pWidget {
struct pCanvas : public pWidget {
Canvas &canvas;
uint32_t *bufferRGB;
uint32_t *bufferBGR;
cairo_surface_t *surface;
uint32_t* buffer();
void setGeometry(const Geometry &geometry);
@@ -201,7 +212,6 @@ struct pCanvas : public pWidget {
pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}
void constructor();
void redraw();
};
struct pCheckBox : public pWidget {
@@ -254,6 +264,18 @@ struct pHexEdit : public pWidget {
void updateScroll();
};
struct pHorizontalScrollBar : public pWidget {
HorizontalScrollBar &horizontalScrollBar;
Geometry minimumGeometry();
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
pHorizontalScrollBar(HorizontalScrollBar &horizontalScrollBar) : pWidget(horizontalScrollBar), horizontalScrollBar(horizontalScrollBar) {}
void constructor();
};
struct pHorizontalSlider : public pWidget {
HorizontalSlider &horizontalSlider;
@@ -358,6 +380,18 @@ struct pTextEdit : public pWidget {
void constructor();
};
struct pVerticalScrollBar : public pWidget {
VerticalScrollBar &verticalScrollBar;
Geometry minimumGeometry();
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
pVerticalScrollBar(VerticalScrollBar &verticalScrollBar) : pWidget(verticalScrollBar), verticalScrollBar(verticalScrollBar) {}
void constructor();
};
struct pVerticalSlider : public pWidget {
VerticalSlider &verticalSlider;

24
bsnes/phoenix/gtk/timer.cpp Executable file
View File

@@ -0,0 +1,24 @@
static guint Timer_trigger(pTimer *self) {
//timer may have been disabled prior to triggering, so check state
if(self->timer.state.enabled) {
if(self->timer.onTimeout) self->timer.onTimeout();
}
//callback may have disabled timer, so check state again
if(self->timer.state.enabled) {
g_timeout_add(self->timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)self);
}
//kill this timer instance (it is spawned above if needed again)
return false;
}
void pTimer::setEnabled(bool enabled) {
if(enabled) {
g_timeout_add(timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)this);
}
}
void pTimer::setInterval(unsigned milliseconds) {
}
void pTimer::constructor() {
}

View File

@@ -5,7 +5,7 @@ static void Button_tick(Button *self) {
Geometry pButton::minimumGeometry() {
Font &font = pWidget::font();
Geometry geometry = font.geometry(button.state.text);
return { 0, 0, geometry.width + 24, geometry.height + 14 };
return { 0, 0, geometry.width + 24, geometry.height + 12 };
}
void pButton::setText(const string &text) {

View File

@@ -1,17 +1,21 @@
static void Canvas_expose(pCanvas *self) {
self->redraw();
static gboolean Canvas_expose(GtkWidget *widget, GdkEvent *event, pCanvas *self) {
cairo_t *context = gdk_cairo_create(gtk_widget_get_window(widget));
cairo_set_source_surface(context, self->surface, 0, 0);
cairo_paint(context);
cairo_destroy(context);
return true;
}
uint32_t* pCanvas::buffer() {
return bufferRGB;
return (uint32_t*)cairo_image_surface_get_data(surface);
}
void pCanvas::setGeometry(const Geometry &geometry) {
delete[] bufferRGB;
delete[] bufferBGR;
if(geometry.width == cairo_image_surface_get_width(surface)
&& geometry.height == cairo_image_surface_get_height(surface)) return;
bufferRGB = new uint32_t[geometry.width * geometry.height]();
bufferBGR = new uint32_t[geometry.width * geometry.height]();
cairo_surface_destroy(surface);
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, geometry.width, geometry.height);
pWidget::setGeometry(geometry);
update();
@@ -19,41 +23,16 @@ void pCanvas::setGeometry(const Geometry &geometry) {
void pCanvas::update() {
if(gtk_widget_get_realized(gtkWidget) == false) return;
GdkRectangle rect;
rect.x = 0;
rect.y = 0;
rect.width = gtkWidget->allocation.width;
rect.height = gtkWidget->allocation.height;
gdk_window_invalidate_rect(gtkWidget->window, &rect, true);
gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), 0, true);
}
void pCanvas::constructor() {
bufferRGB = new uint32_t[256 * 256]();
bufferBGR = new uint32_t[256 * 256]();
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 256, 256);
gtkWidget = gtk_drawing_area_new();
GdkColor color;
color.pixel = color.red = color.green = color.blue = 0;
gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, &color);
gtk_widget_set_double_buffered(gtkWidget, false);
gtk_widget_add_events(gtkWidget, GDK_EXPOSURE_MASK);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
}
void pCanvas::redraw() {
if(gtk_widget_get_realized(gtkWidget) == false) return;
uint32_t *rgb = bufferRGB, *bgr = bufferBGR;
for(unsigned y = gtkWidget->allocation.height; y; y--) {
for(unsigned x = gtkWidget->allocation.width; x; x--) {
uint32_t pixel = *rgb++;
*bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff);
}
}
gdk_draw_rgb_32_image(
gtkWidget->window,
gtkWidget->style->fg_gc[GTK_WIDGET_STATE(gtkWidget)],
0, 0, gtkWidget->allocation.width, gtkWidget->allocation.height,
GDK_RGB_DITHER_NONE, (guchar*)bufferBGR, sizeof(uint32_t) * gtkWidget->allocation.width
);
g_signal_connect(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
}

View File

@@ -13,14 +13,12 @@ Geometry pComboBox::minimumGeometry() {
foreach(item, comboBox.state.text) maximumWidth = max(maximumWidth, font.geometry(item).width);
Geometry geometry = font.geometry(" ");
return { 0, 0, maximumWidth + 44, geometry.height + 10 };
return { 0, 0, maximumWidth + 44, geometry.height + 12 };
}
void pComboBox::reset() {
locked = true;
for(signed n = itemCounter - 1; n >= 0; n--) {
gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), n);
}
gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(gtkWidget))));
itemCounter = 0;
locked = false;
}

View File

@@ -112,17 +112,17 @@ bool pHexEdit::keyPress(unsigned scancode) {
unsigned cursorY = position / lineWidth;
unsigned cursorX = position % lineWidth;
if(scancode == GDK_Home) {
if(scancode == GDK_KEY_Home) {
setCursorPosition(cursorY * lineWidth + 10);
return true;
}
if(scancode == GDK_End) {
if(scancode == GDK_KEY_End) {
setCursorPosition(cursorY * lineWidth + 10 + (hexEdit.state.columns * 3 - 1));
return true;
}
if(scancode == GDK_Up) {
if(scancode == GDK_KEY_Up) {
if(cursorY != 0) return false;
signed newOffset = hexEdit.state.offset - hexEdit.state.columns;
@@ -133,7 +133,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
return true;
}
if(scancode == GDK_Down) {
if(scancode == GDK_KEY_Down) {
if(cursorY != hexEdit.state.rows - 1) return false;
signed newOffset = hexEdit.state.offset + hexEdit.state.columns;
@@ -144,7 +144,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
return true;
}
if(scancode == GDK_Page_Up) {
if(scancode == GDK_KEY_Page_Up) {
signed newOffset = hexEdit.state.offset - hexEdit.state.columns * hexEdit.state.rows;
if(newOffset >= 0) {
hexEdit.setOffset(newOffset);
@@ -155,7 +155,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
return true;
}
if(scancode == GDK_Page_Down) {
if(scancode == GDK_KEY_Page_Down) {
signed newOffset = hexEdit.state.offset + hexEdit.state.columns * hexEdit.state.rows;
for(unsigned n = 0; n < hexEdit.state.rows; n++) {
if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) {

View File

@@ -0,0 +1,29 @@
static void HorizontalScrollBar_change(HorizontalScrollBar *self) {
if(self->state.position == self->position()) return;
self->state.position = self->position();
if(self->onChange) self->onChange();
}
Geometry pHorizontalScrollBar::minimumGeometry() {
return { 0, 0, 0, 20 };
}
unsigned pHorizontalScrollBar::position() {
return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget));
}
void pHorizontalScrollBar::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
}
void pHorizontalScrollBar::setPosition(unsigned position) {
gtk_range_set_value(GTK_RANGE(gtkWidget), position);
}
void pHorizontalScrollBar::constructor() {
gtkWidget = gtk_hscrollbar_new(0);
setLength(101);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalScrollBar_change), (gpointer)&horizontalScrollBar);
}

View File

@@ -15,6 +15,7 @@ unsigned pHorizontalSlider::position() {
void pHorizontalSlider::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
}
void pHorizontalSlider::setPosition(unsigned position) {
@@ -24,5 +25,6 @@ void pHorizontalSlider::setPosition(unsigned position) {
void pHorizontalSlider::constructor() {
gtkWidget = gtk_hscale_new_with_range(0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
setLength(101);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)&horizontalSlider);
}

View File

@@ -13,7 +13,7 @@ Geometry pLineEdit::minimumGeometry() {
}
void pLineEdit::setEditable(bool editable) {
gtk_entry_set_editable(GTK_ENTRY(gtkWidget), editable);
gtk_editable_set_editable(GTK_EDITABLE(gtkWidget), editable);
}
void pLineEdit::setText(const string &text) {

View File

@@ -0,0 +1,29 @@
static void VerticalScrollBar_change(VerticalScrollBar *self) {
if(self->state.position == self->position()) return;
self->state.position = self->position();
if(self->onChange) self->onChange();
}
Geometry pVerticalScrollBar::minimumGeometry() {
return { 0, 0, 20, 0 };
}
unsigned pVerticalScrollBar::position() {
return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget));
}
void pVerticalScrollBar::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
}
void pVerticalScrollBar::setPosition(unsigned position) {
gtk_range_set_value(GTK_RANGE(gtkWidget), position);
}
void pVerticalScrollBar::constructor() {
gtkWidget = gtk_vscrollbar_new(0);
setLength(101);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalScrollBar_change), (gpointer)&verticalScrollBar);
}

View File

@@ -15,6 +15,7 @@ unsigned pVerticalSlider::position() {
void pVerticalSlider::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
}
void pVerticalSlider::setPosition(unsigned position) {
@@ -24,5 +25,6 @@ void pVerticalSlider::setPosition(unsigned position) {
void pVerticalSlider::constructor() {
gtkWidget = gtk_vscale_new_with_range(0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
setLength(101);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)&verticalSlider);
}

View File

@@ -1,5 +1,5 @@
uintptr_t pViewport::handle() {
return GDK_WINDOW_XID(gtkWidget->window);
return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget));
}
void pViewport::constructor() {

View File

@@ -1,70 +1,79 @@
static void Action_setFont(GtkWidget *widget, gpointer font);
static void Widget_setFont(GtkWidget *widget, gpointer font);
static gint Window_close(Window *window) {
static gint Window_close(GtkWidget *widget, GdkEvent *event, Window *window) {
if(window->onClose) window->onClose();
window->setVisible(false);
return true;
}
static gboolean Window_configure(Window *window) {
static gboolean Window_expose(GtkWidget *widget, GdkEvent *event, Window *window) {
cairo_t *context = gdk_cairo_create(widget->window);
Color color = window->backgroundColor();
double red = (double)color.red / 255.0;
double green = (double)color.green / 255.0;
double blue = (double)color.blue / 255.0;
double alpha = (double)color.alpha / 255.0;
if(gdk_screen_is_composited(gdk_screen_get_default())) {
cairo_set_source_rgba(context, red, green, blue, alpha);
} else {
cairo_set_source_rgb(context, red, green, blue);
}
cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
cairo_paint(context);
cairo_destroy(context);
return false;
}
static gboolean Window_configure(GtkWidget *widget, GdkEvent *event, Window *window) {
if(gtk_widget_get_realized(window->p.widget) == false) return false;
GdkWindow *gdkWindow = gtk_widget_get_window(widget);
//update geometry settings
Display *display = XOpenDisplay(0);
XWindowAttributes attributes, parentAttributes;
XGetWindowAttributes(display, GDK_WINDOW_XID(window->p.widget->window), &attributes);
X11Window rootWindow, parentWindow, *childWindow = 0;
unsigned int childCount;
XQueryTree(display, GDK_WINDOW_XID(window->p.widget->window), &rootWindow, &parentWindow, &childWindow, &childCount);
XGetWindowAttributes(display, parentWindow, &parentAttributes);
if(childWindow) XFree(childWindow);
XCloseDisplay(display);
GdkRectangle border, client;
gdk_window_get_frame_extents(gdkWindow, &border);
gdk_window_get_geometry(gdkWindow, 0, 0, &client.width, &client.height, 0);
gdk_window_get_origin(gdkWindow, &client.x, &client.y);
settings.frameGeometryX = attributes.x;
settings.frameGeometryY = attributes.y;
settings.frameGeometryWidth = parentAttributes.width - attributes.width;
settings.frameGeometryHeight = parentAttributes.height - attributes.height;
GtkAllocation menuAllocation, statusAllocation;
gtk_widget_get_allocation(window->p.menu, &menuAllocation);
gtk_widget_get_allocation(window->p.status, &statusAllocation);
if(menuAllocation.height > 1) settings.menuGeometryHeight = menuAllocation.height;
if(statusAllocation.height > 1) settings.statusGeometryHeight = statusAllocation.height;
//calculate current window position
signed eventX = parentAttributes.x + attributes.x;
signed eventY = parentAttributes.y + attributes.y + window->p.menuHeight();
unsigned eventWidth = attributes.width;
unsigned eventHeight = attributes.height - window->p.menuHeight() - window->p.statusHeight();
settings.frameGeometryX = client.x - border.x;
settings.frameGeometryY = client.y - border.y;
settings.frameGeometryWidth = border.width - client.width;
settings.frameGeometryHeight = border.height - client.height;
//move
if(window->p.locked == false && window->state.fullScreen == false) {
if(window->state.geometry.x != eventX || window->state.geometry.y != eventY) {
window->state.geometry.x = eventX;
window->state.geometry.y = eventY;
if(event->configure.x != window->p.lastConfigure.x
|| event->configure.y != window->p.lastConfigure.y
) {
if(window->state.fullScreen == false) {
window->state.geometry.x = client.x;
window->state.geometry.y = client.y + window->p.menuHeight();
}
if(window->p.locked == false && window->onMove) window->onMove();
}
if(window->onMove) window->onMove();
//size
if(window->p.locked == false && window->state.fullScreen == false) {
if(window->state.geometry.width != eventWidth || window->state.geometry.height != eventHeight) {
window->state.geometry.width = eventWidth;
window->state.geometry.height = eventHeight;
if(event->configure.width != window->p.lastConfigure.width
|| event->configure.height != window->p.lastConfigure.height
) {
if(window->state.fullScreen == false) {
window->state.geometry.width = client.width;
window->state.geometry.height = client.height - window->p.menuHeight() - window->p.statusHeight();
}
foreach(layout, window->state.layout) {
Geometry geometry = window->geometry();
geometry.x = geometry.y = 0;
layout.setGeometry(geometry);
}
if(window->p.locked == false && window->onSize) window->onSize();
}
foreach(layout, window->state.layout) {
Geometry geometry = window->geometry();
geometry.x = geometry.y = 0;
layout.setGeometry(geometry);
}
if(window->onSize) window->onSize();
window->p.lastConfigure = event->configure;
return false;
}
@@ -77,7 +86,7 @@ void pWindow::append(Layout &layout) {
void pWindow::append(Menu &subMenu) {
if(window.state.menuFont) subMenu.p.setFont(*window.state.menuFont);
gtk_menu_bar_append(menu, subMenu.p.widget);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), subMenu.p.widget);
gtk_widget_show(subMenu.p.widget);
}
@@ -90,6 +99,12 @@ void pWindow::append(Widget &widget) {
widget.setVisible();
}
Color pWindow::backgroundColor() {
if(window.state.backgroundColorOverride) return window.state.backgroundColor;
GdkColor color = widget->style->bg[GTK_STATE_NORMAL];
return { (uint8_t)(color.red >> 8), (uint8_t)(color.green >> 8), (uint8_t)(color.blue >> 8), 255 };
}
Geometry pWindow::frameMargin() {
if(window.state.fullScreen) return { 0, menuHeight(), 0, menuHeight() + statusHeight() };
return {
@@ -111,13 +126,13 @@ Geometry pWindow::geometry() {
return window.state.geometry;
}
void pWindow::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) {
GdkColor color;
color.pixel = (red << 16) | (green << 8) | (blue << 0);
color.red = (red << 8) | (red << 0);
color.green = (green << 8) | (green << 0);
color.blue = (blue << 8) | (blue << 0);
gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &color);
void pWindow::setBackgroundColor(const Color &color) {
GdkColor gdkColor;
gdkColor.pixel = (color.red << 16) | (color.green << 8) | (color.blue << 0);
gdkColor.red = (color.red << 8) | (color.red << 0);
gdkColor.green = (color.green << 8) | (color.green << 0);
gdkColor.blue = (color.blue << 8) | (color.blue << 0);
gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gdkColor);
}
void pWindow::setFocused() {
@@ -129,14 +144,12 @@ void pWindow::setFullScreen(bool fullScreen) {
gtk_window_unfullscreen(GTK_WINDOW(widget));
gtk_window_set_resizable(GTK_WINDOW(widget), window.state.resizable);
gtk_window_set_decorated(GTK_WINDOW(widget), true);
locked = true;
for(unsigned n = 0; n < 4; n++) {
setGeometry(window.state.geometry);
gtk_widget_set_size_request(widget, -1, -1);
OS::processEvents();
usleep(2000);
}
locked = false;
} else {
gtk_window_fullscreen(GTK_WINDOW(widget));
gtk_window_set_decorated(GTK_WINDOW(widget), false);
@@ -198,7 +211,15 @@ void pWindow::setWidgetFont(Font &font) {
}
void pWindow::constructor() {
memset(&lastConfigure, 0, sizeof(GdkEventConfigure));
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
if(gdk_screen_is_composited(gdk_screen_get_default())) {
gtk_widget_set_colormap(widget, gdk_screen_get_rgba_colormap(gdk_screen_get_default()));
} else {
gtk_widget_set_colormap(widget, gdk_screen_get_rgb_colormap(gdk_screen_get_default()));
}
gtk_window_set_resizable(GTK_WINDOW(widget), true);
gtk_widget_set_app_paintable(widget, true);
gtk_widget_add_events(widget, GDK_CONFIGURE);
@@ -224,8 +245,9 @@ void pWindow::constructor() {
setTitle("");
setGeometry(window.state.geometry);
g_signal_connect_swapped(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
g_signal_connect_swapped(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
}
unsigned pWindow::menuHeight() {

View File

@@ -1,3 +1,6 @@
#ifndef PHOENIX_CPP
#define PHOENIX_CPP
#if defined(PHOENIX_WINDOWS)
#define UNICODE
#define WINVER 0x0501
@@ -14,13 +17,16 @@
#include <QApplication>
#include <QtGui>
#elif defined(PHOENIX_GTK)
#define None X11None
#define None
#define Window X11Window
#define X11None 0L
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <cairo.h>
#include <gdk/gdkkeysyms.h>
#include <X11/Xatom.h>
#undef None
#undef Window
@@ -35,3 +41,5 @@ using namespace nall;
namespace phoenix {
#include "core/core.cpp"
}
#endif

View File

@@ -1,3 +1,6 @@
#ifndef PHOENIX_HPP
#define PHOENIX_HPP
#include <nall/array.hpp>
#include <nall/config.hpp>
#include <nall/foreach.hpp>
@@ -11,3 +14,5 @@
namespace phoenix {
#include "core/core.hpp"
}
#endif

Some files were not shown because too many files have changed in this diff Show More