Compare commits

..

51 Commits
v074 ... v079

Author SHA1 Message Date
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
Tim Allen
0a3d6e4c53 Update to v078 release.
byuu says:

Finally, a new release. I have been very busy finishing up SNES box,
cartridge and PCB scanning plus cataloguing the data, however this
release still has some significant improvements.

Most notably would be randomization on startup. This will help match the
behavior of real hardware and uninitialized memory + registers. It
should help catch homebrew software that forgets to initialize things
properly. Of course, I was not able to test the complete library, so it
is possible that if I've randomized anything that should be constant,
that this could cause a regression. You can disable this randomization
for netplay or to work around any incompatibilities by editing bsnes.cfg
and setting snes.random to false.

The GUI also received some updates. Widget sizes are now computed based
on font sizes, giving it a perfectly native look (because it is native.)
I've also added a hotkey remapping screen to the input settings. Not
only can you remap inputs to controllers now, but those who did not know
the hotkey bindings can now quickly see which ones exist and what they
are mapped to.

Changelog (since v077):

- memory and most registers are now randomly initialized on power-up
- fixed auto joypad polling issue in Super Star Wars
- fixed .nec and .rtc file extensions (they were missing the dot) [krom]
- PPU/accuracy now clears overscan region on any frame when it is
  disabled
- PPU/compatibility no longer auto-blends hires pixels (use NTSC filter
  for this)
- added hotkey remapping dialog to input settings window
- added a few new hotkeys, including quick-reset
- phoenix API now auto-sizes widgets based on font sizes
- file dialog once again remembers previously selected file when
  possible
2011-04-30 23:12:15 +10:00
Tim Allen
378b78dad7 Update to v077r05 release.
byuu says:

Changelog:
- fixed .nec and both .rtc file extensions (thanks krom)
- randomized most S-PPU registers, should trip up some broken homebrew
  that does not initialize all registers
- randomization is now seeded with time(0) rather than 'byuu'
- SNES::interface.video_refresh() now always receives
  {256,512}x{240,480}
- PPU/accuracy scanline 0 does not render the screen back color anymore,
  fixes strange coloring look on first scanline in PAL TV mode in
  non-overscan games
- disabled hires blending in PPU/compatibility; all three cores act the
  same now
2011-04-27 18:57:31 +10:00
Tim Allen
721e0b1762 Update to v077r04 release.
byuu says:

Changelog:
- setGeometry is called after append(layout) now. This fixes the window
  sizing on Qt.
- removed enum Style {} code, as it's no longer necessary.
- removed Filter, Shader path selection code from the file load dialog,
  since that is menu-driven now.
- improved the file load dialog to remember last selected file when mode
  doesn't change (had to split a switch statement into two switches.)
- added Hotkeys port onto input settings window, allowing one to
  dynamically see and remap GUI shortcuts
- added power cycle / reset shortcuts

Still very minimal with the hotkeys, so I packed them all into one group
for now. A few more I'd like to add, but I don't want to get ridiculous
like with the Qt GUI.
2011-03-26 22:31:07 +11:00
Tim Allen
2bf3dbf375 Update to v077r03 release.
byuu says:

Fixed up the geometry calculation code. There is now minimumGeometry()
[returns minimum size needed to display a layout, treats MaximumSize as
MinimumSize], and minimumLayoutGeometry() [like minimumGeometry(), but
it will return MaximumSize if a single container item has that
attribute. Used mostly internally for layout sizing.]

It looks great on Windows, but it looks visually off on Qt. Not exactly
sure what's up there. When I make a test application, everything looks
great. Going to have to clone a bsnes window that's having a problem (eg
bsnes main debugger checkbox window), and see what's up.
2011-03-23 19:04:37 +11:00
Tim Allen
396003e7f6 Update to v077r02 release.
byuu says:

Wouldn't recommend using this, but it has bsnes ported to the new auto-size calculating phoenix API.

Known issues:
- minimumWidth/Height on layouts isn't working right, windows that use
  it are usually too small
- Windows gives 0,0 size for empty text string sizes, which messes up
  a lot of default sizes for LineEdit controls
2011-03-22 23:56:49 +11:00
Tim Allen
a92a554d7b Update to v077r01 release.
byuu says:

Changelog:
- fixed auto joypad polling bug (fixes Super Star Wars start button
  input)
- added pseudo-random class; so far it is RAM only [WRAM, APURAM, VRAM,
  OAM, CGRAM]
- added new system configuration options to bsnes.cfg

The pseudo-random class is optional. For right now, I am defaulting it
to enabled. bsnes.cfg::snes.random = true.
You can of course turn it off if you want. This will break Death Brade
and Unnamed Euro Racer, no questions about it.
So I don't know if I want to leave this on or off by default. Leaving it
on will thwart emulator detection code and help to keep code that relies
on uninitialized memory from working, but leaving it off will increase
compatibility.
2011-03-21 00:57:55 +11:00
Tim Allen
9ea35ce569 Update to v077 release.
byuu says:

Changelog (since v076):
- video filters and shaders now populate inside main menu; no longer
  have to select them as files
- fixed 2xSaI, Super 2xSaI and Super Eagle on 32-bit platforms; still
  buggy on 64-bit Windows
- fixed linear mirroring issues (fixes Mega Man X dash bug)
- fixed RAM memory mapping bug in Sufami Turbo games
- home folder is now %APPDATA%/bsnes or ~/.config/bsnes
- added paths.cfg file, which will allow you to specify custom paths for
  any file types
- save states and cheat files for multi-slot games are based on slot
  names instead of BIOS names
- fixed compilation warning on OS X with nall::decimal
- fixed calculation bug in nall::fp
- Makefile now has options variable, example: make options=debugger
- configuration files and cheat database can now reside in the same
  folder as the binary itself
- updated to 2011-03-11 release of mightymo's cheat database
2011-03-17 23:49:46 +11:00
Tim Allen
348bace8ed Update to v076r07 release.
byuu says:

Rather than make the libsnes API incompatible with previous versions,
I just implemented path as return { basename, hint };
So you will still use the set basename function already there as before.
So either DSP/MSU1/Serial all go in the ROM folder, or they all go
somewhere else. You can be fancy and detect the gametype and override
the basename as you like, if you really want.

Bumped the API to 1.3, and added const char* snes_library_id(void); it
will return "bsnes v076.07" at the moment. The internal string is
static, but don't try caching it or modifying it anyway. You'll have to
split the name and version yourself if you want them separately. API is
backward-compatible to 1.0 still.

Also improved string::assign and string::append to take a variadic
number of arguments. To make this happen, I had to make to_string return
const char* so that infinite recursion did not happen.
2011-03-17 21:39:55 +11:00
Tim Allen
5cbf5b617b Update to v076r06 release.
byuu says:

Changelog:
- path code finished for ui/, just need to expose for ui-libsnes/ now
- filters and shaders use radio items now, so you know which is active
- smooth video was placed inside video mode (list is getting too long)
- .bsnes -> .config/bsnes in make install
- pixelshaders -> snesshader, added Makefile with make install target
- snesfilter, added Makefile with make install and make clean targets
- maybe other stuff
2011-03-17 21:20:51 +11:00
Tim Allen
d5cd21eb0c Update to v076r05 release.
byuu says:

Changelog:
- QMenu::setVisible() does nothing, have to use
  QMenu::menuAction::setVisible()
- improved path system, especially for ST games (states will be
  /path/to/slotA+notdir(slotB).ext, saves are per-game and per-path)
- paths are now valid when you load just the BS-X/ST/SGB BIOSes with no
  carts inserted; maybe useful for BS-X I guess
- removed video filter and pixel shader code from video settings dialog
- added Settings->Video Filter and Settings->Video Shader menu lists
- fixed the SaI family of filters in lores-mode only; although I don't
  really know how or why the change fixed them, the code is too vague

The menu list for the video filters and shaders are populated from
either base/filters and base/shaders, or user/.config/bsnes/filters and
user/.config/bsnes/shaders. It tries the first, and if it does not find
anything it tries the second, just like the configuration files.

That meant doing away with multiple folders for the shaders, so now the
shaders have a suffix to indicate what driver uses them, eg
"Curvature.OpenGL.shader" and "Sepia.Direct3D.shader" -- probably nicer
to use GLSL/HLSL, but using the driver name lets me sub in the currently
loaded video driver with no special casing. So the filter if you have eg
OpenGL loaded is "*.OpenGL.shader"; and for SDL you get "*.SDL.shader",
which will obviously not be there as the non-GL-based SDL driver doesn't
support shaders.

If there are no filters or no shaders available, the menu options do not
show up. The lists are not radio items with active item ticked states
just yet, but they will be.
2011-03-14 22:04:21 +11:00
Tim Allen
8b7dd89059 Update to v076r04 release.
byuu says:

Changelog:
- fixed Sufami Turbo ROM and RAM mapping bugs
- more paths work, definitely need to clean up parameter names and enum
  typenames now

I had to cheat just a bit on the ST SRAM. For now, I am specifying the
RAM size in the XML file.

The base XML isn't supposed to know about the slots though. What I need
to do is write an ST header parser to get actual RAM sizes, and then
from there generate an ST-specific XML file (like I do for the Game Boy)
to specify this info. Would also be nice for some BS-X info.

Added NECDSP, MSU1 and Serial paths. This eliminates
SNES::Cartridge::basename.
2011-03-08 22:23:47 +11:00
Tim Allen
6c4e3ec790 Update to v076r03 release.
byuu says:

Changelog:
- paths.cfg work completed
- save states/archives and cheat files for multi-slot games are more
  intelligent now

For paths.cfg, there are three types of entries. Each have different
special prefixes.

Folder paths: sfc, bs, st, gb, filter, shader
By default, bsnes will remember the last path you loaded a file of said
type from. It will be prefixed with "recent/" in the file. Specify an
explicit hard-coded path to override this.

BIOS paths: satellaviewBios, sufamiTurboBios, superGameBoyBios
Remembers an explicit hard-coded path to the BIOS you selected last.
I was thinking that a nice feature would be for the "Load Special"
windows to pop open the slot A load dialog if a BIOS was selected.
Select a game from this popup and it loads directly, cancel it to get
the regular window to override the BIOS.

Save paths: srm, rtc, bsa, bst, cht, log
Paths to write various files that the emulator generates. Note: srm
groups bsp, bss and sav for now. Was being lazy.
There are four special prefixes for these:

    "base/" -- gets replaced with the executable path
    "user/" -- gets replaced with the same folder where bsnes.cfg goes
	       (%APPDATA%/bsnes or ~/.config/bsnes) -- good for hiding
	       files
    "./"    -- gets replaced with the current ROM path
    "../"   -- gets replaced with the folder above the current ROM path

If you want to go up two folders or more, then use a hard-coded path. If
that's not good enough, kill yourself because God hates you.
2011-03-04 19:57:00 +11:00
Tim Allen
8d64f9b155 Update to v076r02 release.
byuu says:

Changelog:
- fixed a crashing bug when you toggle a cheat code and then load a save state
- added paths.cfg
- bsnes now remembers the most recent path per file-type, like bsnes/Qt
  used to do
- re-added -s to Linux linker flags
- fixed crash with libsnes/SGB XML parsing
2011-03-02 00:31:28 +11:00
Tim Allen
bc5fd8c53c Update to v076r01 release.
byuu says:

Changelog:
- fixed linear mirroring issue in memory map (fixes dash in Mega Man X)
- home path for Windows is now %APPDATA%/bsnes (not %APPDATA%/.bsnes)
- home path for OS X and Linux is now ~/.config/bsnes (not ~/.bsnes)
- bsnes-geometry.cfg is now geometry.cfg; and it stores width,height
- I do not yet restore width, height; because the GTK+ and Qt APIs treat window
  resize as implying setMinimumSize
- added bsnes/ui/path; which I have some significant plans for
- fixed a bug in realpath (specified path is not always a folder, so we should
  not always append / as with userpath)
- bsnes.cfg, geometry.cfg and cheats.xml may now optionally exist in the same
  folder as the binary itself
- ruby only imports the nall namespace after including system headers (should
  fix OS X 'decimal' issue)
- nall::fp now uses atof (fixes nall::fp("0.05"))
- I split the CheatDatabase to a separate cheat-database.cpp file; it was
  pretty ugly packed into cheat-editor.cpp
- Makefile now has "options := " line; where you can add "debugger" if you
  like, and in the future maybe more options
  - also works via command-line: make options=debugger
2011-03-01 10:45:31 +11:00
Tim Allen
64072325c4 Update to v076 release.
byuu says:

Most notable in this release is that sound support has been added to my
own Super Game Boy emulation. The GUI toolkit, phoenix, has also
received a complete rewrite; with the most visible change there being
that windows are now resizable.

Changelog (since v075):
* added sound emulation to Game Boy core
* fixed Super Game Boy save state
* support added HexEdit widget to Windows and Qt targets; debugger can
  now be compiled on all platforms
* entering fullscreen now auto-hides mouse; and mouse capture is toggled
  otherwise by F12 key
* fullscreen command and geometry caching works much better on GTK+ and
  Qt targets
* phoenix rewritten from scratch; now supports resizable layout
  containers
* phoenix/Windows no longer relies on buggy SetParent API to reparent
  widgets
2011-02-27 20:11:01 +11:00
Tim Allen
017f9926fc Update to v075r16 release.
byuu says:

This has my latest API enhancements, but there are some known issues:
- resize on Windows seems to not repaint the buttons properly in rare
  cases. I may just need to revert to flickering resize.
- GTK+ reports the wrong menu height, off by two pixels, prior to the
  window being realized (made visible)
  - this results in the main window moving up two pixels after each run
    of bsnes

The menu height bug was actually there previously, it was just that Qt
and GTK+ were computing the frame margins incorrectly (ignoring the menu
bar) before.

On the bright side, ui/settings/input.cpp has been improved by way of
the new multi-layout support. The window is no longer forced to an
awkward 640 pixels wide, as the mouse axes/buttons can overlap now. The
code is also simpler since I am using the Layout::setVisible command to
toggle groups on and off instead of doing it for each and every control.
2011-02-27 20:05:10 +11:00
Tim Allen
c31543ea58 Update to v075r15 release.
byuu says:

phoenix/GTK+ rewrite completed. All three targets should now be 100%
operational with full resize support.
2011-02-24 20:27:21 +11:00
Tim Allen
7c3aaf12b0 Update to v075r14 release.
byuu says:

Adds the new phoenix/Windows. Testing would once again be appreciated,
as this is basically a rewrite of the entire core of the GUI.
2011-02-24 20:25:20 +11:00
Tim Allen
3fad0a0105 Update to v075r13 release.
byuu says (in the thread about rewriting Phoenix):

- added phoenix/reference; a dummy implementation that contains the 22KB
  of static boilerplate code needed to start a new target
- OS::setDefaultFont removed; problem is that objects in the global
  scope are constructed before you can call that function
- added Window::setWidgetFont, which is applied if your widgets have no
  custom font set already upon attaching them to the window with
  Window::setLayout, more understandable behavior
- renamed ListBox to ListView

byuu says (in the v075 WIP thread):

Found the source of lag on cartridge load. ListView::modify() was not
locking Qt messages, so it was causing a CheatEditor::refresh (copies
16MB of memory each call) for all 128 cheat items.

Final issue now is that nested submenus (menus inside of menus) are not
applying Window::setMenuFont yet.
2011-02-16 23:35:40 +11:00
Tim Allen
72a2967eeb Update to v075r12 release.
byuu says:

phoenix has been completely rewritten from scratch, and bsnes/ui + bsnes/ui-gameboy have been updated to use the new API. Debugger works too. Currently, only phoenix/Qt is completed, and there are two known issues:

1: font sizes of menu items are wrong, I can fix this easily enough
2: there's some sort of multi-second lag when loading games, not sure
   what's happening there yet

The new phoenix isn't exactly complete yet, still making some key
changes, and then I'll start on phoenix/Windows and phoenix/GTK+.

The most noticeable difference is that you don't have to give all of the
header paths and PHOENIX_PLATFORM defines when compiling individual GUI
object files. It's only needed for phoenix.cpp itself. The overall
structure of the phoenix source folder is much saner as well for
sync.sh.

I'm really surprised things are working as well as they are for
a two-day power rewrite of an entire phoenix target. The other targets
won't be as bad insofar as the core stuff is completed this time. And
thank god for that, I was about ready to kill myself after writing
dozens of lines like this:

    HorizontalSlider::HorizontalSlider() : state(*new State),
    base_from_member<pHorizontalSlider&>(*new pHorizontalSlider(*this)),
    Widget(base_from_member<pHorizontalSlider&>::value),
    p(base_from_member<pHorizontalSlider&>::value) {}

But each platform does have some new, unique problems. phoenix/GTK+ was
acting screwy prior to the rewrite, and will most likely still have
issues. Even more important, one of the major points of this rewrite was
having the new phoenix/core cache widget settings/data, so that I can
destroy and recreate widgets rather than relying on SetParent. This
means that simple copying of the old phoenix/Windows won't work, and
this new method is significantly more involved.
2011-02-15 23:22:37 +11:00
Tim Allen
a8ee35633c Update to v075r11 release.
byuu says:

Rewrote the way menus are attached, they act like layouts/widgets now.

All three phoenix targets once again work with both radio menu items and
radio widgets. Both GTK+ and Qt have built-in group controls right
inside the widgets, so I don't have to keep my own groups around
anymore. They do act screwy at widget creation though, have to jump
through some hoops to get it to work right. All I can say is, definitely
set all child widgets to the parent before trying to check any of them.

My long-term goal for the main window is to honor the fullscreen video
setting as a generic setting, and let the window scale auto-fit the best
possible size that matches your scale preference into the output window,
centered just like fullscreen. For now, I've just set it to a fixed
window size until I finish working on phoenix. The scale X settings will
just be to snap the window to an exact size in case you don't want any
black borders, they won't be radio items and the bsnes-geometry.cfg file
will save width/height information as well.

Simplified the sizing requirements for creating layouts and updated all
bsnes windows to support the new system. Layouts also expose their
minimum width/height values, which I use to create perfectly sized
windows on all three platforms. This will fix cut-off heights on the
last Windows WIP. Qt is being annoying though and forcing a minimum
window size of 300,100 despite me telling it to use a smaller window
size. Always have to fight with Qt, I swear to god.
2011-02-10 21:08:12 +11:00
Tim Allen
7dda70baa4 Update to v075r10 release.
byuu says:

phoenix/Windows and phoenix/Qt are mostly fully operational now. All
platforms support dynamic layout resizing. I tried WM_GETMINMAXINFO
(thanks, OV2), but it was acting kind of choppy on resize, and it would
get confused and go crazy if you snapped one direction to the minimum
height but not another, so for now I'm leaving it off.

phoenix/GTK+ will be missing some functionality in regards to window
geometry. The other two have a more coherent strategy now: geometry() is
the client area, and setGeometry moves the client area to where you ask
for. This makes truly centering your client area trivial.
frameGeometry() includes the borders, menu and status. There is no
setFrameGeometry(), not sure if I really even want that, but it could be
useful so who knows. All targets also support non-resizable windows.

X11 is of course horrendously poor with frame sizes, Qt and GTK+ don't
even pretend to simulate them, so they say the frame is 0x0 pixels in
size until your widget is fully realized and visible to the end user. So
for now, to get window positioning right, I have to wait until the
window appears and then reposition the window again, causing a slight
jump. My plan is to build some persistent caching support directly into
phoenix. From here, I can just have the window snap the very first time
you run your very first phoenix app. I'll then determine the frame size
information, and use that to create future windows. Once they spawn,
I'll recheck and update the frame size info in case it has changed (eg
user changed themes.) Saving settings into .config/phoenix will allow me
to avoid having to snap the window every time on first startup. If the
config file is missing or unwritable, too bad, happens every time then.

I'm thinking about renaming onResize to onSize, and getting rid of
Window::create(). Rather make it spawn like every other control in its
constructor.
2011-02-07 20:20:07 +11:00
Tim Allen
2c61ce2522 Update to v075r09 release.
byuu says:

Ported phoenix/Windows and phoenix/GTK+ over to the new system. There
are some problems that need to be addressed:

- Windows ComboBox height setting needs widget creation height to
  properly scale itself (make Widget::setGeometry virtual and override
  ComboBox::setGeometry)
- Windows Canvas is completely broken
- GTK+ Canvas is slow as shit compared to Qt Canvas, probably nothing
  I can do about it, have to do a very costly conversion because GTK+ is
  stupid and uses BGR like Nintendo
- GTK+ listboxes are fucking insanely complicated to set up. Currently
  I just split the second-half of creation to the setHeaderText call,
  but when you don't call that, things explode
  - I'm probably going to have to completely destroy and recreate
    listboxes when changing the header text / column count
- Qt resize code is still impossible to get right, it's not letting me
  size a window > 2/3rds of the screen size (it's in their docs)
  - I swear, Qt is the most painful API in the world to move/size
    windows with
- now that Window is separate, it really needs geometry() and
  frameGeometry() as the two are quite different
- I need a way to toggle window resizability for fixed layouts, Qt is
  once again going to be a nightmare as it lacks a way to do this other
  than fixed layouts
- GTK+ currently explodes on bsnes, millions of console messages,
  wonderful
- plenty more I'm forgetting

One bit of really cool/good news though: I made
Fixed/Horizontal/Vertical layouts external to phoenix itself. The code
is included for all targets so that it's always there and compiled into
one object, but the great news is that you can easily write your own
layout widgets and they'll work on all toolkits instantly.

That resize issue with bsnes was so simple it's annoying: my FixedLayout
container was repositioning on geometry updates. Made it only do it once
at creation like it should.

bsnes now has a fancy resize, grow the window and get black borders,
shrink it and the video shrinks with it. I plan to make it fancier with
constraint settings (center, scale, stretch). Basically I want to turn
the fullscreen setting into a general setting that also applies to
windowed scaling. I will probably turn the video scale X sizes into
regular items instead of radio boxes, so you can easily reset to a fixed
size whenever you want. Update bsnes to remember width,height geometry
as well and it should be quite nice.
2011-02-07 20:18:01 +11:00
Tim Allen
266495b475 Update to v075r08 release.
byuu says:

Eleven hours of work. Window is now a base type (inherits from Object,
not Widget), same for Layout. FixedLayout still inherits from Layout.
Added HorizontalLayout and VerticalLayout types, that can append each
other to themselves to create box layouts. Layout margins are supported,
spacing is specified inline (I find this a much better way to fine-grain
spacing than Qt's single setSpacing function), and alignment is handled
strictly via padding widgets (insert a zero-sized label and it will
automatically grow to consume all extra space.)

Overall, my box packing model is slightly less powerful than Qt's, but
it is a good deal simpler and and easier to use in 90% of cases. The one
limitation I hit was with my input settings window, I'm not currently
able to embed two different layouts and toggle one on and the other off
to show only either { mouse x-axis, y-axis } or { mouse left, middle,
right }, so they instead just space out differently and I had to grow
the input window width a bit to compensate.

Resizing works great, pretty cool seeing that this is the first time
I've ever written my own resizer. I had to fight with Qt for several
hours to the point of potentially developing an aneurysm, but I finally
got it to properly handle geometry and sizing stuff. Some weird issue
with the bsnes viewport widget, I tell it to resize and for some reason
it doesn't. Cheap hack, I just make it constantly resize every video
refresh and it eventually takes. Wish I knew what was up with that.

All of bsnes now uses dynamic layouts sans the main window, so you can
resize them however you like.

This is still all Qt-only, I'm afraid. The other two ports are
in-progress.
2011-02-07 20:15:43 +11:00
Tim Allen
133d568f76 Update to v075r07 release.
byuu says:

This has the phoenix changes applied. Instead of widgets attaching
directly to windows, you now attach them to layouts, which can then be
attached to windows. Layouts are widgets themselves, so adding layouts
to layouts is trivial. It also allows for multi-widget show/hide, etc.

Right now there is only FixedLayout, but of course the plan is to
support a BoxLayout, that lets you add HorizontalLayout and
VerticalLayout containers to it, thus enabling auto-resize and simpler
form layout.

So far only phoenix/Qt is 100% moved over. phoenix/GTK+ has about 1/3rd
ported, and phoenix/Windows only has one control ported over as
a proof-of-concept.

On the user side, bsnes, bgameboy, snespurify and curse have been moved
to this new layout system. All of bsnes works great with it, as far as
I can tell. Fullscreen, debugger, etc are good.
2011-02-07 20:14:14 +11:00
Tim Allen
b433838e9f Update to v075r06 release.
byuu says:

Removed the floating-point volume adjustments from the wave channel and
the left/right speaker mixers. Also, against my better judgment I'm
backing out of left/right computation when they are both turned off.
This basically makes non-stereo games run faster, but will make stereo
games appear to run slower. I don't like it when end-users experience
mystery slowdowns.

Anyway, it appears that the audio calculation is really fucking
demanding. Knocks FPS from 800 down to 300. I thought it might be libco,
so I took it out and it only went up to 305fps o.O

There is also some sort of problem with bsnes/Super Game Boy audio. The
latency is really great when you first start, but it seems to drift
apart over time until it is well over 500ms, and then it either pops or
fades back to very low, sub-50ms latency again. The way I handle mixing
is that the coprocessor audio samples go into a resampler to the native
SNES rate, and fed to an output buffer. SNES audio samples go there
untouched. When there is a sample in each, I add them together and
average the result (I still don't understand why we divide by two since
these are signed integers), and output it immediately. It's just-in-time
sampling, so as long as DSP v Coprocessor do not drift very far, it
should have very low latency. And I make the CPU sync DSP and
Coprocessor once per scanline, which is something like 15 samples or so.
2011-02-03 22:17:35 +11:00
Tim Allen
a3abe8ebaa Update to v075r05 release.
byuu says:

Added Game Boy sound emulation, all four channels.
It's really, really, really bad. Plenty of bugs, I don't even know what
the fuck a high-pass filter is so that isn't there. Hermite resampling
from 4MHz down to 44KHz. But it's tolerable.
I don't understand what sweep is for at all, and I'm sure I have that
insane recursive reload behavior wrong.

This is pretty much my own design. I referenced blargg's gb snd emu,
blargg's older gb apu ref, Cydrak's APU core, that lousy gbdev wiki
article, the completely and utterly worthless pandocs, and received
nothing but bad and wrong information that just wasted my time from

But I managed to pull it off. It's also painfully slow, like 250fps on
my machine slow. Countless optimizations are possible.
2011-02-02 21:38:28 +11:00
Tim Allen
f88ef9e9a2 Update to v075r04 release.
byuu says:

Changelog:
- hooked up everything necessary for Game Boy sound emulation ...
- bgameboy and bsnes/SGB input 4MHz frequency, and output 44.1KHz
  frequency (produces soft static for now, to verify it is working)
- rewrote all of gameboy/apu, it now has a 4MHz worker thread, and
  separate classes/folders for each channel+master, and serializes

So it's basically all I can do without actual emulation code or
human-readable documentation/example code.
2011-02-02 21:37:31 +11:00
Tim Allen
a136378a7b Update to v075r03 release.
byuu says:

Changelog:
- added full HexEditor widget to phoenix/Qt (has dynamic scrollbar like
  phoenix/GTK, but does not yet support page up/down scrolling)
- optimized debugger to look great with either phoenix/GTK or phoenix/Qt
- fixed phoenix/Qt fullscreen mode (had to allow resizing of the layout,
  and resize the container)
- fixed phoenix/Qt Window::setBackgroundColor() bug that was making
  statusbar invisible
- entering fullscreen now captures mouse, leaving fullscreen releases it
  - so by default, no cursor in fullscreen mode now
- F12 key was assigned the task of toggling mouse capture,
  Tools->Capture Mouse was removed
- above change allows toggling mouse capture in fullscreen if you like

It wasn't my idea, but toggling the mouse capture in fullscreen also hiding the mouse cursor is what I call genius design. Two birds with one stone, and very intuitive.

Also, the default GUI on Linux for bsnes and bgameboy is now Qt, instead
of GTK+. I did this because Qt's fullscreen is far more stable, and
I fixed up the remaining bugs anyway.
2011-02-02 21:35:15 +11:00
Tim Allen
012cdd4b14 Update to v075r02 release.
byuu says:

Changelog:
- added ui-libsnes directory back into source archive; make archive-all
  includes it now
- added basic HexEditor widget to phoenix/Windows
2011-02-02 21:33:35 +11:00
Tim Allen
eecc085e42 Update to v075r01 release.
byuu says:

Changelog:
- fixed Super Game Boy save state support
- both SNES and GameBoy only initialize serialize size on cartridge load
  once now, just like I've already done with memory mapping
- added nall/public_cast.hpp for fun ... don't worry, I'm never actually
  going to use it in production code :D
2011-01-29 20:48:44 +11:00
Tim Allen
6b4104867f Update to v075 release.
byuu says:

This release brings improved Super Game Boy emulation, the final SHA256
hashes for the DSP-(1,1B,2,3,4) and ST-(0010,0011) coprocessors, user
interface improvements, and major internal code restructuring.

Changelog (since v074):
- completely rewrote memory sub-system to support 1-byte granularity in
  XML mapping
- removed Memory inheritance and MMIO class completely, any address can
  be mapped to any function now
- SuperFX: removed SuperFXBus : Bus, now implemented manually
- SA-1: removed SA1Bus : Bus, now implemented manually
- entire bus mapping is now static, happens once on cartridge load
- as a result, read/write handlers now handle MMC mapping; slower
  average case, far faster worst case
- namespace memory is no more, RAM arrays are stored inside the chips
  they are owned by now
- GameBoy: improved CPU HALT emulation, fixes Zelda: Link's Awakening
  scrolling
- GameBoy: added serial emulation (cannot connect to another GB yet),
  fixes Shin Megami Tensei - Devichil
- GameBoy: improved LCD STAT emulation, fixes Sagaia
- ui: added fullscreen support (F11 key), video settings allows for
  three scale settings
- ui: fixed brightness, contrast, gamma, audio volume, input frequency
  values on program startup
- ui: since Qt is dead, config file becomes bsnes.cfg once again
- Super Game Boy: you can now load the BIOS without a game inserted to
  see a pretty white box
- ui-gameboy: can be built without SNES components now
- libsnes: now a UI target, compile with 'make ui=ui-libsnes'
- libsnes: added WRAM, APURAM, VRAM, OAM, CGRAM access (cheat search,
  etc)
- source: removed launcher/, as the Qt port is now gone
- source: Makefile restructuring to better support new ui targets
- source: lots of other internal code cleanup work
2011-01-27 19:52:34 +11:00
Tim Allen
53fe43afd8 Update to v074r11 release.
byuu says:

Changelog:
- debugger compiles on all three profiles
- libsnes compiles on all three platforms (no API changes to libsnes)
- memory.cpp : namespace memory removed (wram -> cpu, apuram -> smp,
  vram, oam, cgram -> ppu)
- sa1.cpp : namespace memory removed (SA-1 specific functions merged
  inline to SA1::bus_read,write)
- GameBoy: added serial link support with interrupts and proper 8192hz
  timing, but obviously it acts as if no other GB is connected to it
- GameBoy: added STAT OAM interrupt, and better STAT d1,d0 mode values
- UI: since Qt is dead, I've renamed the config files back to bsnes.cfg
  and bsnes-geometry.cfg
- SA1: IRAM was not syncing to CPU on SA-1 side
- PPU/Accuracy and PPU/Performance needed Sprite oam renamed to Sprite
  sprite; so that I could add uint8 oam[544]
  - makes more sense anyway, OAM = object attribute memory, obj or
    sprite are better names for Sprite rendering class
- more cleanup
2011-01-24 20:03:17 +11:00
Tim Allen
054bdd4094 Update to v074r10 release.
byuu says:

Major WIP, countless changes. I really went to town on cleaning up the
source today with all kinds of new ideas. I'll post the ones I remember,
use diff -ru to get the rest.

What I like the most is my new within template:

    template<unsigned lo, unsigned hi>
    alwaysinline bool within(unsigned addr) {
      static const unsigned mask = ~(hi ^ lo);
      return (addr & mask) == lo;
    }

Before, you would see code like this:

    if((addr & 0xe0e000) == 0x206000) {  //$20-3f:6000-7fff

The comment is basically necessary, and you have to trust that the mask
is right, or do the math yourself.

Now, it looks like this:

    if(within<0x20, 0x3f, 0x6000, 0x7fff>(addr)) {

That's the same as within<0x206000, 0x3f7fff>, I just made an
SNES-variant to more closely simulate my XML mapping style:
20-3f:6000-7fff.

Now obviously this has limitations, it only works in base-2 and it can't
manage some tricky edge cases like (addr & 0x408000) == 0x008000 for
00-3f|80-bf:8000-ffff. But for the most part, I'll be using this where
I can. The Game Boy is fully ported over to it (via the MBCs), but the
SNES only has the BS-X town cartridge moved over so far. SuperFX and
SA-1 at the very least could benefit.

Next up, since the memory map is now static, there's really no reason to
remap the entire thing at power-on and reset. So it is now set up at
cartridge load and that's it. I moved the CPU/PPU/WRAM mapping out of
memory.cpp and into their respective processors. A bit of duplication
only because there are multiple processor cores for the different
profiles, but I'm not worried about that. This is also going to be
necessary to fix the debugger.

Next, Coprocessor::enable() actually does what I initially intended it
to now: it is called once to turn a chip on after cartridge load. It's
not called on power cycle anymore. This should help fix power-cycle on
my serial simulation code, and was needed to map the bus exactly one
time. Although most stuff is mapped through XML, some chips still need
some manual hooks for monitoring and such (eg S-DD1.)

Next, I've started killing off memory::, it was initially an
over-reaction to the question of where to put APURAM (in the SMP or
DSP?). The idea was to have this namespace that contained all memory for
everything. But it was very annoying and tedious, and various chips
ignored the convention anyway like ST-0011 RAM, which couldn't work
anyway since it is natively uint16 and not uint8. Cx4 will need 24-bit
RAM eventually, too. There's 8->24-bit functions in there now, because
the HLE code is hideous.

So far, all the cartridge.cpp memory:: types have been destroyed.
memory::cartrom, memory::cartram become cartridge.rom and cartridge.ram.
memory::cartrtc was moved into the SRTC and SPC7110 classes directly.
memory::bsxflash was moved into BSXFlash. memory::bsxram and
memory::bsxpram were moved into BSXCartridge (the town cartridge).
memory::st[AB](rom|ram) were moved into a new area,
snes/chip/sufamiturbo. The snes/chip moniker really doesn't work so
well, since it also has base units, and the serial communications stuff
which is through the controller port, but oh well, now it also has the
base structure for the Sufami Turbo cartridge too. So now we have
sufamiturbo.slotA.rom, sufamiturbo.slotB.ram, etc.

Next, the ST-0010/ST-0011 actually save the data RAM to disk. This
wasn't at all compatible with my old system, and I didn't want to keep
adding memory types to check inside the main UI cartridge RAM loading
and saving routines.

So I built a NonVolatileRAM vector inside SNES::Cartridge, and any chip
that has memory it wants to save and load from disk can append onto it
: data, size, id ("srm", "rtc", "nec", etc) and slot (0 = cartridge,
1 = slot A, 2 = slot B)

To load and save memory, we just do a simple: foreach(memory,
SNES::cartridge.nvram) load/saveMemory(memory).

As a result, you can now keep your save games in F1 Race of Champions II
and Hayazashi Nidan Morita Shougi. Technically I think Metal Combat
should work this way as well, having the RAM being part of the chip
itself, but for now that chip just writes directly into cartridge.ram,
so it also technically saves to disk for now.

To avoid a potential conflict with a manipulated memory map, BS-X SRAM
and PSRAM are now .bss and .bsp, and not .srm and .psr. Honestly I don't
like .srm as an extension either, but it doesn't bother me enough to
break save RAM compatibility with other emulators, so don't worry about
that changing.

I finally killed off MappedRAM initializing size to ~0 (-1U). A size of
zero means there is no memory there just the same. This was an old
holdover for handling MMIO mapping, if I recall correctly. Something
about a size of zero on MMIO-Memory objects causing it to wrap the
address, so ~0 would let it map direct addresses ... or something.
Whatever, that's not needed at all anymore.

BSXBase becomes BSXSatellaview, and I've defaulted the device to being
attached since it won't affect non-BSX games anyway. Eventually the GUI
needs to make that an option. BSXCart becomes BSXCartridge. BSXFlash
remains unchanged.

I probably need to make Coprocessor::disable() functions now to free up
memory on unload, but it shouldn't hurt anything the way it is.

libsnes is most definitely broken to all hell and back now, and the
debugger is still shot. I suppose we'll need some tricky code to work
with the old ID system, and we'll need to add some more IDs for the new
memory types.
2011-01-24 19:59:45 +11:00
Tim Allen
ecf96726f9 Update to v074r09 release.
byuu says:

This WIP fixes up the last bit from the old memory mapping system, the
BS-X Satellaview town cartridge now has a static memory mapper that is
dynamically decoded based on register settings. Tested with BS Town + BS
Zelda, and I can get in-game just fine. The mapping code is actually
a lot more readable this way, too.
2011-01-22 19:18:03 +11:00
Tim Allen
cab5917806 Update to v074r08 release.
byuu says:

The nall::function binding for the memory map apparently breaks when the
debugger is enabled, as PPU:: becomes PPUdebugger::, etc; and C++ isn't
smart enough to upconvert for us.
Not sure how I am going to work around that yet ...

Changelog:
- improved GameBoy::CPU::Halt emulation, fixes Legend of Zelda intro
  water+world-map scrolling at the same time
- added GameBoy::APU skeleton, and hooked up MMIO read/write for all
  registers
- modified nall integer->string functions
2011-01-22 19:15:49 +11:00
Tim Allen
c833b69087 Update to v074r07 release.
byuu says:

Fullscreen works on all ports, but Qt is in general pretty wonky at the
moment. Don't really care right now.

F11 toggles fullscreen, there is no menu or status in fullscreen, and it
auto-scales to the highest even multiple that it can.
It copies all other settings (filter, shader, aspect correction, video
region, vsync, async) from your windowed mode settings.
You cannot enable the menu or status bars, because those are a major
pain in the ass to support with both GTK+ and Qt returning nonsense
geometries.
Combine with each platform treating whether or not to handle menus
and/or statuses as part of the geometry or not, and it's a recipe for
pain.
It is pseudo-fullscreen for now, but if we have some spiffy code in the
future to set a true fullscreen that works on Linux (and hopefully OS
X), I'll add a true-FS option to ruby later.
2011-01-18 21:20:05 +11:00
Tim Allen
1a065bafb1 Update to v074r06 release.
byuu says:

This WIP uses the 16MB+64MB memory tables for 1:1 mapping of the S-CPU
bus.
Minimum RAM requirement goes up to 128MB, dare I be bold here and
recommend you have 256MB.

I also hooked up the basic bindings for making pseudo-fullscreen windows
in phoenix/Windows and phoenix/GTK+, but both have some serious issues.
- GTK+ won't resize my form container, even though it's the same code
  I used successfully in bsnes v039 and prior with hiro
- Windows scale selection breaks the faux-fullscreen effects
- I am intending to write off the menu/status bars and just auto-size
  the video to fill the screen, nice and simple
2011-01-18 21:17:48 +11:00
Tim Allen
ea077a7d96 Update to v074r05 release.
byuu says:

Oh good, that turned out to be a lot easier than I expected.
Almost all of the work was already done yesterday in porting the SA-1
over to a static map.
2011-01-17 00:22:51 +11:00
Tim Allen
a937f9b79b Update to v074r04 release.
byuu says:

SuperFXBus was inheriting from Bus, in other words using a copy of the
same memory mapping subsystem.
I've removed that and made it a simple mask, since only ROM+RAM are
mapped there.
2011-01-17 00:17:45 +11:00
Tim Allen
2d73086569 Update to v074r03 release.
byuu says:

You guys are going to hate the hell out of this one. It's twenty hours
of non-stop work, no exaggeration at all. Started at 4AM, just wrapped
up now at 8PM.
I rewrote the entire memory subsystem.

Old system:
65536 pages that map 256 bytes each
Mapping a new page overwrites old page
Granularity capped at 256 bytes minimum, requiring ST-001x to map
    60:0000-00ff instead of 60:0000,0001
Classes inherit from MMIO and Memory, forcing only one mappable function
    per class, and fixed names
MMIO sub-mapper inside memory: 00-3f:2000-5fff for one-byte granularity
Can dynamically change the map at run-time, MMC register settings
    perform dynamic remapping

New system:
XML mapping is still based around banklo-bankhi:addrlo-addrhi, as that
    shapes almost everything on the SNES very well
Internally, 2048 pages that map 8192 bytes each
Pages are vectors, scans O(n) from last to first (O(log n) would not
    help, n is never > 3)
Can multi-cast writes, but not reads [for the obvious reason of: which
    read do you return?]
Can map reads and writes separately
Granularity of one for entire 24-bit address range, no need for MMIO
    - whatever is in XML is exactly what you get
Read/Write tables bind function callbacks, so I can have any number of
    functions with any names from any classes with no inheritance (no
    more uPD7725DR, uPD7725SR helpers, etc)
Less memory usage overall due to less tables [ I tried 16 million tables
    and it used 2GB of RAM >_o ]
Cannot dynamically change the map at run-time, MMC read/write functions
    perform address translation [worse average case speed, better worst
    case speed]

Now the hate me part, functors can't beat virtual functions for speed.
There are speed penalties involved:
-4.5% on average games
-11% on SuperFX games (SFX has its own bus)
-15% on SA-1 games (SA-1 has two buses)
Of course the two that need the speed the most get the biggest hits.

I'm afraid there's really not a lot of wiggle room to boost speed back
up.
I suppose one bright spot is that we can much more easily try out
entirely new mapping systems now, since the dynamic portions have been
eliminated.
2011-01-15 15:30:29 +11:00
Tim Allen
5810e69be3 Update to v074r02 release.
byuu says:

Changelog:
- updated/fixed st0011.bin SHA256 sum
- removed launcher/
- building ui-gameboy only builds the Game Boy core, not the SNES core
- binary name output is bgameboy for ui-gameboy
- make install/make uninstall work as expected with the extra data files

[Editor's note - those last three changes seem to have been made in
v074r01]
2011-01-13 21:08:27 +11:00
Tim Allen
a198e555dc Update to v074r01 release.
byuu says:

Changelog:
- fixed libsnes to compile again, the GB RTC constant is there but
  doesn't do anything just yet (no serialize support in GameBoy core)
- libsnes: added SNES_MEMORY_(WRAM,APURAM,VRAM,OAM,CGRAM) -- really only
  for the first one, it allows libsnes users to implement their own
  cheat search
- you can now load the SGB BIOS without a game!! please be sure to enjoy
  the blinking cartridge icon emulation :D
- necdsp (uPD7725,96050) - simplified code a bit: removed persistent
  regs.idb, simplified jumps, merged exec() with main loop, etc.
- nall::function - fixed an initialization bug when copy-constructing
  objects
- nall::vector - use calloc instead of malloc to help safeguard against
  uninitialized class data (potentially hides errors, but better than
  crashing in production)
2011-01-13 21:07:04 +11:00
761 changed files with 94511 additions and 71480 deletions

View File

@@ -4,6 +4,9 @@ gameboy := gameboy
profile := accuracy
ui := ui
# debugger
options :=
# compiler
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
@@ -18,6 +21,8 @@ objects := libco
# profile-guided optimization
# flags += -fprofile-use
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
# platform
ifeq ($(platform),x)
link += -s -ldl -lX11 -lXext
@@ -48,39 +53,10 @@ all: build;
obj/libco.o: libco/libco.c libco/*
include $(snes)/Makefile
include $(gameboy)/Makefile
include $(ui)/Makefile
objects := $(patsubst %,obj/%.o,$(objects))
# targets
build: ui_build $(objects)
ifeq ($(platform),osx)
test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS
$(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link))
else
$(strip $(cpp) -o out/bsnes $(objects) $(link))
endif
install:
ifeq ($(platform),x)
install -D -m 755 out/bsnes $(DESTDIR)$(prefix)/bin/bsnes
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
test -d ~/.bsnes || mkdir ~/.bsnes
cp data/cheats.xml ~/.bsnes/cheats.xml
chmod 777 ~/.bsnes ~/.bsnes/cheats.xml
endif
uninstall:
ifeq ($(platform),x)
rm $(DESTDIR)$(prefix)/bin/bsnes
rm $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
rm $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
endif
clean: ui_clean
clean:
-@$(call delete,obj/*.o)
-@$(call delete,obj/*.a)
-@$(call delete,obj/*.so)
@@ -94,6 +70,6 @@ clean: ui_clean
-@$(call delete,*.manifest)
archive-all:
tar -cjf bsnes.tar.bz2 data gameboy launcher libco nall obj out phoenix ruby snes ui ui-gameboy Makefile cc.bat clean.bat sync.sh
tar -cjf bsnes.tar.bz2 data gameboy libco nall obj out phoenix ruby snes ui ui-gameboy ui-libsnes Makefile cc.bat clean.bat sync.sh
help:;

View File

@@ -1,2 +1,2 @@
@mingw32-make -j 2
@mingw32-make -j 8
@pause

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
gameboy_objects := gameboy-system gameboy-scheduler
gameboy_objects += gameboy-memory gameboy-cartridge
gameboy_objects += gameboy-cpu gameboy-lcd
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
objects += $(gameboy_objects)
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
@@ -8,4 +8,5 @@ obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(g
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/)
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)

105
bsnes/gameboy/apu/apu.cpp Executable file
View File

@@ -0,0 +1,105 @@
#include <gameboy/gameboy.hpp>
#define APU_CPP
namespace GameBoy {
#include "square1/square1.cpp"
#include "square2/square2.cpp"
#include "wave/wave.cpp"
#include "noise/noise.cpp"
#include "master/master.cpp"
#include "serialization.cpp"
APU apu;
void APU::Main() {
apu.main();
}
void APU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(sequencer_base == 0) { //512hz
if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz
square1.clock_length();
square2.clock_length();
wave.clock_length();
noise.clock_length();
}
if(sequencer_step == 2 || sequencer_step == 6) { //128hz
square1.clock_sweep();
}
if(sequencer_step == 7) { //64hz
square1.clock_envelope();
square2.clock_envelope();
noise.clock_envelope();
}
sequencer_step++;
}
sequencer_base++;
square1.run();
square2.run();
wave.run();
noise.run();
master.run();
system.interface->audio_sample(master.center, master.left, master.right);
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
void APU::power() {
create(Main, 4194304);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
foreach(n, mmio_data) n = 0x00;
sequencer_base = 0;
sequencer_step = 0;
square1.power();
square2.power();
wave.power();
noise.power();
master.power();
}
uint8 APU::mmio_read(uint16 addr) {
static const uint8 table[48] = {
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave
0xff, 0xff, 0x00, 0x00, 0xbf, //noise
0x00, 0x00, 0x70, //master
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
};
if(addr == 0xff26) {
uint8 data = master.enable << 7;
if(square1.counter && square1.length) data |= 0x01;
if(square2.counter && square2.length) data |= 0x02;
if( wave.counter && wave.length) data |= 0x04;
if( noise.counter && noise.length) data |= 0x08;
return data | table[addr - 0xff10];
}
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
return 0xff;
}
void APU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
}
}

28
bsnes/gameboy/apu/apu.hpp Executable file
View File

@@ -0,0 +1,28 @@
struct APU : Processor, MMIO {
#include "square1/square1.hpp"
#include "square2/square2.hpp"
#include "wave/wave.hpp"
#include "noise/noise.hpp"
#include "master/master.hpp"
uint8 mmio_data[48];
uint13 sequencer_base;
uint3 sequencer_step;
Square1 square1;
Square2 square2;
Wave wave;
Noise noise;
Master master;
static void Main();
void main();
void power();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void serialize(serializer&);
};
extern APU apu;

View File

@@ -0,0 +1,132 @@
#ifdef APU_CPP
void APU::Master::run() {
if(enable == false) {
center = 0;
left = 0;
right = 0;
return;
}
signed sample = 0, channels;
sample += apu.square1.output;
sample += apu.square2.output;
sample += apu.wave.output;
sample += apu.noise.output;
sample >>= 2;
center = sclamp<16>(sample);
if(left_enable == false && right_enable == false) {
left = center;
right = center;
return;
}
sample = 0;
channels = 0;
if(channel1_left_enable) { sample += apu.square1.output; channels++; }
if(channel2_left_enable) { sample += apu.square2.output; channels++; }
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
left = sclamp<16>(sample);
switch(left_volume) {
case 0: left >>= 3; break; // 12.5%
case 1: left >>= 2; break; // 25.0%
case 2: left = (left >> 2) + (left >> 3); break; // 37.5%
case 3: left >>= 1; break; // 50.0%
case 4: left = (left >> 1) + (left >> 3); break; // 62.5%
case 5: left -= (left >> 2); break; // 75.0%
case 6: left -= (left >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(left_enable == false) left = 0;
sample = 0;
channels = 0;
if(channel1_right_enable) { sample += apu.square1.output; channels++; }
if(channel2_right_enable) { sample += apu.square2.output; channels++; }
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
right = sclamp<16>(sample);
switch(right_volume) {
case 0: right >>= 3; break; // 12.5%
case 1: right >>= 2; break; // 25.0%
case 2: right = (right >> 2) + (right >> 3); break; // 37.5%
case 3: right >>= 1; break; // 50.0%
case 4: right = (right >> 1) + (right >> 3); break; // 62.5%
case 5: right -= (right >> 2); break; // 75.0%
case 6: right -= (right >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(right_enable == false) right = 0;
}
void APU::Master::write(unsigned r, uint8 data) {
if(r == 0) {
left_enable = data & 0x80;
left_volume = (data >> 4) & 7;
right_enable = data & 0x08;
right_volume = (data >> 0) & 7;
}
if(r == 1) {
channel4_left_enable = data & 0x80;
channel3_left_enable = data & 0x40;
channel2_left_enable = data & 0x20;
channel1_left_enable = data & 0x10;
channel4_right_enable = data & 0x08;
channel3_right_enable = data & 0x04;
channel2_right_enable = data & 0x02;
channel1_right_enable = data & 0x01;
}
if(r == 2) {
enable = data & 0x80;
}
}
void APU::Master::power() {
left_enable = 0;
left_volume = 0;
right_enable = 0;
right_volume = 0;
channel4_left_enable = 0;
channel3_left_enable = 0;
channel2_left_enable = 0;
channel1_left_enable = 0;
channel4_right_enable = 0;
channel3_right_enable = 0;
channel2_right_enable = 0;
channel1_right_enable = 0;
enable = 0;
center = 0;
left = 0;
right = 0;
}
void APU::Master::serialize(serializer &s) {
s.integer(left_enable);
s.integer(left_volume);
s.integer(right_enable);
s.integer(right_volume);
s.integer(channel4_left_enable);
s.integer(channel3_left_enable);
s.integer(channel2_left_enable);
s.integer(channel1_left_enable);
s.integer(channel4_right_enable);
s.integer(channel3_right_enable);
s.integer(channel2_right_enable);
s.integer(channel1_right_enable);
s.integer(enable);
s.integer(center);
s.integer(left);
s.integer(right);
}
#endif

View File

@@ -0,0 +1,24 @@
struct Master {
bool left_enable;
unsigned left_volume;
bool right_enable;
unsigned right_volume;
bool channel4_left_enable;
bool channel3_left_enable;
bool channel2_left_enable;
bool channel1_left_enable;
bool channel4_right_enable;
bool channel3_right_enable;
bool channel2_right_enable;
bool channel1_right_enable;
bool enable;
int16 center;
int16 left;
int16 right;
void run();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

102
bsnes/gameboy/apu/noise/noise.cpp Executable file
View File

@@ -0,0 +1,102 @@
#ifdef APU_CPP
void APU::Noise::run() {
if(period && --period == 0) {
period = divisor << frequency;
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << 14);
if(narrow_lfsr) lfsr |= (bit << 6);
}
}
uint4 sample = (lfsr & 1) ? 0 : volume;
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Noise::clock_length() {
if(counter && length) length--;
}
void APU::Noise::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) {
initial_length = 64 - (data & 0x3f);
length = initial_length;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
if(initialize) {
lfsr = ~0U;
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
}
}
}
void APU::Noise::power() {
initial_length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
narrow_lfsr = 0;
divisor = 0;
counter = 0;
output = 0;
length = 0;
envelope_period = 0;
volume = 0;
period = 0;
lfsr = 0;
}
void APU::Noise::serialize(serializer &s) {
s.integer(initial_length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(narrow_lfsr);
s.integer(divisor);
s.integer(counter);
s.integer(output);
s.integer(length);
s.integer(envelope_period);
s.integer(volume);
s.integer(period);
s.integer(lfsr);
}
#endif

View File

@@ -0,0 +1,24 @@
struct Noise {
unsigned initial_length;
unsigned envelope_volume;
bool envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
bool narrow_lfsr;
unsigned divisor;
bool counter;
int16 output;
unsigned length;
unsigned envelope_period;
unsigned volume;
unsigned period;
uint15 lfsr;
void run();
void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -0,0 +1,15 @@
#ifdef APU_CPP
void APU::serialize(serializer &s) {
s.array(mmio_data);
s.integer(sequencer_base);
s.integer(sequencer_step);
square1.serialize(s);
square2.serialize(s);
wave.serialize(s);
noise.serialize(s);
master.serialize(s);
}
#endif

View File

@@ -0,0 +1,146 @@
#ifdef APU_CPP
void APU::Square1::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Square1::sweep() {
if(enable == false) return;
signed offset = frequency_shadow >> sweep_shift;
if(sweep_direction) offset = -offset;
frequency_shadow += offset;
if(frequency_shadow < 0) {
frequency_shadow = 0;
} else if(frequency_shadow > 2047) {
frequency_shadow = 2048;
enable = false;
}
if(frequency_shadow <= 2047 && sweep_shift) {
frequency = frequency_shadow;
period = 4 * (2048 - frequency);
}
}
void APU::Square1::clock_length() {
if(counter && length) length--;
}
void APU::Square1::clock_sweep() {
if(sweep_frequency && sweep_period && --sweep_period == 0) {
sweep_period = sweep_frequency;
sweep();
}
}
void APU::Square1::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Square1::write(unsigned r, uint8 data) {
if(r == 0) {
sweep_frequency = (data >> 4) & 7;
sweep_direction = data & 0x08;
sweep_shift = data & 0x07;
}
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
envelope_period = envelope_frequency;
volume = envelope_volume;
frequency_shadow = frequency;
sweep_period = sweep_frequency;
enable = sweep_period || sweep_shift;
if(sweep_shift) sweep();
}
}
period = 4 * (2048 - frequency);
}
void APU::Square1::power() {
sweep_frequency = 0;
sweep_direction = 0;
sweep_shift = 0;
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
counter = 0;
output = 0;
duty_output = 0;
phase = 0;
period = 0;
envelope_period = 0;
sweep_period = 0;
frequency_shadow = 0;
enable = 0;
volume = 0;
}
void APU::Square1::serialize(serializer &s) {
s.integer(sweep_frequency);
s.integer(sweep_direction);
s.integer(sweep_shift);
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
s.integer(duty_output);
s.integer(phase);
s.integer(period);
s.integer(envelope_period);
s.integer(sweep_period);
s.integer(frequency_shadow);
s.integer(enable);
s.integer(volume);
}
#endif

View File

@@ -0,0 +1,31 @@
struct Square1 {
unsigned sweep_frequency;
unsigned sweep_direction;
unsigned sweep_shift;
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned sweep_period;
signed frequency_shadow;
bool enable;
unsigned volume;
void run();
void sweep();
void clock_length();
void clock_sweep();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -0,0 +1,97 @@
#ifdef APU_CPP
void APU::Square2::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Square2::clock_length() {
if(counter && length) length--;
}
void APU::Square2::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Square2::write(unsigned r, uint8 data) {
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
envelope_period = envelope_frequency;
volume = envelope_volume;
}
}
period = 4 * (2048 - frequency);
}
void APU::Square2::power() {
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
counter = 0;
output = 0;
duty_output = 0;
phase = 0;
period = 0;
envelope_period = 0;
volume = 0;
}
void APU::Square2::serialize(serializer &s) {
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
s.integer(duty_output);
s.integer(phase);
s.integer(period);
s.integer(envelope_period);
s.integer(volume);
}
#endif

View File

@@ -0,0 +1,23 @@
struct Square2 {
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned volume;
void run();
void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

103
bsnes/gameboy/apu/wave/wave.cpp Executable file
View File

@@ -0,0 +1,103 @@
#ifdef APU_CPP
void APU::Wave::run() {
if(period && --period == 0) {
period = 2 * (2048 - frequency);
pattern_offset = (pattern_offset + 1) & 31;
pattern_sample = pattern[pattern_offset];
}
uint4 sample = pattern_sample;
if(counter && length == 0) sample = 0;
if(enable == false) sample = 0;
output = (sample * 4369) - 32768;
output >>= volume;
}
void APU::Wave::clock_length() {
if(counter && length) length--;
}
void APU::Wave::write(unsigned r, uint8 data) {
if(r == 0) {
dac_enable = data & 0x80;
if(dac_enable == false) enable = false;
}
if(r == 1) {
initial_length = 256 - data;
length = initial_length;
}
if(r == 2) {
switch((data >> 5) & 3) {
case 0: volume = 16; break; // 0%
case 1: volume = 0; break; //100%
case 2: volume = 1; break; // 50%
case 3: volume = 2; break; // 25%
}
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize && dac_enable) {
enable = true;
pattern_offset = 0;
length = initial_length;
}
}
period = 2 * (2048 - frequency);
}
void APU::Wave::write_pattern(unsigned p, uint8 data) {
p <<= 1;
pattern[p + 0] = (data >> 4) & 15;
pattern[p + 1] = (data >> 0) & 15;
}
void APU::Wave::power() {
dac_enable = 0;
initial_length = 0;
volume = 0;
frequency = 0;
counter = 0;
random_lfsr r;
foreach(n, pattern) n = r() & 15;
output = 0;
enable = 0;
length = 0;
period = 0;
pattern_offset = 0;
pattern_sample = 0;
}
void APU::Wave::serialize(serializer &s) {
s.integer(dac_enable);
s.integer(initial_length);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
s.array(pattern);
s.integer(output);
s.integer(enable);
s.integer(length);
s.integer(period);
s.integer(pattern_offset);
s.integer(pattern_sample);
}
#endif

22
bsnes/gameboy/apu/wave/wave.hpp Executable file
View File

@@ -0,0 +1,22 @@
struct Wave {
bool dac_enable;
unsigned initial_length;
unsigned volume;
unsigned frequency;
bool counter;
uint8 pattern[32];
int16 output;
bool enable;
unsigned length;
unsigned period;
unsigned pattern_offset;
unsigned pattern_sample;
void run();
void clock_length();
void write(unsigned r, uint8 data);
void write_pattern(unsigned p, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -16,13 +16,14 @@ namespace GameBoy {
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(const string &xml, uint8_t *data, unsigned size) {
void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
if(size == 0) size = 32768;
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
//uint32_t crc = crc32_calculate(data, size);
//print("CRC32 = ", hex<4>(crc), "\n");
romdata = new uint8[romsize = size];
memcpy(romdata, data, size);
info.mapper = Mapper::Unknown;
info.ram = false;
info.battery = false;
@@ -69,7 +70,19 @@ void Cartridge::load(const string &xml, uint8_t *data, unsigned size) {
}
}
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
}
ramdata = new uint8_t[ramsize = info.ramsize]();
system.load();
loaded = true;
}
@@ -103,7 +116,19 @@ void Cartridge::ram_write(unsigned addr, uint8 data) {
ramdata[addr] = data;
}
uint8 Cartridge::mmio_read(uint16 addr) {
if(bootrom_enable && within<0x0000, 0x00ff>(addr)) return System::BootROM::sgb[addr];
return mapper->mmio_read(addr);
}
void Cartridge::mmio_write(uint16 addr, uint8 data) {
if(bootrom_enable && addr == 0xff50) bootrom_enable = false;
mapper->mmio_write(addr, data);
}
void Cartridge::power() {
bootrom_enable = true;
mbc0.power();
mbc1.power();
mbc2.power();
@@ -112,26 +137,10 @@ void Cartridge::power() {
mmm01.power();
huc1.power();
huc3.power();
map();
}
void Cartridge::map() {
MMIO *mapper = 0;
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
}
if(mapper) {
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = mapper;
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = mapper;
}
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = this;
}
Cartridge::Cartridge() {

View File

@@ -1,4 +1,4 @@
struct Cartridge : property<Cartridge> {
struct Cartridge : MMIO, property<Cartridge> {
#include "mbc0/mbc0.hpp"
#include "mbc1/mbc1.hpp"
#include "mbc2/mbc2.hpp"
@@ -41,7 +41,10 @@ struct Cartridge : property<Cartridge> {
uint8_t *ramdata;
unsigned ramsize;
void load(const string &xml, uint8_t *data, unsigned size);
MMIO *mapper;
bool bootrom_enable;
void load(const string &xml, const uint8_t *data, unsigned size);
void unload();
uint8 rom_read(unsigned addr);
@@ -49,8 +52,10 @@ struct Cartridge : property<Cartridge> {
uint8 ram_read(unsigned addr);
void ram_write(unsigned addr, uint8 data);
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
void map();
void serialize(serializer&);
Cartridge();

View File

@@ -1,15 +1,15 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
if((addr & 0xc000) == 0x0000) { //0000-3fff
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if((addr & 0xc000) == 0x4000) { //4000-7fff
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
@@ -18,27 +18,27 @@ uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
}
void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0x0000) { //0000-1fff
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if((addr & 0xe000) == 0x2000) { //2000-3fff
if(within<0x2000, 0x3fff>(addr)) {
rom_select = data;
return;
}
if((addr & 0xe000) == 0x4000) { //4000-5fff
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data;
return;
}
if((addr & 0xe000) == 0x6000) { //6000-7fff
if(within<0x6000, 0x7fff>(addr)) {
//unknown purpose
return;
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}

View File

@@ -1,15 +1,15 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
if((addr & 0xc000) == 0x0000) { //0000-3fff
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if((addr & 0xc000) == 0x4000) { //4000-7fff
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
@@ -18,27 +18,27 @@ uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
}
void Cartridge::HuC3::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0x0000) { //0000-1fff
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if((addr & 0xe000) == 0x2000) { //2000-3fff
if(within<0x2000, 0x3fff>(addr)) {
rom_select = data;
return;
}
if((addr & 0xe000) == 0x4000) { //4000-5fff
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data;
return;
}
if((addr & 0xe000) == 0x6000) { //6000-7fff
if(within<0x6000, 0x7fff>(addr)) {
//unknown purpose
return;
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}

View File

@@ -1,11 +1,11 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
if((addr & 0x8000) == 0x0000) { //0000-7fff
if(within<0x0000, 0x7fff>(addr)) {
return cartridge.rom_read(addr);
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
return cartridge.ram_read(addr & 0x1fff);
}
@@ -13,7 +13,7 @@ uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
}
void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
cartridge.ram_write(addr & 0x1fff, data);
return;
}

View File

@@ -1,11 +1,11 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
if((addr & 0xc000) == 0x0000) { //0000-3fff
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if((addr & 0xc000) == 0x4000) { //4000-7fff
if(within<0x4000, 0x7fff>(addr)) {
if(mode_select == 0) {
return cartridge.rom_read((ram_select << 19) | (rom_select << 14) | (addr & 0x3fff));
} else {
@@ -13,7 +13,7 @@ uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
}
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(mode_select == 0) {
return cartridge.ram_read(addr & 0x1fff);
@@ -28,27 +28,27 @@ uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
}
void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0x0000) { //0000-1fff
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if((addr & 0xe000) == 0x2000) { //2000-3fff
if(within<0x2000, 0x3fff>(addr)) {
rom_select = (data & 0x1f) + ((data & 0x1f) == 0);
return;
}
if((addr & 0xe000) == 0x4000) { //4000-5fff
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data & 0x03;
return;
}
if((addr & 0xe000) == 0x6000) { //6000-7fff
if(within<0x6000, 0x7fff>(addr)) {
mode_select = data & 0x01;
return;
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(mode_select == 0) {
cartridge.ram_write(addr & 0x1fff, data);

View File

@@ -1,15 +1,15 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
if((addr & 0xc000) == 0x0000) { //0000-3fff
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if((addr & 0xc000) == 0x4000) { //4000-7fff
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if((addr & 0xfe00) == 0xa000) { //a000-a1ff
if(within<0xa000, 0xa1ff>(addr)) {
if(ram_enable) return cartridge.ram_read(addr & 0x1ff);
return 0x00;
}
@@ -18,17 +18,17 @@ uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
}
void Cartridge::MBC2::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe100) == 0x0000) { //0000-1fff [d8=0]
ram_enable = (data & 0x0f) == 0x0a;
if(within<0x0000, 0x1fff>(addr)) {
if(!(addr & 0x0100)) ram_enable = (data & 0x0f) == 0x0a;
return;
}
if((addr & 0xe100) == 0x2100) { //2000-3fff [d8=1]
rom_select = (data & 0x0f) + ((data & 0x0f) == 0);
if(within<0x2000, 0x3fff>(addr)) {
if( (addr & 0x0100)) rom_select = (data & 0x0f) + ((data & 0x0f) == 0);
return;
}
if((addr & 0xfe00) == 0xa000) { //a000-a1ff
if(within<0xa000, 0xa1ff>(addr)) {
if(ram_enable) cartridge.ram_write(addr & 0x1ff, data & 0x0f);
return;
}

View File

@@ -19,15 +19,15 @@ void Cartridge::MBC3::second() {
}
uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
if((addr & 0xc000) == 0x0000) { //0000-3fff
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if((addr & 0xc000) == 0x4000) { //4000-7fff
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(ram_select >= 0x00 && ram_select <= 0x03) {
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
@@ -45,22 +45,22 @@ uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
}
void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0x0000) { //0000-1fff
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if((addr & 0xe000) == 0x2000) { //2000-3fff
if(within<0x2000, 0x3fff>(addr)) {
rom_select = (data & 0x7f) + ((data & 0x7f) == 0);
return;
}
if((addr & 0xe000) == 0x4000) { //4000-5fff
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data;
return;
}
if((addr & 0xe000) == 0x6000) { //6000-7fff
if(within<0x6000, 0x7fff>(addr)) {
if(rtc_latch == 0 && data == 1) {
rtc_latch_second = rtc_second;
rtc_latch_minute = rtc_minute;
@@ -72,7 +72,7 @@ void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
return;
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) {
if(ram_select >= 0x00 && ram_select <= 0x03) {
cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);

View File

@@ -1,15 +1,15 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
if((addr & 0xc000) == 0x0000) { //0000-3fff
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(addr);
}
if((addr & 0xc000) == 0x4000) { //4000-7fff
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
return 0x00;
}
@@ -18,27 +18,27 @@ uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
}
void Cartridge::MBC5::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0x0000) { //0000-1fff
if(within<0x0000, 0x1fff>(addr)) {
ram_enable = (data & 0x0f) == 0x0a;
return;
}
if((addr & 0xf000) == 0x2000) { //2000-2fff
if(within<0x2000, 0x2fff>(addr)) {
rom_select = (rom_select & 0x0100) | data;
return;
}
if((addr & 0xf000) == 0x3000) { //3000-3fff
if(within<0x3000, 0x3fff>(addr)) {
rom_select = ((data & 1) << 8) | (rom_select & 0x00ff);
return;
}
if((addr & 0xe000) == 0x4000) { //4000-5fff
if(within<0x4000, 0x5fff>(addr)) {
ram_select = data & 0x0f;
return;
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
return;
}

View File

@@ -1,19 +1,19 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
if((addr & 0x8000) == 0x0000) {
if(within<0x0000, 0x7fff>(addr)) {
if(rom_mode == 0) return cartridge.rom_read(addr);
}
if((addr & 0xc000) == 0x0000) {
if(within<0x0000, 0x3fff>(addr)) {
return cartridge.rom_read(0x8000 + (rom_base << 14) + (addr & 0x3fff));
}
if((addr & 0xc000) == 0x4000) {
if(within<0x4000, 0x7fff>(addr)) {
return cartridge.rom_read(0x8000 + (rom_base << 14) + (rom_select << 14) + (addr & 0x3fff));
}
if((addr & 0xe000) == 0xa000) {
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) return cartridge.ram_read((ram_select << 13) + (addr & 0x1fff));
return 0x00;
}
@@ -22,7 +22,7 @@ uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
}
void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
if((addr & 0xe000) == 0x0000) { //0000-1fff
if(within<0x0000, 0x1fff>(addr)) {
if(rom_mode == 0) {
rom_mode = 1;
} else {
@@ -30,7 +30,7 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
}
}
if((addr & 0xe000) == 0x2000) { //2000-3fff
if(within<0x2000, 0x3fff>(addr)) {
if(rom_mode == 0) {
rom_base = data & 0x3f;
} else {
@@ -38,17 +38,17 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
}
}
if((addr & 0xe000) == 0x4000) { //4000-5fff
if(within<0x4000, 0x5fff>(addr)) {
if(rom_mode == 1) {
ram_select = data;
}
}
if((addr & 0xe000) == 0x6000) { //6000-7fff
if(within<0x6000, 0x7fff>(addr)) {
//unknown purpose
}
if((addr & 0xe000) == 0xa000) { //a000-bfff
if(within<0xa000, 0xbfff>(addr)) {
if(ram_enable) cartridge.ram_write((ram_select << 13) + (addr & 0x1fff), data);
}
}

View File

@@ -2,6 +2,7 @@
void Cartridge::serialize(serializer &s) {
if(info.battery) s.array(ramdata, ramsize);
s.integer(bootrom_enable);
s.integer(mbc1.ram_enable);
s.integer(mbc1.rom_select);

View File

@@ -28,14 +28,30 @@ void CPU::main() {
}
void CPU::interrupt_raise(CPU::Interrupt id) {
switch(id) {
case Interrupt::Vblank: status.interrupt_request_vblank = 1; break;
case Interrupt::Stat : status.interrupt_request_stat = 1; break;
case Interrupt::Timer : status.interrupt_request_timer = 1; break;
case Interrupt::Serial: status.interrupt_request_serial = 1; break;
case Interrupt::Joypad: status.interrupt_request_joypad = 1; status.stop = false; break;
if(id == Interrupt::Vblank) {
status.interrupt_request_vblank = 1;
if(status.interrupt_enable_vblank) status.halt = false;
}
if(id == Interrupt::Stat) {
status.interrupt_request_stat = 1;
if(status.interrupt_enable_stat) status.halt = false;
}
if(id == Interrupt::Timer) {
status.interrupt_request_timer = 1;
if(status.interrupt_enable_timer) status.halt = false;
}
if(id == Interrupt::Serial) {
status.interrupt_request_serial = 1;
if(status.interrupt_enable_serial) status.halt = false;
}
if(id == Interrupt::Joypad) {
status.interrupt_request_joypad = 1;
if(status.interrupt_enable_joypad) status.halt = status.stop = false;
}
status.halt = false;
}
void CPU::interrupt_test() {
@@ -78,7 +94,7 @@ void CPU::interrupt_exec(uint16 pc) {
}
void CPU::power() {
create(Main, 4 * 1024 * 1024);
create(Main, 4194304);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
@@ -98,18 +114,19 @@ void CPU::power() {
status.clock = 0;
status.halt = false;
status.stop = false;
status.ime = 0;
status.timer0 = 0;
status.timer1 = 0;
status.timer2 = 0;
status.timer3 = 0;
status.p15 = 0;
status.p14 = 0;
status.joyp = 0;
status.mlt_req = 0;
status.serial_data = 0;
status.serial_bits = 0;
status.serial_transfer = 0;
status.serial_clock = 0;
status.div = 0;
status.tima = 0;

View File

@@ -17,12 +17,7 @@ struct CPU : Processor, MMIO {
unsigned clock;
bool halt;
bool stop;
bool ime;
unsigned timer0;
unsigned timer1;
unsigned timer2;
unsigned timer3;
//$ff00 JOYP
bool p15;
@@ -30,6 +25,14 @@ struct CPU : Processor, MMIO {
uint8 joyp;
uint8 mlt_req;
//$ff01 SB
uint8 serial_data;
unsigned serial_bits;
//$ff02 SC
bool serial_transfer;
bool serial_clock;
//$ff04 DIV
uint8 div;
@@ -72,4 +75,4 @@ struct CPU : Processor, MMIO {
CPU();
};
extern CPU cpu;
extern CPU cpu;

View File

@@ -31,6 +31,15 @@ uint8 CPU::mmio_read(uint16 addr) {
| (status.joyp << 0);
}
if(addr == 0xff01) { //SB
return 0xff;
}
if(addr == 0xff02) { //SC
return (status.serial_transfer << 7)
| (status.serial_clock << 0);
}
if(addr == 0xff04) { //DIV
return status.div;
}
@@ -81,10 +90,14 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
}
if(addr == 0xff01) { //SB
status.serial_data = data;
return;
}
if(addr == 0xff02) { //SC
status.serial_transfer = data & 0x80;
status.serial_clock = data & 0x01;
if(status.serial_transfer) status.serial_bits = 8;
return;
}

View File

@@ -21,18 +21,19 @@ void CPU::serialize(serializer &s) {
s.integer(status.clock);
s.integer(status.halt);
s.integer(status.stop);
s.integer(status.ime);
s.integer(status.timer0);
s.integer(status.timer1);
s.integer(status.timer2);
s.integer(status.timer3);
s.integer(status.p15);
s.integer(status.p14);
s.integer(status.joyp);
s.integer(status.mlt_req);
s.integer(status.serial_data);
s.integer(status.serial_bits);
s.integer(status.serial_transfer);
s.integer(status.serial_clock);
s.integer(status.div);
s.integer(status.tima);
s.integer(status.tma);

View File

@@ -1,13 +1,9 @@
//4194304hz (4 * 1024 * 1024)
//70224 clocks/frame
// 456 clocks/scanline
// 154 scanlines/frame
//4194304 / 4096 = 1024
//4194304 / 262144 = 16
//4194304 / 65536 = 64
//4394304 / 16384 = 256
#ifdef CPU_CPP
#include "opcode.cpp"
@@ -17,43 +13,44 @@ void CPU::add_clocks(unsigned clocks) {
scheduler.exit(Scheduler::ExitReason::StepEvent);
status.clock += clocks;
if(status.clock >= 4 * 1024 * 1024) {
status.clock -= 4 * 1024 * 1024;
if(status.clock >= 4194304) {
status.clock -= 4194304;
cartridge.mbc3.second();
}
status.timer0 += clocks;
if(status.timer0 >= 16) timer_stage0();
//4194304 / N(hz) - 1 = mask
if((status.clock & 15) == 0) timer_262144hz();
if((status.clock & 63) == 0) timer_65536hz();
if((status.clock & 255) == 0) timer_16384hz();
if((status.clock & 511) == 0) timer_8192hz();
if((status.clock & 1023) == 0) timer_4096hz();
cpu.clock += clocks;
if(cpu.clock >= 0) co_switch(scheduler.active_thread = lcd.thread);
lcd.clock -= clocks;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
apu.clock -= clocks;
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
}
void CPU::timer_stage0() { //262144hz
void CPU::timer_262144hz() {
if(status.timer_enable && status.timer_clock == 1) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer0 -= 16;
if(++status.timer1 >= 4) timer_stage1();
}
void CPU::timer_stage1() { // 65536hz
void CPU::timer_65536hz() {
if(status.timer_enable && status.timer_clock == 2) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer1 -= 4;
if(++status.timer2 >= 4) timer_stage2();
}
void CPU::timer_stage2() { // 16384hz
void CPU::timer_16384hz() {
if(status.timer_enable && status.timer_clock == 3) {
if(++status.tima == 0) {
status.tima = status.tma;
@@ -62,20 +59,24 @@ void CPU::timer_stage2() { // 16384hz
}
status.div++;
status.timer2 -= 4;
if(++status.timer3 >= 4) timer_stage3();
}
void CPU::timer_stage3() { // 4096hz
void CPU::timer_8192hz() {
if(status.serial_transfer && status.serial_clock) {
if(--status.serial_bits == 0) {
status.serial_transfer = 0;
interrupt_raise(Interrupt::Serial);
}
}
}
void CPU::timer_4096hz() {
if(status.timer_enable && status.timer_clock == 0) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer3 -= 4;
}
#endif

View File

@@ -1,8 +1,9 @@
void add_clocks(unsigned clocks);
void timer_stage0();
void timer_stage1();
void timer_stage2();
void timer_stage3();
void timer_262144hz();
void timer_65536hz();
void timer_16384hz();
void timer_8192hz();
void timer_4096hz();
//opcode.cpp
void op_io();

View File

@@ -5,7 +5,7 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000.11";
static const char Version[] = "000.19";
static unsigned SerializerVersion = 1;
}
}
@@ -15,22 +15,62 @@ namespace GameBoy {
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/varint.hpp>
using namespace nall;
namespace GameBoy {
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef uint_t< 1> uint1;
typedef uint_t< 2> uint2;
typedef uint_t< 3> uint3;
typedef uint_t< 4> uint4;
typedef uint_t< 5> uint5;
typedef uint_t< 6> uint6;
typedef uint_t< 7> uint7;
typedef uint_t< 9> uint9;
typedef uint_t<10> uint10;
typedef uint_t<11> uint11;
typedef uint_t<12> uint12;
typedef uint_t<13> uint13;
typedef uint_t<14> uint14;
typedef uint_t<15> uint15;
typedef uint_t<17> uint17;
typedef uint_t<18> uint18;
typedef uint_t<19> uint19;
typedef uint_t<20> uint20;
typedef uint_t<21> uint21;
typedef uint_t<22> uint22;
typedef uint_t<23> uint23;
typedef uint_t<24> uint24;
typedef uint_t<25> uint25;
typedef uint_t<26> uint26;
typedef uint_t<27> uint27;
typedef uint_t<28> uint28;
typedef uint_t<29> uint29;
typedef uint_t<30> uint30;
typedef uint_t<31> uint31;
template<uint16 lo, uint16 hi>
alwaysinline bool within(uint16 addr) {
static const uint16 mask = ~(hi ^ lo);
return (addr & mask) == lo;
}
struct Processor {
cothread_t thread;
unsigned frequency;
@@ -51,5 +91,6 @@ namespace GameBoy {
#include <gameboy/scheduler/scheduler.hpp>
#include <gameboy/cartridge/cartridge.hpp>
#include <gameboy/cpu/cpu.hpp>
#include <gameboy/apu/apu.hpp>
#include <gameboy/lcd/lcd.hpp>
};

View File

@@ -1,9 +1,10 @@
class Interface {
public:
virtual void lcd_scanline() {}
virtual void joyp_write(bool p15, bool p14) {}
virtual void video_refresh(const uint8_t *data) {}
virtual void audio_sample(signed left, signed right) {}
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
virtual void input_poll() {}
virtual bool input_poll(unsigned id) {}

View File

@@ -19,7 +19,11 @@ void LCD::main() {
add_clocks(4);
if(status.lx == 320) {
if(status.lx == 0) {
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.lx == 252) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
@@ -29,8 +33,8 @@ void LCD::add_clocks(unsigned clocks) {
status.lx += clocks;
if(status.lx >= 456) scanline();
cpu.clock -= clocks;
if(cpu.clock <= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}
}
@@ -71,6 +75,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) {
@@ -186,7 +191,7 @@ void LCD::render_obj() {
}
void LCD::power() {
create(Main, 4 * 1024 * 1024);
create(Main, 4194304);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO

View File

@@ -17,9 +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 >= 320) mode = 0; //Hblank
else mode = 3; //LCD transfer
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
return (status.interrupt_lyc << 6)
| (status.interrupt_oam << 5)

View File

@@ -50,7 +50,7 @@ void Bus::write(uint16 addr, uint8 data) {
}
void Bus::power() {
for(unsigned n = 0; n < 65536; n++) mmio[n] = &unmapped;
for(unsigned n = 0x0000; n <= 0xffff; n++) mmio[n] = &unmapped;
}
}

View File

@@ -41,6 +41,7 @@ void System::serialize_all(serializer &s) {
cartridge.serialize(s);
system.serialize(s);
cpu.serialize(s);
apu.serialize(s);
lcd.serialize(s);
}

View File

@@ -33,35 +33,23 @@ void System::runthreadtosave() {
}
}
uint8 System::mmio_read(uint16 addr) {
if((addr & 0xff00) == 0x0000) {
return BootROM::sgb[addr];
}
return 0x00;
}
void System::mmio_write(uint16 addr, uint8 data) {
if(addr == 0xff50) {
if(data == 0x01) cartridge.map();
}
}
void System::init(Interface *interface_) {
interface = interface_;
}
void System::load() {
serialize_init();
}
void System::power() {
bus.power();
cartridge.power();
cpu.power();
apu.power();
lcd.power();
scheduler.init();
for(unsigned n = 0x0000; n <= 0x00ff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = this;
clocks_executed = 0;
serialize_init();
}
}

View File

@@ -4,7 +4,7 @@ enum class Input : unsigned {
Up, Down, Left, Right, B, A, Select, Start,
};
struct System : MMIO {
struct System {
struct BootROM {
static const uint8 dmg[256];
static const uint8 sgb[256];
@@ -14,10 +14,8 @@ struct System : MMIO {
void runtosave();
void runthreadtosave();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void init(Interface*);
void load();
void power();
Interface *interface;

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="bsnes" version="1.0.0.0" processorArchitecture="x86"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
</assembly>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,4 +0,0 @@
@windres resource.rc resource.o
@mingw32-g++ -std=gnu++0x -mwindows -s -O3 -fomit-frame-pointer -I.. -o ../out/bsnes launcher.cpp resource.o
@del *.o
@pause

View File

@@ -1,2 +0,0 @@
clear
g++ -std=gnu++0x -s -O3 -fomit-frame-pointer -I.. -o ../out/bsnes launcher.cpp

View File

@@ -1,54 +0,0 @@
#include <nall/config.hpp>
#include <nall/detect.hpp>
#include <nall/file.hpp>
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
using namespace nall;
int main(int argc, char **argv) {
char path[PATH_MAX], *unused;
#if !defined(PLATFORM_WIN)
unused = realpath(argv[0], path);
#else
wchar_t **argw = CommandLineToArgvW(GetCommandLineW(), &argc);
unused = realpath(nall::utf8_t(argw[0]), path);
#endif
string realPath = dir(path);
string basePath = string(dir(path), "bsnes-qt.cfg");
unused = userpath(path);
if(!strend(path, "/") && !strend(path, "\\")) strcat(path, "/");
string userPath = string(path, ".bsnes/bsnes-qt.cfg");
configuration config;
string profile;
config.attach(profile = "", "system.profile");
if(config.load(userPath) == false) config.load(basePath);
if(profile == "") profile = "compatibility";
string binaryName = string("bsnes-", profile);
#if defined(PLATFORM_WIN)
binaryName << ".dll";
#endif
string fileName = string(realPath, binaryName);
#if !defined(PLATFORM_WIN)
char **args = new char*[argc + 1];
args[0] = strdup(binaryName);
for(unsigned i = 1; i < argc; i++) args[i] = strdup(argv[i]);
args[argc] = 0;
execvp(args[0], args);
execv(fileName, args);
print("[bsnes] Error: unable to locate binary file: ", binaryName, "\n");
#else
STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(STARTUPINFOW));
if(!CreateProcessW(nall::utf16_t(fileName), GetCommandLineW(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
MessageBoxA(0, string("Error: unable to locate binary file: ", binaryName), "bsnes", MB_OK);
}
#endif
return 0;
}

View File

@@ -1,2 +0,0 @@
1 24 "bsnes.Manifest"
IDI_ICON1 ICON DISCARDABLE "bsnes.ico"

View File

@@ -41,21 +41,22 @@ struct directory {
if(handle != INVALID_HANDLE_VALUE) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(string(name, "/"));
string name = (const char*)utf8_t(data.cFileName);
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 = utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(string(name, "/"));
string name = (const char*)utf8_t(data.cFileName);
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;
}
@@ -70,12 +71,12 @@ struct directory {
handle = FindFirstFile(utf16_t(path), &data);
if(handle != INVALID_HANDLE_VALUE) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
string name = utf8_t(data.cFileName);
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
while(FindNextFile(handle, &data) != false) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
string name = utf8_t(data.cFileName);
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
}
@@ -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

@@ -1,15 +1,7 @@
#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>
@@ -28,6 +20,7 @@ namespace nall {
public:
enum class mode : unsigned { read, write, readwrite, writeread };
enum class index : unsigned { absolute, relative };
enum class time : unsigned { create, modify, access };
uint8_t read() {
if(!fp) return 0xff; //file not open
@@ -142,52 +135,60 @@ namespace nall {
return file_offset >= file_size;
}
static bool exists(const char *fn) {
static bool exists(const char *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 char *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 char *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 char *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

@@ -21,7 +21,7 @@ namespace nall {
public:
enum class mode : unsigned { read, write, readwrite, writeread };
bool opened() const { return p_opened(); }
bool open() const { return p_open(); }
bool open(const char *filename, mode mode_) { return p_open(filename, mode_); }
void close() { return p_close(); }
unsigned size() const { return p_size; }
@@ -42,7 +42,7 @@ namespace nall {
HANDLE p_filehandle, p_maphandle;
bool p_opened() const {
bool p_open() const {
return p_handle;
}
@@ -128,7 +128,7 @@ namespace nall {
int p_fd;
bool p_opened() const {
bool p_open() const {
return p_handle;
}

View File

@@ -5,8 +5,14 @@
#include <nall/concept.hpp>
#undef foreach
#define foreach(iter, object) \
#define foreach2(iter, object) foreach3(iter, object, foreach_counter)
#define foreach3(iter, object, foreach_counter) \
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
#define foreach_impl(...) foreach_decl(__VA_ARGS__, foreach3(__VA_ARGS__), foreach2(__VA_ARGS__), foreach_too_few_arguments)
#define foreach_decl(_1, _2, _3, N, ...) N
#define foreach(...) foreach_impl(__VA_ARGS__)
#endif

View File

@@ -27,7 +27,7 @@ namespace nall {
};
template<typename L> struct lambda : container {
L object;
mutable L object;
R operator()(P... p) const { return object(std::forward<P>(p)...); }
container* copy() const { return new lambda(object); }
lambda(const L& object) : object(object) {}
@@ -46,7 +46,7 @@ namespace nall {
return *this;
}
function(const function &source) { operator=(source); }
function(const function &source) : callback(0) { operator=(source); }
function() : callback(0) {}
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
function(R (*function)(P...)) { callback = new global(function); }

View File

@@ -22,7 +22,7 @@ public:
};
GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
xml = "<?xml version='1.0' encoding='UTF-8'?>\n";
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
if(romsize < 0x4000) return;
info.mapper = "unknown";
@@ -97,6 +97,7 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
xml << " <ram size='" << hex(info.ramsize) << "' battery='" << info.battery << "'/>\n";
xml << "</cartridge>\n";
xml.transform("'", "\"");
}
}

View File

@@ -1,6 +1,12 @@
#ifndef NALL_PLATFORM_HPP
#define NALL_PLATFORM_HPP
#if defined(_WIN32)
//minimum version needed for _wstat64, etc
#undef __MSVCRT_VERSION__
#define __MSVCRT_VERSION__ 0x0601
#endif
#include <nall/utf8.hpp>
//=========================
@@ -18,16 +24,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 +62,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
//================

32
bsnes/nall/public_cast.hpp Executable file
View File

@@ -0,0 +1,32 @@
#ifndef NALL_PUBLIC_CAST_HPP
#define NALL_PUBLIC_CAST_HPP
//this is a proof-of-concept-*only* C++ access-privilege elevation exploit.
//this code is 100% legal C++, per C++98 section 14.7.2 paragraph 8:
//"access checking rules do not apply to names in explicit instantiations."
//usage example:
//struct N { typedef void (Class::*)(); };
//template class public_cast<N, &Class::Reference>;
//(class.*public_cast<N>::value);
//Class::Reference may be public, protected or private
//Class::Reference may be a function, object or variable
namespace nall {
template<typename T, typename T::type... P> struct public_cast;
template<typename T> struct public_cast<T> {
static typename T::type value;
};
template<typename T> typename T::type public_cast<T>::value;
template<typename T, typename T::type P> struct public_cast<T, P> {
static typename T::type value;
};
template<typename T, typename T::type P> typename T::type public_cast<T, P>::value = public_cast<T>::value = P;
}
#endif

View File

@@ -8,12 +8,20 @@ namespace nall {
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
}
struct random_cyclic {
unsigned seed;
inline unsigned operator()() {
return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320);
struct random_lfsr {
inline void seed(unsigned seed__) {
seed_ = seed__;
}
random_cyclic() : seed(0) {}
inline unsigned operator()() {
return seed_ = (seed_ >> 1) ^ (((seed_ & 1) - 1) & 0xedb88320);
}
random_lfsr() : seed_(0) {
}
private:
unsigned seed_;
};
}

103
bsnes/nall/reference_array.hpp Executable file
View File

@@ -0,0 +1,103 @@
#ifndef NALL_REFERENCE_ARRAY_HPP
#define NALL_REFERENCE_ARRAY_HPP
#include <type_traits>
#include <nall/bit.hpp>
#include <nall/concept.hpp>
namespace nall {
template<typename T> struct reference_array {
protected:
typedef typename std::remove_reference<T>::type *Tptr;
Tptr *pool;
unsigned poolsize, buffersize;
public:
unsigned size() const { return buffersize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = 0;
poolsize = 0;
buffersize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (Tptr*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
buffersize = min(buffersize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize));
buffersize = newsize;
}
void append(const T data) {
unsigned index = buffersize++;
if(index >= poolsize) resize(index + 1);
pool[index] = &data;
}
template<typename... Args> reference_array(Args&... args) : pool(0), poolsize(0), buffersize(0) {
construct(args...);
}
~reference_array() {
reset();
}
reference_array& operator=(const reference_array &source) {
if(pool) free(pool);
buffersize = source.buffersize;
poolsize = source.poolsize;
pool = (Tptr*)malloc(sizeof(T) * poolsize);
memcpy(pool, source.pool, sizeof(T) * buffersize);
return *this;
}
reference_array& operator=(const reference_array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
buffersize = source.buffersize;
source.pool = 0;
source.reset();
return *this;
}
inline T operator[](unsigned index) {
if(index >= buffersize) throw "reference_array[] out of bounds";
return *pool[index];
}
inline const T operator[](unsigned index) const {
if(index >= buffersize) throw "reference_array[] out of bounds";
return *pool[index];
}
private:
void construct() {
}
void construct(const reference_array &source) {
operator=(source);
}
void construct(const reference_array &&source) {
operator=(std::move(source));
}
template<typename... Args> void construct(T data, Args&... args) {
append(data);
construct(args...);
}
};
template<typename T> struct has_size<reference_array<T>> { enum { value = true }; };
}
#endif

View File

@@ -112,13 +112,13 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
if(type == TypeBsx) {
xml << "<cartridge/>";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
return;
}
if(type == TypeSufamiTurbo) {
xml << "<cartridge/>";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
return;
}
@@ -128,7 +128,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <ram size='" << hex(gameboy_ram_size(data, size)) << "'/>\n";
}
xml << "</cartridge>\n";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
return;
}
@@ -279,19 +279,23 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " </superfx>\n";
} else if(mapper == SA1ROM) {
xml << " <sa1>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\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='00-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='40-4f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:6000-7fff'/>\n";
xml << " </bwram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:2200-23ff'/>\n";
@@ -334,11 +338,14 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " </slot>\n";
xml << " </bsx>\n";
} else if(mapper == BSXROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " </rom>\n";
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";
@@ -355,7 +362,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
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>\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";
@@ -365,7 +372,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
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>\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";
@@ -402,48 +409,72 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
if(has_dsp1) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
if(dsp1_mapper == DSP1LoROM1MB) {
xml << " <dr mask='004000' test='000000'/>\n";
xml << " <sr mask='004000' test='004000'/>\n";
xml << " <map address='20-3f:8000-ffff'/>\n";
xml << " <map address='a0-bf:8000-ffff'/>\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";
} else if(dsp1_mapper == DSP1LoROM2MB) {
xml << " <dr mask='004000' test='000000'/>\n";
xml << " <sr mask='004000' test='004000'/>\n";
xml << " <map address='60-6f:0000-7fff'/>\n";
xml << " <map address='e0-ef:0000-7fff'/>\n";
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";
} else if(dsp1_mapper == DSP1HiROM) {
xml << " <dr mask='001000' test='000000'/>\n";
xml << " <sr mask='001000' test='001000'/>\n";
xml << " <map address='00-1f:6000-7fff'/>\n";
xml << " <map address='80-9f:6000-7fff'/>\n";
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 << " </necdsp>\n";
}
if(has_dsp2) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
xml << " <dr mask='004000' test='000000'/>\n";
xml << " <sr mask='004000' test='004000'/>\n";
xml << " <map address='20-3f:8000-ffff'/>\n";
xml << " <map address='a0-bf:8000-ffff'/>\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";
}
if(has_dsp3) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
xml << " <dr mask='004000' test='000000'/>\n";
xml << " <sr mask='004000' test='004000'/>\n";
xml << " <map address='20-3f:8000-ffff'/>\n";
xml << " <map address='a0-bf:8000-ffff'/>\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";
}
if(has_dsp4) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
xml << " <dr mask='004000' test='000000'/>\n";
xml << " <sr mask='004000' test='004000'/>\n";
xml << " <map address='30-3f:8000-ffff'/>\n";
xml << " <map address='b0-bf:8000-ffff'/>\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";
}
@@ -456,21 +487,35 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
if(has_st010) {
xml << " <necdsp revision='upd96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
xml << " <dr mask='080001' test='000000'/>\n";
xml << " <sr mask='080001' test='000001'/>\n";
xml << " <dp mask='080000' test='080000'/>\n";
xml << " <map address='60-6f:0000-0fff'/>\n";
xml << " <map address='e0-ef:0000-0fff'/>\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";
}
if(has_st011) {
xml << " <necdsp revision='upd96050' frequency='15000000' program='st0011.bin' sha256='b71d71fa5f18ff39b2b3688e914a04d1286b613002ddde827529fdc54650a130'>\n";
xml << " <dr mask='080001' test='000000'/>\n";
xml << " <sr mask='080001' test='000001'/>\n";
xml << " <dp mask='080000' test='080000'/>\n";
xml << " <map address='60-6f:0000-0fff'/>\n";
xml << " <map address='e0-ef:0000-0fff'/>\n";
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";
}
@@ -482,7 +527,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
}
xml << "</cartridge>\n";
xmlMemoryMap = xml;
xmlMemoryMap = xml.transform("'", "\"");
}
void SNESCartridge::read_header(const uint8_t *data, unsigned size) {

View File

@@ -12,20 +12,18 @@
namespace nall {
class string;
template<typename T> inline string to_string(T);
template<typename T> inline const char* to_string(T);
class string {
public:
inline void reserve(unsigned);
inline string& assign(const char*);
inline string& append(const char*);
inline string& append(bool);
inline string& append(signed int value);
inline string& append(unsigned int value);
inline string& append(double value);
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 char*);
inline bool readfile(const string&);
inline string& replace (const char*, const char*);
inline string& qreplace(const char*, const char*);
@@ -145,10 +143,15 @@ namespace nall {
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
inline unsigned strlcat(string &dest, const char *src, unsigned length);
inline string substr(const char *src, unsigned start = 0, unsigned length = 0);
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string integer(intmax_t value);
template<unsigned length = 0, char padding = '0'> inline string decimal(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
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);
inline unsigned fp(char *str, double value);
inline string fp(double value);

View File

@@ -4,14 +4,16 @@
namespace nall {
//this is needed, as C++0x does not support explicit template specialization inside classes
template<> inline string to_string<bool> (bool v) { return v ? "true" : "false"; }
template<> inline string to_string<signed int> (signed int v) { return integer(v); }
template<> inline string to_string<unsigned int> (unsigned int v) { return decimal(v); }
template<> inline string to_string<double> (double v) { return fp(v); }
template<> inline string to_string<char*> (char *v) { return v; }
template<> inline string to_string<const char*> (const char *v) { return v; }
template<> inline string to_string<string> (string v) { return v; }
template<> inline string to_string<const string&>(const string &v) { return v; }
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<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)); }
@@ -22,8 +24,8 @@ template<typename T> lstring& lstring::operator<<(T value) {
}
#if defined(QSTRING_H)
template<> inline string to_string<QString>(QString v) { return v.toUtf8().constData(); }
template<> inline string to_string<const QString&>(const QString &v) { return v.toUtf8().constData(); }
template<> inline const char* to_string<QString>(QString v) { return v.toUtf8().constData(); }
template<> inline const char* to_string<const QString&>(const QString &v) { return v.toUtf8().constData(); }
string::operator QString() const { return QString::fromUtf8(*this); }
#endif

View File

@@ -65,8 +65,11 @@ intmax_t integer(const char *str) {
intmax_t result = 0;
bool negate = false;
//check for negation
if(*str == '-') {
//check for sign
if(*str == '+') {
negate = false;
str++;
} else if(*str == '-') {
negate = true;
str++;
}
@@ -114,38 +117,7 @@ uintmax_t binary(const char *str) {
}
double fp(const char *str) {
if(!str) return 0.0;
bool negate = false;
//check for negation
if(*str == '-') {
negate = true;
str++;
}
intmax_t result_integral = 0;
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else if(x == '.' || x == ',') break; //break loop and read fractional part
else return (double)result_integral; //invalid value, assume no fractional part
result_integral = result_integral * 10 + x;
}
intmax_t result_fractional = 0;
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else break; //stop at first invalid character
result_fractional = result_fractional * 10 + x;
}
//calculate fractional portion
double result = (double)result_fractional;
while((uintmax_t)result > 0) result /= 10.0;
result += (double)result_integral;
return !negate ? result : -result;
return atof(str);
}
}

View File

@@ -3,6 +3,15 @@
namespace nall {
static void istring(string &output) {
}
template<typename T, typename... Args>
static void istring(string &output, const T &value, Args&&... args) {
output.append_(to_string(value));
istring(output, std::forward<Args>(args)...);
}
void string::reserve(unsigned size_) {
if(size_ > size) {
size = size_;
@@ -11,25 +20,31 @@ void string::reserve(unsigned size_) {
}
}
string& string::assign(const char *s) {
template<typename... Args> string& string::assign(Args&&... args) {
*data = 0;
istring(*this, std::forward<Args>(args)...);
return *this;
}
template<typename... Args> string& string::append(Args&&... args) {
istring(*this, std::forward<Args>(args)...);
return *this;
}
string& string::assign_(const char *s) {
unsigned length = strlen(s);
reserve(length);
strcpy(data, s);
return *this;
}
string& string::append(const char *s) {
string& string::append_(const char *s) {
unsigned length = strlen(data) + strlen(s);
reserve(length);
strcat(data, s);
return *this;
}
string& string::append(bool value) { append(value ? "true" : "false"); return *this; }
string& string::append(signed int value) { append(integer(value)); return *this; }
string& string::append(unsigned int value) { append(decimal(value)); return *this; }
string& string::append(double value) { append(fp(value)); return *this; }
string::operator const char*() const {
return data;
}
@@ -64,15 +79,6 @@ string& string::operator=(string &&source) {
return *this;
}
static void istring(string &output) {
}
template<typename T, typename... Args>
static void istring(string &output, const T &value, Args&&... args) {
output.append(value);
istring(output, std::forward<Args>(args)...);
}
template<typename... Args> string::string(Args&&... args) {
size = 64;
data = (char*)malloc(size + 1);
@@ -95,7 +101,7 @@ string::~string() {
if(data) free(data);
}
bool string::readfile(const char *filename) {
bool string::readfile(const string &filename) {
assign("");
#if !defined(_WIN32)

View File

@@ -3,15 +3,15 @@
namespace nall {
string realpath(const char *name) {
string currentpath() {
char path[PATH_MAX];
if(::realpath(name, path)) {
if(::getcwd(path)) {
string result(path);
result.transform("\\", "/");
if(result.endswith("/") == false) result.append("/");
return result;
}
return "";
return "./";
}
string userpath() {
@@ -22,18 +22,17 @@ string userpath() {
if(result.endswith("/") == false) result.append("/");
return result;
}
return "";
return currentpath();
}
string currentpath() {
string realpath(const char *name) {
char path[PATH_MAX];
if(::getcwd(path)) {
if(::realpath(name, path)) {
string result(path);
result.transform("\\", "/");
if(result.endswith("/") == false) result.append("/");
return result;
}
return "";
return userpath();
}
}

View File

@@ -27,98 +27,196 @@ string substr(const char *src, unsigned start, unsigned length) {
/* arithmetic <> string */
template<unsigned length, char padding> string hex(uintmax_t value) {
string output;
unsigned offset = 0;
//render string backwards, as we do not know its length yet
do {
unsigned n = value & 15;
output[offset++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
//reverse the string in-place
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
template<unsigned length, char padding> string integer(intmax_t value) {
string output;
unsigned offset = 0;
string integer(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
do {
unsigned n = value % 10;
output[offset++] = '0' + n;
value /= 10;
} while(value);
while(offset < length) output[offset++] = padding;
if(negative) output[offset++] = '-';
output[offset--] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
template<unsigned length, char padding> string decimal(uintmax_t value) {
string output;
unsigned offset = 0;
char buffer[64];
unsigned size = 0;
do {
unsigned n = value % 10;
output[offset++] = '0' + n;
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
char result[size + 1];
memset(result, '0', size);
result[size] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return output;
return (const char*)result;
}
template<unsigned length, char padding> string binary(uintmax_t value) {
string output;
unsigned offset = 0;
template<unsigned length_> string linteger(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);
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);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
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) {
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, ' ', 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 rdecimal(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, ' ', length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string hex(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value & 15;
buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_> string binary(uintmax_t value) {
char buffer[256];
unsigned size = 0;
do {
unsigned n = value & 1;
output[offset++] = '0' + n;
buffer[size++] = '0' + n;
value >>= 1;
} while(value);
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
result[length] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return output;
return (const char*)result;
}
//using sprintf is certainly not the most ideal method to convert

View File

@@ -46,7 +46,7 @@ namespace nall {
void reserve(unsigned newsize) {
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
T *poolcopy = (T*)malloc(newsize * sizeof(T));
T *poolcopy = (T*)calloc(newsize, sizeof(T));
for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]);
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
free(pool);

181
bsnes/phoenix/core/core.cpp Executable file
View File

@@ -0,0 +1,181 @@
#include "state.hpp"
#include "layout/fixed-layout.cpp"
#include "layout/horizontal-layout.cpp"
#include "layout/vertical-layout.cpp"
#if defined(PHOENIX_WINDOWS)
#include "../windows/windows.cpp"
#elif defined(PHOENIX_QT)
#include "../qt/qt.cpp"
#elif defined(PHOENIX_GTK)
#include "../gtk/gtk.cpp"
#elif defined(PHOENIX_REFERENCE)
#include "../reference/reference.cpp"
#endif
Object::Object() { OS::initialize(); }
Geometry OS::availableGeometry() { return pOS::availableGeometry(); }
Geometry OS::desktopGeometry() { return pOS::desktopGeometry(); }
string OS::fileLoad_(Window &parent, const string &path, const lstring &filter_) { auto filter = filter_; if(filter.size() == 0) filter.append("All files (*)"); return pOS::fileLoad(parent, path, filter); }
string OS::fileSave_(Window &parent, const string &path, const lstring &filter_) { auto filter = filter_; if(filter.size() == 0) filter.append("All files (*)"); return pOS::fileSave(parent, path, filter); }
string OS::folderSelect(Window &parent, const string &path) { return pOS::folderSelect(parent, path); }
void OS::main() { return pOS::main(); }
bool OS::pendingEvents() { return pOS::pendingEvents(); }
void OS::processEvents() { return pOS::processEvents(); }
void OS::quit() { return pOS::quit(); }
void OS::initialize() { static bool initialized = false; if(initialized == false) { initialized = true; return pOS::initialize(); } }
Geometry Font::geometry(const string &text) { return p.geometry(text); }
void Font::setBold(bool bold) { state.bold = bold; return p.setBold(bold); }
void Font::setFamily(const string &family) { state.family = family; return p.setFamily(family); }
void Font::setItalic(bool italic) { state.italic = italic; return p.setItalic(italic); }
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); }
MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::critical(parent, text, buttons); }
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); }
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::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); }
void Window::setGeometry(const Geometry &geometry) { state.geometry = geometry; return p.setGeometry(geometry); }
void Window::setMenuFont(Font &font) { state.menuFont = &font; return p.setMenuFont(font); }
void Window::setMenuVisible(bool visible) { state.menuVisible = visible; return p.setMenuVisible(visible); }
void Window::setResizable(bool resizable) { state.resizable = resizable; return p.setResizable(resizable); }
void Window::setStatusFont(Font &font) { state.statusFont = &font; return p.setStatusFont(font); }
void Window::setStatusText(const string &text) { state.statusText = text; return p.setStatusText(text); }
void Window::setStatusVisible(bool visible) { state.statusVisible = visible; return p.setStatusVisible(visible); }
void Window::setTitle(const string &text) { state.title = text; return p.setTitle(text); }
void Window::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
void Window::setWidgetFont(Font &font) { state.widgetFont = &font; return p.setWidgetFont(font); }
Window::Window() : state(*new State), p(*new pWindow(*this)) { p.constructor(); }
void Action::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Action::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
Action::Action(pAction &p) : state(*new State), p(p) { p.constructor(); }
void Menu::append(Action &action) { state.action.append(action); return p.append(action); }
void Menu::setText(const string &text) { state.text = text; return p.setText(text); }
Menu::Menu() : state(*new State), base_from_member<pMenu&>(*new pMenu(*this)), Action(base_from_member<pMenu&>::value), p(base_from_member<pMenu&>::value) { p.constructor(); }
Separator::Separator() : base_from_member<pSeparator&>(*new pSeparator(*this)), Action(base_from_member<pSeparator&>::value), p(base_from_member<pSeparator&>::value) { p.constructor(); }
void Item::setText(const string &text) { state.text = text; return p.setText(text); }
Item::Item() : state(*new State), base_from_member<pItem&>(*new pItem(*this)), Action(base_from_member<pItem&>::value), p(base_from_member<pItem&>::value) { p.constructor(); }
bool CheckItem::checked() { return p.checked(); }
void CheckItem::setChecked(bool checked) { state.checked = checked; return p.setChecked(checked); }
void CheckItem::setText(const string &text) { state.text = text; return p.setText(text); }
CheckItem::CheckItem() : state(*new State), base_from_member<pCheckItem&>(*new pCheckItem(*this)), Action(base_from_member<pCheckItem&>::value), p(base_from_member<pCheckItem&>::value) { p.constructor(); }
void RadioItem::group(const reference_array<RadioItem&> &list) { foreach(item, list) item.p.setGroup(item.state.group = list); if(list.size()) list[0].setChecked(); }
bool RadioItem::checked() { return p.checked(); }
void RadioItem::setChecked() { foreach(item, state.group) item.state.checked = false; state.checked = true; return p.setChecked(); }
void RadioItem::setText(const string &text) { state.text = text; return p.setText(text); }
RadioItem::RadioItem() : state(*new State), base_from_member<pRadioItem&>(*new pRadioItem(*this)), Action(base_from_member<pRadioItem&>::value), p(base_from_member<pRadioItem&>::value) { p.constructor(); }
bool Widget::enabled() { return state.enabled; }
Font& Widget::font() { return p.font(); }
Geometry Widget::minimumGeometry() { return p.minimumGeometry(); }
void Widget::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Widget::setFocused() { return p.setFocused(); }
void Widget::setFont(Font &font) { state.font = &font; return p.setFont(font); }
void Widget::setGeometry(const Geometry &geometry) { state.geometry = geometry; return p.setGeometry(geometry); }
void Widget::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
bool Widget::visible() { return state.visible; }
Widget::Widget() : state(*new State), p(*new pWidget(*this)) { state.abstract = true; p.constructor(); }
Widget::Widget(pWidget &p) : state(*new State), p(p) { p.constructor(); }
void Button::setText(const string &text) { state.text = text; return p.setText(text); }
Button::Button() : state(*new State), base_from_member<pButton&>(*new pButton(*this)), Widget(base_from_member<pButton&>::value), p(base_from_member<pButton&>::value) { p.constructor(); }
uint32_t* Canvas::buffer() { return p.buffer(); }
void Canvas::update() { return p.update(); }
Canvas::Canvas() : base_from_member<pCanvas&>(*new pCanvas(*this)), Widget(base_from_member<pCanvas&>::value), p(base_from_member<pCanvas&>::value) { p.constructor(); }
bool CheckBox::checked() { return p.checked(); }
void CheckBox::setChecked(bool checked) { state.checked = checked; return p.setChecked(checked); }
void CheckBox::setText(const string &text) { state.text = text; return p.setText(text); }
CheckBox::CheckBox() : state(*new State), base_from_member<pCheckBox&>(*new pCheckBox(*this)), Widget(base_from_member<pCheckBox&>::value), p(base_from_member<pCheckBox&>::value) { p.constructor(); }
void ComboBox::append(const string &text) { state.text.append(text); return p.append(text); }
void ComboBox::reset() { state.selection = 0; state.text.reset(); return p.reset(); }
unsigned ComboBox::selection() { return p.selection(); }
void ComboBox::setSelection(unsigned row) { state.selection = row; return p.setSelection(row); }
ComboBox::ComboBox() : state(*new State), base_from_member<pComboBox&>(*new pComboBox(*this)), Widget(base_from_member<pComboBox&>::value), p(base_from_member<pComboBox&>::value) { p.constructor(); }
void HexEdit::setColumns(unsigned columns) { state.columns = columns; return p.setColumns(columns); }
void HexEdit::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HexEdit::setOffset(unsigned offset) { state.offset = offset; return p.setOffset(offset); }
void HexEdit::setRows(unsigned rows) { state.rows = rows; return p.setRows(rows); }
void HexEdit::update() { return p.update(); }
HexEdit::HexEdit() : state(*new State), base_from_member<pHexEdit&>(*new pHexEdit(*this)), Widget(base_from_member<pHexEdit&>::value), p(base_from_member<pHexEdit&>::value) { p.constructor(); }
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); }
HorizontalSlider::HorizontalSlider() : state(*new State), base_from_member<pHorizontalSlider&>(*new pHorizontalSlider(*this)), Widget(base_from_member<pHorizontalSlider&>::value), p(base_from_member<pHorizontalSlider&>::value) { p.constructor(); }
void Label::setText(const string &text) { state.text = text; return p.setText(text); }
Label::Label() : state(*new State), base_from_member<pLabel&>(*new pLabel(*this)), Widget(base_from_member<pLabel&>::value), p(base_from_member<pLabel&>::value) { p.constructor(); }
void LineEdit::setEditable(bool editable) { state.editable = editable; return p.setEditable(editable); }
void LineEdit::setText(const string &text) { state.text = text; return p.setText(text); }
string LineEdit::text() { return p.text(); }
LineEdit::LineEdit() : state(*new State), base_from_member<pLineEdit&>(*new pLineEdit(*this)), Widget(base_from_member<pLineEdit&>::value), p(base_from_member<pLineEdit&>::value) { p.constructor(); }
void ListView::append_(const lstring &text) { state.checked.append(false); state.text.append(text); return p.append(text); }
void ListView::autoSizeColumns() { return p.autoSizeColumns(); }
bool ListView::checked(unsigned row) { return p.checked(row); }
void ListView::modify_(unsigned row, const lstring &text) { state.text[row] = text; return p.modify(row, text); }
void ListView::reset() { state.checked.reset(); state.text.reset(); return p.reset(); }
bool ListView::selected() { return p.selected(); }
unsigned ListView::selection() { return p.selection(); }
void ListView::setCheckable(bool checkable) { state.checkable = checkable; return p.setCheckable(checkable); }
void ListView::setChecked(unsigned row, bool checked) { state.checked[row] = checked; return p.setChecked(row, checked); }
void ListView::setHeaderText_(const lstring &text) { state.headerText = text; return p.setHeaderText(text); }
void ListView::setHeaderVisible(bool visible) { state.headerVisible = visible; return p.setHeaderVisible(visible); }
void ListView::setSelected(bool selected) { state.selected = selected; return p.setSelected(selected); }
void ListView::setSelection(unsigned row) { state.selected = true; state.selection = row; return p.setSelection(row); }
ListView::ListView() : state(*new State), base_from_member<pListView&>(*new pListView(*this)), Widget(base_from_member<pListView&>::value), p(base_from_member<pListView&>::value) { p.constructor(); }
void ProgressBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
ProgressBar::ProgressBar() : state(*new State), base_from_member<pProgressBar&>(*new pProgressBar(*this)), Widget(base_from_member<pProgressBar&>::value), p(base_from_member<pProgressBar&>::value) { p.constructor(); }
void RadioBox::group(const reference_array<RadioBox&> &list) { foreach(item, list) item.p.setGroup(item.state.group = list); if(list.size()) list[0].setChecked(); }
bool RadioBox::checked() { return p.checked(); }
void RadioBox::setChecked() { foreach(item, state.group) item.state.checked = false; state.checked = true; return p.setChecked(); }
void RadioBox::setText(const string &text) { state.text = text; return p.setText(text); }
RadioBox::RadioBox() : state(*new State), base_from_member<pRadioBox&>(*new pRadioBox(*this)), Widget(base_from_member<pRadioBox&>::value), p(base_from_member<pRadioBox&>::value) { p.constructor(); }
void TextEdit::setCursorPosition(unsigned position) { state.cursorPosition = position; return p.setCursorPosition(position); }
void TextEdit::setEditable(bool editable) { state.editable = editable; return p.setEditable(editable); }
void TextEdit::setText(const string &text) { state.text = text; return p.setText(text); }
void TextEdit::setWordWrap(bool wordWrap) { state.wordWrap = wordWrap; return p.setWordWrap(wordWrap); }
string TextEdit::text() { return p.text(); }
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 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); }
VerticalSlider::VerticalSlider() : state(*new State), base_from_member<pVerticalSlider&>(*new pVerticalSlider(*this)), Widget(base_from_member<pVerticalSlider&>::value), p(base_from_member<pVerticalSlider&>::value) { p.constructor(); }
uintptr_t Viewport::handle() { return p.handle(); }
Viewport::Viewport() : base_from_member<pViewport&>(*new pViewport(*this)), Widget(base_from_member<pViewport&>::value), p(base_from_member<pViewport&>::value) { p.constructor(); }

431
bsnes/phoenix/core/core.hpp Executable file
View File

@@ -0,0 +1,431 @@
struct Font;
struct Window;
struct Menu;
struct Layout;
struct Widget;
struct pOS;
struct pFont;
struct pTimer;
struct pWindow;
struct pAction;
struct pMenu;
struct pSeparator;
struct pItem;
struct pCheckItem;
struct pRadioItem;
struct pLayout;
struct pWidget;
struct pButton;
struct pCanvas;
struct pCheckBox;
struct pComboBox;
struct pHexEdit;
struct pHorizontalSlider;
struct pLabel;
struct pLineEdit;
struct pListView;
struct pProgressBar;
struct pRadioBox;
struct pTextEdit;
struct pVerticalSlider;
struct pViewport;
enum : unsigned {
MaximumSize = ~0u,
MinimumSize = 0u,
};
struct Geometry {
signed x, y;
unsigned width, height;
inline Geometry() : x(0), y(0), width(0), height(0) {}
inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
};
struct Object {
Object();
Object& operator=(const Object&) = delete;
Object(const Object&) = delete;
virtual void unused() {} //allows dynamic_cast<> on Object
};
struct OS : Object {
static Geometry availableGeometry();
static Geometry desktopGeometry();
template<typename... Args> static nall::string fileLoad(Window &parent, const nall::string &path, const Args&... args) { return fileLoad_(parent, path, { args... }); }
template<typename... Args> static nall::string fileSave(Window &parent, const nall::string &path, const Args&... args) { return fileSave_(parent, path, { args... }); }
static nall::string folderSelect(Window &parent, const nall::string &path);
static void main();
static bool pendingEvents();
static void processEvents();
static void quit();
OS();
static void initialize();
private:
static nall::string fileLoad_(Window &parent, const nall::string &path, const nall::lstring& filter);
static nall::string fileSave_(Window &parent, const nall::string &path, const nall::lstring& filter);
};
struct Font : Object {
Geometry geometry(const nall::string &text);
void setBold(bool bold = true);
void setFamily(const nall::string &family);
void setItalic(bool italic = true);
void setSize(unsigned size);
void setUnderline(bool underline = true);
Font();
struct State;
State &state;
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,
OkCancel,
YesNo,
};
enum class Response : unsigned {
Ok,
Cancel,
Yes,
No,
};
static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo);
static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
};
struct Window : Object {
static Window None;
nall::function<void ()> onClose;
nall::function<void ()> onMove;
nall::function<void ()> onSize;
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
Geometry frameGeometry();
Geometry frameMargin();
bool focused();
Geometry geometry();
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setFrameGeometry(const Geometry &geometry);
void setFocused();
void setFullScreen(bool fullScreen = true);
void setGeometry(const Geometry &geometry);
void setMenuFont(Font &font);
void setMenuVisible(bool visible = true);
void setResizable(bool resizable = true);
void setStatusFont(Font &font);
void setStatusText(const nall::string &text);
void setStatusVisible(bool visible = true);
void setTitle(const nall::string &text);
void setVisible(bool visible = true);
void setWidgetFont(Font &font);
Window();
struct State;
State &state;
pWindow &p;
};
struct Action : Object {
void setEnabled(bool enabled = true);
void setVisible(bool visible = true);
Action(pAction &p);
struct State;
State &state;
pAction &p;
};
struct Menu : private nall::base_from_member<pMenu&>, Action {
void append(Action &action);
void setText(const nall::string &text);
Menu();
struct State;
State &state;
pMenu &p;
};
struct Separator : private nall::base_from_member<pSeparator&>, Action {
Separator();
pSeparator &p;
};
struct Item : private nall::base_from_member<pItem&>, Action {
nall::function<void ()> onTick;
void setText(const nall::string &text);
Item();
struct State;
State &state;
pItem &p;
};
struct CheckItem : private nall::base_from_member<pCheckItem&>, Action {
nall::function<void ()> onTick;
bool checked();
void setChecked(bool checked = true);
void setText(const nall::string &text);
CheckItem();
struct State;
State &state;
pCheckItem &p;
};
struct RadioItem : private nall::base_from_member<pRadioItem&>, Action {
template<typename... Args> static void group(Args&... args) { group({ args... }); }
static void group(const nall::reference_array<RadioItem&> &list);
nall::function<void ()> onTick;
bool checked();
void setChecked();
void setText(const nall::string &text);
RadioItem();
struct State;
State &state;
pRadioItem &p;
};
struct Layout : Object {
virtual void setGeometry(const Geometry &geometry) = 0;
virtual void setParent(Window &parent) = 0;
virtual void setVisible(bool visible = true) = 0;
};
struct Widget : Object {
bool enabled();
Font& font();
Geometry minimumGeometry();
void setEnabled(bool enabled = true);
void setFocused();
void setFont(Font &font);
void setGeometry(const Geometry &geometry);
void setVisible(bool visible = true);
bool visible();
Widget();
Widget(pWidget &p);
struct State;
State &state;
pWidget &p;
};
struct Button : private nall::base_from_member<pButton&>, Widget {
nall::function<void ()> onTick;
void setText(const nall::string &text);
Button();
struct State;
State &state;
pButton &p;
};
struct Canvas : private nall::base_from_member<pCanvas&>, Widget {
uint32_t* buffer();
void update();
Canvas();
pCanvas &p;
};
struct CheckBox : private nall::base_from_member<pCheckBox&>, Widget {
nall::function<void ()> onTick;
bool checked();
void setChecked(bool checked = true);
void setText(const nall::string &text);
CheckBox();
struct State;
State &state;
pCheckBox &p;
};
struct ComboBox : private nall::base_from_member<pComboBox&>, Widget {
nall::function<void ()> onChange;
void append(const nall::string &text);
void reset();
unsigned selection();
void setSelection(unsigned row);
ComboBox();
struct State;
State &state;
pComboBox &p;
};
struct HexEdit : private nall::base_from_member<pHexEdit&>, Widget {
nall::function<uint8_t (unsigned)> onRead;
nall::function<void (unsigned, uint8_t)> onWrite;
void setColumns(unsigned columns);
void setLength(unsigned length);
void setOffset(unsigned offset);
void setRows(unsigned rows);
void update();
HexEdit();
struct State;
State &state;
pHexEdit &p;
};
struct HorizontalSlider : private nall::base_from_member<pHorizontalSlider&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
HorizontalSlider();
struct State;
State &state;
pHorizontalSlider &p;
};
struct Label : private nall::base_from_member<pLabel&>, Widget {
void setText(const nall::string &text);
Label();
struct State;
State &state;
pLabel &p;
};
struct LineEdit : private nall::base_from_member<pLineEdit&>, Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
void setEditable(bool editable = true);
void setText(const nall::string &text);
nall::string text();
LineEdit();
struct State;
State &state;
pLineEdit &p;
};
struct ListView : private nall::base_from_member<pListView&>, Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
nall::function<void (unsigned)> onTick;
template<typename... Args> void append(const Args&... args) { append_({ args... }); }
void autoSizeColumns();
bool checked(unsigned row);
template<typename... Args> void modify(unsigned row, const Args&... args) { modify_(row, { args... }); }
void reset();
bool selected();
unsigned selection();
void setCheckable(bool checkable = true);
void setChecked(unsigned row, bool checked = true);
template<typename... Args> void setHeaderText(const Args&... args) { setHeaderText_({ args... }); }
void setHeaderVisible(bool visible = true);
void setSelected(bool selected = true);
void setSelection(unsigned row);
ListView();
struct State;
State &state;
pListView &p;
private:
void append_(const nall::lstring &list);
void modify_(unsigned row, const nall::lstring &list);
void setHeaderText_(const nall::lstring &list);
};
struct ProgressBar : private nall::base_from_member<pProgressBar&>, Widget {
void setPosition(unsigned position);
ProgressBar();
struct State;
State &state;
pProgressBar &p;
};
struct RadioBox : private nall::base_from_member<pRadioBox&>, Widget {
template<typename... Args> static void group(Args&... args) { group({ args... }); }
static void group(const nall::reference_array<RadioBox&> &list);
nall::function<void ()> onTick;
bool checked();
void setChecked();
void setText(const nall::string &text);
RadioBox();
struct State;
State &state;
pRadioBox &p;
};
struct TextEdit : private nall::base_from_member<pTextEdit&>, Widget {
nall::function<void ()> onChange;
void setCursorPosition(unsigned position);
void setEditable(bool editable = true);
void setText(const nall::string &text);
void setWordWrap(bool wordWrap = true);
nall::string text();
TextEdit();
struct State;
State &state;
pTextEdit &p;
};
struct VerticalSlider : private nall::base_from_member<pVerticalSlider&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
VerticalSlider();
struct State;
State &state;
pVerticalSlider &p;
};
struct Viewport : private nall::base_from_member<pViewport&>, Widget {
uintptr_t handle();
Viewport();
pViewport &p;
};
#include "layout/fixed-layout.hpp"
#include "layout/horizontal-layout.hpp"
#include "layout/vertical-layout.hpp"

View File

@@ -0,0 +1,20 @@
void FixedLayout::setParent(Window &parent) {
foreach(child, children) {
parent.append(*child.widget);
child.widget->setGeometry(child.geometry);
}
}
void FixedLayout::append(Widget &widget, const Geometry &geometry) {
children.append({ &widget, geometry });
}
void FixedLayout::setGeometry(const Geometry &geometry) {
}
void FixedLayout::setVisible(bool visible) {
foreach(child, children) child.widget->setVisible(visible);
}
FixedLayout::FixedLayout() {
}

View File

@@ -0,0 +1,15 @@
struct FixedLayout : Layout {
void append(Widget &widget, const Geometry &geometry);
void setGeometry(const Geometry &geometry);
void setParent(Window &parent);
void setVisible(bool visible);
FixedLayout();
//private:
Window *parent;
struct Children {
Widget *widget;
Geometry geometry;
};
nall::linear_vector<Children> children;
};

View File

@@ -0,0 +1,139 @@
void HorizontalLayout::append(VerticalLayout &layout, unsigned spacing) {
children.append({ &layout, 0, MinimumSize, MinimumSize, spacing });
}
void HorizontalLayout::append(Widget &widget, unsigned width, unsigned height, unsigned spacing) {
children.append({ 0, &widget, width, height, spacing });
}
Geometry HorizontalLayout::minimumGeometry() {
unsigned width = 0, height = 0;
foreach(child, children) {
width += child.spacing;
if(child.width == MinimumSize || child.width == MaximumSize) {
if(child.layout) width += child.layout->minimumGeometry().width;
if(child.widget) width += child.widget->minimumGeometry().width;
continue;
}
width += child.width;
}
foreach(child, children) {
if(child.height == MinimumSize || child.height == MaximumSize) {
if(child.layout) height = max(height, child.layout->minimumGeometry().height);
if(child.widget) height = max(height, child.widget->minimumGeometry().height);
continue;
}
height = max(height, child.height);
}
return { 0, 0, margin * 2 + width, margin * 2 + height };
}
Geometry HorizontalLayout::minimumLayoutGeometry() {
unsigned width = 0, height = 0;
bool maximumWidth = false;
bool maximumHeight = false;
foreach(child, children) {
if(child.width == MaximumSize) {
maximumWidth = true;
break;
}
if(child.width == MinimumSize) {
if(child.layout) width += child.layout->minimumGeometry().width;
if(child.widget) width += child.widget->minimumGeometry().width;
continue;
}
width += child.width;
}
foreach(child, children) {
if(child.height == MaximumSize) {
maximumHeight = true;
break;
}
if(child.height == MinimumSize) {
if(child.layout) height = max(height, child.layout->minimumGeometry().height);
if(child.widget) height = max(height, child.widget->minimumGeometry().height);
continue;
}
height = max(height, child.height);
}
return { 0, 0, maximumWidth ? MaximumSize : margin * 2 + width, maximumHeight ? MaximumSize : margin * 2 + height };
}
void HorizontalLayout::setGeometry(const Geometry &containerGeometry) {
auto children = this->children;
foreach(child, children) {
if(child.layout) {
child.width = child.layout->minimumLayoutGeometry().width;
child.height = child.layout->minimumLayoutGeometry().height;
}
if(child.widget) {
if(child.width == MinimumSize) child.width = child.widget->minimumGeometry().width;
if(child.height == MinimumSize) child.height = child.widget->minimumGeometry().height;
}
}
Geometry geometry = containerGeometry;
geometry.x += margin;
geometry.y += margin;
geometry.width -= margin * 2;
geometry.height -= margin * 2;
unsigned minimumWidth = 0, maximumWidthCounter = 0;
foreach(child, children) {
if(child.width == MaximumSize) maximumWidthCounter++;
if(child.width != MaximumSize) minimumWidth += child.width;
minimumWidth += child.spacing;
}
foreach(child, children) {
if(child.width == MaximumSize) child.width = (geometry.width - minimumWidth) / maximumWidthCounter;
if(child.height == MaximumSize) child.height = geometry.height;
}
unsigned maximumHeight = 0;
foreach(child, children) maximumHeight = max(maximumHeight, child.height);
foreach(child, children) {
unsigned pivot = (maximumHeight - child.height) / 2;
Geometry childGeometry = { geometry.x, geometry.y + pivot, child.width, child.height };
if(child.layout) child.layout->setGeometry(childGeometry);
if(child.widget) child.widget->setGeometry(childGeometry);
geometry.x += child.width + child.spacing;
geometry.width -= child.width + child.spacing;
}
}
void HorizontalLayout::setMargin(unsigned margin) {
this->margin = margin;
}
void HorizontalLayout::setParent(Window &parent) {
foreach(child, children) {
if(child.layout) child.layout->setParent(parent);
if(child.widget) parent.append(*child.widget);
}
}
void HorizontalLayout::setVisible(bool visible) {
foreach(child, children) {
if(child.layout) child.layout->setVisible(visible);
if(child.widget) child.widget->setVisible(visible);
}
}
HorizontalLayout::HorizontalLayout() {
margin = 0;
}

View File

@@ -0,0 +1,22 @@
struct VerticalLayout;
struct HorizontalLayout : public Layout {
void append(VerticalLayout &layout, unsigned spacing = 0);
void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
Geometry minimumLayoutGeometry();
Geometry minimumGeometry();
void setGeometry(const Geometry &geometry);
void setMargin(unsigned margin);
void setParent(Window &parent);
void setVisible(bool visible);
HorizontalLayout();
//private:
unsigned margin;
struct Children {
VerticalLayout *layout;
Widget *widget;
unsigned width, height, spacing;
};
nall::linear_vector<Children> children;
};

View File

@@ -0,0 +1,139 @@
void VerticalLayout::append(HorizontalLayout &layout, unsigned spacing) {
children.append({ &layout, 0, MinimumSize, MinimumSize, spacing });
}
void VerticalLayout::append(Widget &widget, unsigned width, unsigned height, unsigned spacing) {
children.append({ 0, &widget, width, height, spacing });
}
Geometry VerticalLayout::minimumGeometry() {
unsigned width = 0, height = 0;
foreach(child, children) {
if(child.width == MinimumSize || child.width == MaximumSize) {
if(child.layout) width = max(width, child.layout->minimumGeometry().width);
if(child.widget) width = max(width, child.widget->minimumGeometry().width);
continue;
}
width = max(width, child.width);
}
foreach(child, children) {
height += child.spacing;
if(child.height == MinimumSize || child.height == MaximumSize) {
if(child.layout) height += child.layout->minimumGeometry().height;
if(child.widget) height += child.widget->minimumGeometry().height;
continue;
}
height += child.height;
}
return { 0, 0, margin * 2 + width, margin * 2 + height };
}
Geometry VerticalLayout::minimumLayoutGeometry() {
unsigned width = 0, height = 0;
bool maximumWidth = false;
bool maximumHeight = false;
foreach(child, children) {
if(child.width == MaximumSize) {
maximumWidth = true;
break;
}
if(child.width == MinimumSize) {
if(child.layout) width = max(width, child.layout->minimumGeometry().width);
if(child.widget) width = max(width, child.widget->minimumGeometry().width);
continue;
}
width = max(width, child.width);
}
foreach(child, children) {
if(child.height == MaximumSize) {
maximumHeight = true;
break;
}
if(child.height == MinimumSize) {
if(child.layout) height += child.layout->minimumGeometry().height;
if(child.widget) height += child.widget->minimumGeometry().height;
continue;
}
height += child.height;
}
return { 0, 0, maximumWidth ? MaximumSize : margin * 2 + width, maximumHeight ? MaximumSize : margin * 2 + height };
}
void VerticalLayout::setGeometry(const Geometry &containerGeometry) {
auto children = this->children;
foreach(child, children) {
if(child.layout) {
child.width = child.layout->minimumLayoutGeometry().width;
child.height = child.layout->minimumLayoutGeometry().height;
}
if(child.widget) {
if(child.width == MinimumSize) child.width = child.widget->minimumGeometry().width;
if(child.height == MinimumSize) child.height = child.widget->minimumGeometry().height;
}
}
Geometry geometry = containerGeometry;
geometry.x += margin;
geometry.y += margin;
geometry.width -= margin * 2;
geometry.height -= margin * 2;
unsigned minimumHeight = 0, maximumHeightCounter = 0;
foreach(child, children) {
if(child.height == MaximumSize) maximumHeightCounter++;
if(child.height != MaximumSize) minimumHeight += child.height;
minimumHeight += child.spacing;
}
foreach(child, children) {
if(child.width == MaximumSize) child.width = geometry.width;
if(child.height == MaximumSize) child.height = (geometry.height - minimumHeight) / maximumHeightCounter;
}
unsigned maximumWidth = 0;
foreach(child, children) maximumWidth = max(maximumWidth, child.width);
foreach(child, children) {
unsigned pivot = 0; //(maximumWidth - child.width) / 2;
Geometry childGeometry = { geometry.x + pivot, geometry.y, child.width, child.height };
if(child.layout) child.layout->setGeometry(childGeometry);
if(child.widget) child.widget->setGeometry(childGeometry);
geometry.y += child.height + child.spacing;
geometry.height -= child.height + child.spacing;
}
}
void VerticalLayout::setMargin(unsigned margin) {
this->margin = margin;
}
void VerticalLayout::setParent(Window &parent) {
foreach(child, children) {
if(child.layout) child.layout->setParent(parent);
if(child.widget) parent.append(*child.widget);
}
}
void VerticalLayout::setVisible(bool visible) {
foreach(child, children) {
if(child.layout) child.layout->setVisible(visible);
if(child.widget) child.widget->setVisible(visible);
}
}
VerticalLayout::VerticalLayout() {
margin = 0;
}

View File

@@ -0,0 +1,22 @@
struct HorizontalLayout;
struct VerticalLayout : public Layout {
void append(HorizontalLayout &layout, unsigned spacing = 0);
void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
Geometry minimumGeometry();
Geometry minimumLayoutGeometry();
void setGeometry(const Geometry &geometry);
void setMargin(unsigned margin);
void setParent(Window &parent);
void setVisible(bool visible);
VerticalLayout();
//private:
unsigned margin;
struct Children {
HorizontalLayout *layout;
Widget *widget;
unsigned width, height, spacing;
};
nall::linear_vector<Children> children;
};

234
bsnes/phoenix/core/state.hpp Executable file
View File

@@ -0,0 +1,234 @@
struct Font::State {
bool bold;
string family;
bool italic;
unsigned size;
bool underline;
State() {
bold = false;
italic = false;
size = 8;
underline = false;
}
};
struct Timer::State {
bool enabled;
unsigned milliseconds;
State() {
enabled = false;
milliseconds = 0;
}
};
struct Window::State {
bool backgroundColor;
unsigned backgroundColorRed, backgroundColorGreen, backgroundColorBlue;
bool fullScreen;
Geometry geometry;
reference_array<Layout&> layout;
reference_array<Menu&> menu;
Font *menuFont;
bool menuVisible;
bool resizable;
Font *statusFont;
string statusText;
bool statusVisible;
string title;
bool visible;
reference_array<Widget&> widget;
Font *widgetFont;
State() {
backgroundColor = false;
backgroundColorRed = 0;
backgroundColorGreen = 0;
backgroundColorBlue = 0;
fullScreen = false;
geometry = { 128, 128, 256, 256 };
menuFont = 0;
menuVisible = false;
resizable = true;
statusVisible = false;
visible = false;
widgetFont = 0;
}
};
struct Action::State {
bool enabled;
Window *parent;
bool visible;
State() {
enabled = true;
parent = 0;
visible = true;
}
};
struct Menu::State {
reference_array<Action&> action;
string text;
};
struct Item::State {
string text;
};
struct CheckItem::State {
bool checked;
string text;
State() {
checked = false;
}
};
struct RadioItem::State {
bool checked;
reference_array<RadioItem&> group;
string text;
State() {
checked = true;
}
};
struct Widget::State {
bool abstract;
bool enabled;
Font *font;
Geometry geometry;
bool visible;
State() {
abstract = false;
enabled = true;
font = 0;
geometry = { 0, 0, 0, 0 };
visible = true;
}
};
struct Button::State {
string text;
State() {
}
};
struct CheckBox::State {
bool checked;
string text;
State() {
checked = false;
}
};
struct ComboBox::State {
unsigned selection;
linear_vector<string> text;
State() {
selection = 0;
}
};
struct HexEdit::State {
unsigned columns;
unsigned length;
unsigned offset;
unsigned rows;
State() {
columns = 16;
length = 0;
offset = 0;
rows = 16;
}
};
struct HorizontalSlider::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};
struct Label::State {
string text;
};
struct LineEdit::State {
bool editable;
string text;
State() {
editable = true;
}
};
struct ListView::State {
bool checkable;
array<bool> checked;
lstring headerText;
bool headerVisible;
bool selected;
unsigned selection;
linear_vector<lstring> text;
State() {
checkable = false;
headerVisible = false;
selected = false;
selection = 0;
}
};
struct ProgressBar::State {
unsigned position;
State() {
position = 0;
}
};
struct RadioBox::State {
bool checked;
reference_array<RadioBox&> group;
string text;
State() {
checked = true;
}
};
struct TextEdit::State {
unsigned cursorPosition;
bool editable;
string text;
bool wordWrap;
State() {
cursorPosition = 0;
editable = true;
wordWrap = false;
}
};
struct VerticalSlider::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};

View File

@@ -0,0 +1,22 @@
static void Action_setFont(GtkWidget *widget, gpointer font) {
if(font == 0) return;
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font);
}
}
void pAction::setEnabled(bool enabled) {
gtk_widget_set_sensitive(widget, enabled);
}
void pAction::setFont(Font &font) {
Action_setFont(widget, font.p.gtkFont);
}
void pAction::setVisible(bool visible) {
gtk_widget_set_visible(widget, visible);
}
void pAction::constructor() {
}

View File

@@ -0,0 +1,22 @@
static void CheckItem_tick(CheckItem *self) {
if(self->p.locked == false && self->onTick) self->onTick();
}
bool pCheckItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
}
void pCheckItem::setChecked(bool checked) {
locked = true;
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), checked);
locked = false;
}
void pCheckItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pCheckItem::constructor() {
widget = gtk_check_menu_item_new_with_label("");
g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(CheckItem_tick), (gpointer)&checkItem);
}

View File

@@ -0,0 +1,12 @@
static void Item_tick(Item *self) {
if(self->onTick) self->onTick();
}
void pItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pItem::constructor() {
widget = gtk_menu_item_new_with_label("");
g_signal_connect_swapped(G_OBJECT(widget), "activate", G_CALLBACK(Item_tick), (gpointer)&item);
}

View File

@@ -0,0 +1,19 @@
void pMenu::append(Action &action) {
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), action.p.widget);
gtk_widget_show(action.p.widget);
}
void pMenu::setFont(Font &font) {
pAction::setFont(font);
foreach(item, menu.state.action) item.p.setFont(font);
}
void pMenu::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pMenu::constructor() {
submenu = gtk_menu_new();
widget = gtk_menu_item_new_with_label("");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu);
}

View File

@@ -0,0 +1,33 @@
static void RadioItem_tick(RadioItem *self) {
if(self->p.locked == false && self->checked() && self->onTick) self->onTick();
}
bool pRadioItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
}
void pRadioItem::setChecked() {
locked = true;
foreach(item, radioItem.state.group) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), false);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true);
locked = false;
}
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
foreach(item, group, n) {
if(n == 0) continue;
GSList *currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(group[0].p.widget));
if(currentGroup != gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item.p.widget))) {
gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item.p.widget), currentGroup);
}
}
}
void pRadioItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pRadioItem::constructor() {
widget = gtk_radio_menu_item_new_with_label(0, "");
g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(RadioItem_tick), (gpointer)&radioItem);
}

View File

@@ -0,0 +1,3 @@
void pSeparator::constructor() {
widget = gtk_separator_menu_item_new();
}

View File

@@ -1,13 +0,0 @@
static void Button_tick(Button *self) {
if(self->onTick) self->onTick();
}
void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_button_new_with_label(text);
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "clicked", G_CALLBACK(Button_tick), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}

View File

@@ -1,59 +0,0 @@
static void Canvas_expose(Canvas *self) {
uint32_t *rgb = self->canvas->bufferRGB;
uint32_t *bgr = self->canvas->bufferBGR;
for(unsigned y = self->object->widget->allocation.height; y; y--) {
for(unsigned x = self->object->widget->allocation.width; x; x--) {
uint32_t pixel = *rgb++;
*bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff);
}
}
gdk_draw_rgb_32_image(
self->object->widget->window,
self->object->widget->style->fg_gc[GTK_WIDGET_STATE(self->object->widget)],
0, 0, self->object->widget->allocation.width, self->object->widget->allocation.height,
GDK_RGB_DITHER_NONE, (guchar*)self->canvas->bufferBGR, self->canvas->pitch
);
}
void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
canvas->bufferRGB = new uint32_t[width * height]();
canvas->bufferBGR = new uint32_t[width * height]();
canvas->pitch = width * sizeof(uint32_t);
object->widget = gtk_drawing_area_new();
widget->parent = &parent;
GdkColor color;
color.pixel = color.red = color.green = color.blue = 0;
gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color);
gtk_widget_set_double_buffered(object->widget, false);
gtk_widget_add_events(object->widget, GDK_EXPOSURE_MASK);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
uint32_t* Canvas::buffer() {
return canvas->bufferRGB;
}
void Canvas::redraw() {
GdkRectangle rect;
rect.x = 0;
rect.y = 0;
rect.width = object->widget->allocation.width;
rect.height = object->widget->allocation.height;
gdk_window_invalidate_rect(object->widget->window, &rect, true);
}
Canvas::Canvas() {
canvas = new Canvas::Data;
canvas->bufferRGB = 0;
canvas->bufferBGR = 0;
}
Canvas::~Canvas() {
if(canvas->bufferRGB) delete[] canvas->bufferRGB;
if(canvas->bufferBGR) delete[] canvas->bufferBGR;
}

View File

@@ -1,23 +0,0 @@
static void CheckBox_tick(CheckBox *self) {
if(self->onTick && self->object->locked == false) self->onTick();
}
void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_check_button_new_with_label(text);
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
bool CheckBox::checked() {
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget));
}
void CheckBox::setChecked(bool checked) {
object->locked = true;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), checked);
object->locked = false;
}

View File

@@ -1,48 +0,0 @@
void ComboBox_change(ComboBox *self) {
if(self->object->locked == false && self->onChange) self->onChange();
}
void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_combo_box_new_text();
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(ComboBox_change), (gpointer)this);
if(*text) {
lstring list;
list.split("\n", text);
foreach(item, list) addItem(item);
}
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void ComboBox::reset() {
object->locked = true;
for(signed i = counter - 1; i >= 0; i--) {
gtk_combo_box_remove_text(GTK_COMBO_BOX(object->widget), i);
}
object->locked = false;
counter = 0;
}
void ComboBox::addItem(const string &text) {
gtk_combo_box_append_text(GTK_COMBO_BOX(object->widget), text);
if(counter++ == 0) setSelection(0);
}
unsigned ComboBox::selection() {
return gtk_combo_box_get_active(GTK_COMBO_BOX(object->widget));
}
void ComboBox::setSelection(unsigned item) {
object->locked = true;
gtk_combo_box_set_active(GTK_COMBO_BOX(object->widget), item);
object->locked = false;
}
ComboBox::ComboBox() {
counter = 0;
}

View File

@@ -1,58 +0,0 @@
static void EditBox_change(EditBox *self) {
if(self->object->locked == false && self->onChange) self->onChange();
}
void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_scrolled_window_new(0, 0);
widget->parent = &parent;
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN);
gtk_widget_set_size_request(object->widget, width, height);
object->subWidget = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), GTK_WRAP_WORD_CHAR);
gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget);
object->textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(object->subWidget));
gtk_text_buffer_set_text(object->textBuffer, text, -1);
g_signal_connect_swapped(G_OBJECT(object->textBuffer), "changed", G_CALLBACK(EditBox_change), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->subWidget);
gtk_widget_show(object->widget);
}
void EditBox::setFocused() {
gtk_widget_grab_focus(object->subWidget);
}
void EditBox::setEditable(bool editable) {
gtk_text_view_set_editable(GTK_TEXT_VIEW(object->subWidget), editable);
}
void EditBox::setWordWrap(bool wordWrap) {
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
}
string EditBox::text() {
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(object->textBuffer, &start);
gtk_text_buffer_get_end_iter(object->textBuffer, &end);
char *temp = gtk_text_buffer_get_text(object->textBuffer, &start, &end, true);
string text = temp;
g_free(temp);
return text;
}
void EditBox::setText(const string &text) {
object->locked = true;
gtk_text_buffer_set_text(object->textBuffer, text, -1);
object->locked = false;
}
void EditBox::setCursorPosition(unsigned position) {
GtkTextMark *mark = gtk_text_buffer_get_mark(object->textBuffer, "insert");
GtkTextIter iter;
gtk_text_buffer_get_end_iter(object->textBuffer, &iter);
gtk_text_iter_set_offset(&iter, min(position, gtk_text_iter_get_offset(&iter)));
gtk_text_buffer_place_cursor(object->textBuffer, &iter);
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(object->subWidget), mark);
}

View File

@@ -1,18 +1,32 @@
bool Font::create(const string &name, unsigned size, Font::Style style) {
font->font = pango_font_description_new();
pango_font_description_set_family(font->font, name);
pango_font_description_set_size(font->font, size * PANGO_SCALE);
pango_font_description_set_style(font->font, (style & Style::Italic) == Style::Italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL);
pango_font_description_set_weight(font->font, (style & Style::Bold) == Style::Bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
return true;
Geometry pFont::geometry(const string &text) {
pango_layout_set_font_description(gtkLayout, gtkFont);
pango_layout_set_text(gtkLayout, text, -1);
int width = 0, height = 0;
pango_layout_get_pixel_size(gtkLayout, &width, &height);
return { 0, 0, width, height };
}
Font::Font() {
font = new Font::Data;
font->font = 0;
void pFont::setBold(bool bold) {
pango_font_description_set_weight(gtkFont, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
}
Font::~Font() {
if(font->font) pango_font_description_free(font->font);
delete font;
void pFont::setFamily(const string &family) {
pango_font_description_set_family(gtkFont, family);
}
void pFont::setItalic(bool italic) {
pango_font_description_set_style(gtkFont, italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL);
}
void pFont::setSize(unsigned size) {
pango_font_description_set_size(gtkFont, size * PANGO_SCALE);
}
void pFont::setUnderline(bool underline) {
}
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);
}

View File

@@ -1,49 +1,159 @@
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#include "gtk.hpp"
#define None X11None
#define Window X11Window
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <cairo.h>
#include <gdk/gdkkeysyms.h>
#undef None
#undef Window
using namespace nall;
namespace phoenix {
#include "object.cpp"
#include "settings.cpp"
#include "font.cpp"
#include "menu.cpp"
#include "widget.cpp"
#include "timer.cpp"
#include "message-window.cpp"
#include "window.cpp"
#include "button.cpp"
#include "canvas.cpp"
#include "checkbox.cpp"
#include "combobox.cpp"
#include "editbox.cpp"
#include "hexeditor.cpp"
#include "horizontalslider.cpp"
#include "label.cpp"
#include "listbox.cpp"
#include "progressbar.cpp"
#include "radiobox.cpp"
#include "textbox.cpp"
#include "verticalslider.cpp"
#include "viewport.cpp"
#include "messagewindow.cpp"
Window Window::None;
#include "action/action.cpp"
#include "action/menu.cpp"
#include "action/separator.cpp"
#include "action/item.cpp"
#include "action/check-item.cpp"
#include "action/radio-item.cpp"
void OS::initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
#include "widget/widget.cpp"
#include "widget/button.cpp"
#include "widget/canvas.cpp"
#include "widget/check-box.cpp"
#include "widget/combo-box.cpp"
#include "widget/hex-edit.cpp"
#include "widget/horizontal-slider.cpp"
#include "widget/label.cpp"
#include "widget/line-edit.cpp"
#include "widget/list-view.cpp"
#include "widget/progress-bar.cpp"
#include "widget/radio-box.cpp"
#include "widget/text-edit.cpp"
#include "widget/vertical-slider.cpp"
#include "widget/viewport.cpp"
Font pOS::defaultFont;
Geometry pOS::availableGeometry() {
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() {
return {
0, 0,
gdk_screen_get_width(gdk_screen_get_default()),
gdk_screen_get_height(gdk_screen_get_default())
};
}
static string pOS_fileDialog(bool save, Window &parent, const string &path, const lstring &filter) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
save == 0 ? "Load File" : "Save File",
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
save == 0 ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
foreach(filterItem, filter) {
GtkFileFilter *gtkFilter = gtk_file_filter_new();
gtk_file_filter_set_name(gtkFilter, filterItem);
lstring part;
part.split("(", filterItem);
part[1].rtrim<1>(")");
lstring list;
list.split(",", part[1]);
foreach(pattern, list) gtk_file_filter_add_pattern(gtkFilter, pattern);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), gtkFilter);
}
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
return name;
}
string pOS::fileLoad(Window &parent, const string &path, const lstring &filter) {
return pOS_fileDialog(0, parent, path, filter);
}
string pOS::fileSave(Window &parent, const string &path, const lstring &filter) {
return pOS_fileDialog(1, parent, path, filter);
}
string pOS::folderSelect(Window &parent, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Select Folder",
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
if(name == "") return "";
if(name.endswith("/") == false) name.append("/");
return name;
}
void pOS::main() {
gtk_main();
}
bool pOS::pendingEvents() {
return gtk_events_pending();
}
void pOS::processEvents() {
while(pendingEvents()) gtk_main_iteration_do(false);
}
void pOS::quit() {
settings.save();
gtk_main_quit();
}
void pOS::initialize() {
settings.load();
int argc = 1;
char *argv[2];
@@ -63,128 +173,3 @@ void OS::initialize() {
"class \"GtkTreeView\" style \"phoenix-gtk\"\n"
);
}
bool OS::pending() {
return gtk_events_pending();
}
void OS::run() {
while(pending()) gtk_main_iteration_do(false);
}
void OS::main() {
gtk_main();
}
void OS::quit() {
gtk_main_quit();
}
unsigned OS::desktopWidth() {
return gdk_screen_get_width(gdk_screen_get_default());
}
unsigned OS::desktopHeight() {
return gdk_screen_get_height(gdk_screen_get_default());
}
string OS::folderSelect(Window &parent, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Select Folder",
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
if(name.endswith("/") == false) name.append("/");
return name;
}
string OS::fileOpen(Window &parent, const string &filter, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Open File",
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
lstring list;
list.split("\n", filter);
foreach(item, list) {
lstring part;
part.split("\t", item);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")"));
lstring patterns;
patterns.split(",", part[1]);
foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
}
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
return name;
}
string OS::fileSave(Window &parent, const string &filter, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Save File",
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
lstring list;
list.split("\n", filter);
foreach(item, list) {
lstring part;
part.split("\t", item);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")"));
lstring patterns;
patterns.split(",", part[1]);
foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
}
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
return name;
}
}

View File

@@ -1,287 +1,390 @@
namespace phoenix {
struct Settings : public configuration {
unsigned frameGeometryX;
unsigned frameGeometryY;
unsigned frameGeometryWidth;
unsigned frameGeometryHeight;
unsigned menuGeometryHeight;
unsigned statusGeometryHeight;
struct Window;
struct Object {
Object();
Object& operator=(const Object&) = delete;
Object(const Object&) = delete;
//private:
virtual void unused();
struct Data;
Data *object;
void load();
void save();
Settings();
};
struct Geometry {
unsigned x, y;
unsigned width, height;
inline Geometry() : x(0), y(0), width(0), height(0) {}
inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
struct pFont;
struct pWindow;
struct pMenu;
struct pLayout;
struct pWidget;
struct pObject {
bool locked;
pObject() {
locked = false;
}
};
struct Font : Object {
enum class Style : unsigned {
None = 0,
Bold = 1,
Italic = 2,
};
bool create(const nall::string &name, unsigned size, Font::Style style = Style::None);
Font();
~Font();
//private:
struct Data;
Data *font;
};
struct pOS : public pObject {
static Font defaultFont;
inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); }
inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); }
struct Action : Object {
bool visible();
void setVisible(bool visible = true);
bool enabled();
void setEnabled(bool enabled = true);
Action();
//private:
struct Data;
Data *action;
};
struct Menu : Action {
void create(Window &parent, const nall::string &text);
void create(Menu &parent, const nall::string &text);
};
struct MenuSeparator : Action {
void create(Menu &parent);
};
struct MenuItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const nall::string &text);
};
struct MenuCheckItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const nall::string &text);
bool checked();
void setChecked(bool checked = true);
};
struct MenuRadioItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const nall::string &text);
void create(MenuRadioItem &parent, const nall::string &text);
bool checked();
void setChecked();
private:
MenuRadioItem *first;
};
struct Widget : Object {
virtual void setFont(Font &font);
bool visible();
void setVisible(bool visible = true);
bool enabled();
void setEnabled(bool enabled = true);
virtual bool focused();
virtual void setFocused();
virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height);
Widget();
//private:
struct Data;
Data *widget;
};
struct Window : Widget {
nall::function<bool ()> onClose;
void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
bool focused();
void setFocused();
Geometry geometry();
void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height);
void setDefaultFont(Font &font);
void setFont(Font &font);
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setTitle(const nall::string &text);
void setStatusText(const nall::string &text);
void setMenuVisible(bool visible = true);
void setStatusVisible(bool visible = true);
Window();
//private:
struct Data;
Data *window;
static Window None;
};
struct Button : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
};
struct Canvas : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
uint32_t* buffer();
void redraw();
Canvas();
~Canvas();
//private:
struct Data;
Data *canvas;
};
struct CheckBox : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
bool checked();
void setChecked(bool checked = true);
};
struct ComboBox : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void reset();
void addItem(const nall::string &text);
unsigned selection();
void setSelection(unsigned item);
ComboBox();
private:
unsigned counter;
};
struct EditBox : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setFocused();
void setEditable(bool editable = true);
void setWordWrap(bool wordWrap = true);
nall::string text();
void setText(const nall::string &text);
void setCursorPosition(unsigned position);
};
struct HexEditor : Widget {
nall::function<uint8_t (unsigned)> onRead;
nall::function<void (unsigned, uint8_t)> onWrite;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
void setSize(unsigned size);
void setOffset(unsigned offset);
void setColumns(unsigned columns);
void setRows(unsigned rows);
void update();
HexEditor();
//private:
struct Data;
Data *hexEditor;
bool keyPress(unsigned scancode);
void scroll(unsigned position);
void setScroll();
void updateScroll();
unsigned cursorPosition();
void setCursorPosition(unsigned position);
};
struct HorizontalSlider : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length);
unsigned position();
void setPosition(unsigned position);
};
struct Label : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setText(const nall::string &text);
};
struct ListBox : Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
nall::function<void (unsigned)> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setFocused();
void setHeaderVisible(bool headerVisible = true);
void setCheckable(bool checkable = true);
void setFont(Font &font);
void reset();
void resizeColumnsToContent();
void addItem(const nall::string &text);
void setItem(unsigned row, const nall::string &text);
bool checked(unsigned row);
void setChecked(unsigned row, bool checked = true);
nall::optional<unsigned> selection();
void setSelection(unsigned row);
ListBox();
//private:
struct Data;
Data *listBox;
};
struct ProgressBar : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
void setPosition(unsigned position);
};
struct RadioBox : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
bool checked();
void setChecked();
private:
RadioBox *first;
};
struct TextBox : Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setEditable(bool editable = true);
nall::string text();
void setText(const nall::string &text);
};
struct VerticalSlider : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length);
unsigned position();
void setPosition(unsigned position);
};
struct Viewport : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
uintptr_t handle();
};
struct MessageWindow : Object {
enum class Buttons : unsigned {
Ok,
OkCancel,
YesNo,
};
enum class Response : unsigned {
Ok,
Cancel,
Yes,
No,
};
static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo);
static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
};
struct OS : Object {
static bool pending();
static void run();
static Geometry availableGeometry();
static Geometry desktopGeometry();
static string fileLoad(Window &parent, const string &path, const lstring &filter);
static string fileSave(Window &parent, const string &path, const lstring &filter);
static string folderSelect(Window &parent, const string &path);
static void main();
static bool pendingEvents();
static void processEvents();
static void quit();
static unsigned desktopWidth();
static unsigned desktopHeight();
static nall::string folderSelect(Window &parent, const nall::string &path = "");
static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = "");
static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = "");
//private:
static void initialize();
};
}
struct pFont : public pObject {
Font &font;
PangoFontDescription *gtkFont;
PangoLayout *gtkLayout;
Geometry geometry(const string &text);
void setBold(bool bold);
void setFamily(const string &family);
void setItalic(bool italic);
void setSize(unsigned size);
void setUnderline(bool underline);
pFont(Font &font) : font(font) {}
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);
static MessageWindow::Response warning(Window &parent, const string &text, MessageWindow::Buttons buttons);
static MessageWindow::Response critical(Window &parent, const string &text, MessageWindow::Buttons buttons);
};
struct pWindow : public pObject {
Window &window;
GtkWidget *widget;
GtkWidget *menuContainer;
GtkWidget *formContainer;
GtkWidget *statusContainer;
GtkWidget *menu;
GtkWidget *status;
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
bool focused();
Geometry frameMargin();
Geometry geometry();
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setFocused();
void setFullScreen(bool fullScreen);
void setGeometry(const Geometry &geometry);
void setMenuFont(Font &font);
void setMenuVisible(bool visible);
void setResizable(bool resizable);
void setStatusFont(Font &font);
void setStatusText(const string &text);
void setStatusVisible(bool visible);
void setTitle(const string &text);
void setVisible(bool visible);
void setWidgetFont(Font &font);
pWindow(Window &window) : window(window) {}
void constructor();
unsigned menuHeight();
unsigned statusHeight();
};
struct pAction : public pObject {
Action &action;
GtkWidget *widget;
void setEnabled(bool enabled);
void setVisible(bool visible);
pAction(Action &action) : action(action) {}
void constructor();
virtual void setFont(Font &font);
};
struct pMenu : public pAction {
Menu &menu;
GtkWidget *submenu;
void append(Action &action);
void setText(const string &text);
pMenu(Menu &menu) : pAction(menu), menu(menu) {}
void constructor();
void setFont(Font &font);
};
struct pSeparator : public pAction {
Separator &separator;
pSeparator(Separator &separator) : pAction(separator), separator(separator) {}
void constructor();
};
struct pItem : public pAction {
Item &item;
void setText(const string &text);
pItem(Item &item) : pAction(item), item(item) {}
void constructor();
};
struct pCheckItem : public pAction {
CheckItem &checkItem;
bool checked();
void setChecked(bool checked);
void setText(const string &text);
pCheckItem(CheckItem &checkItem) : pAction(checkItem), checkItem(checkItem) {}
void constructor();
};
struct pRadioItem : public pAction {
RadioItem &radioItem;
bool checked();
void setChecked();
void setGroup(const reference_array<RadioItem&> &group);
void setText(const string &text);
pRadioItem(RadioItem &radioItem) : pAction(radioItem), radioItem(radioItem) {}
void constructor();
};
struct pWidget : public pObject {
Widget &widget;
GtkWidget *gtkWidget;
pWindow *parentWindow;
bool enabled();
Font& font();
virtual Geometry minimumGeometry();
void setEnabled(bool enabled);
virtual void setFocused();
virtual void setFont(Font &font);
virtual void setGeometry(const Geometry &geometry);
void setVisible(bool visible);
pWidget(Widget &widget) : widget(widget) {}
void constructor();
};
struct pButton : public pWidget {
Button &button;
Geometry minimumGeometry();
void setText(const string &text);
pButton(Button &button) : pWidget(button), button(button) {}
void constructor();
};
struct pCanvas : public pWidget {
Canvas &canvas;
uint32_t *bufferRGB;
uint32_t *bufferBGR;
uint32_t* buffer();
void setGeometry(const Geometry &geometry);
void update();
pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}
void constructor();
void redraw();
};
struct pCheckBox : public pWidget {
CheckBox &checkBox;
bool checked();
Geometry minimumGeometry();
void setChecked(bool checked);
void setText(const string &text);
pCheckBox(CheckBox &checkBox) : pWidget(checkBox), checkBox(checkBox) {}
void constructor();
};
struct pComboBox : public pWidget {
ComboBox &comboBox;
unsigned itemCounter;
void append(const string &text);
Geometry minimumGeometry();
void reset();
unsigned selection();
void setSelection(unsigned row);
pComboBox(ComboBox &comboBox) : pWidget(comboBox), comboBox(comboBox) {}
void constructor();
};
struct pHexEdit : public pWidget {
HexEdit &hexEdit;
GtkWidget *container;
GtkWidget *subWidget;
GtkWidget *scrollBar;
GtkTextBuffer *textBuffer;
GtkTextMark *textCursor;
void setColumns(unsigned columns);
void setLength(unsigned length);
void setOffset(unsigned offset);
void setRows(unsigned rows);
void update();
pHexEdit(HexEdit &hexEdit) : pWidget(hexEdit), hexEdit(hexEdit) {}
void constructor();
unsigned cursorPosition();
bool keyPress(unsigned scancode);
void scroll(unsigned position);
void setCursorPosition(unsigned position);
void setScroll();
void updateScroll();
};
struct pHorizontalSlider : public pWidget {
HorizontalSlider &horizontalSlider;
Geometry minimumGeometry();
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
pHorizontalSlider(HorizontalSlider &horizontalSlider) : pWidget(horizontalSlider), horizontalSlider(horizontalSlider) {}
void constructor();
};
struct pLabel : public pWidget {
Label &label;
Geometry minimumGeometry();
void setText(const string &text);
pLabel(Label &label) : pWidget(label), label(label) {}
void constructor();
};
struct pLineEdit : public pWidget {
LineEdit &lineEdit;
Geometry minimumGeometry();
void setEditable(bool editable);
void setText(const string &text);
string text();
pLineEdit(LineEdit &lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {}
void constructor();
};
struct pListView : public pWidget {
ListView &listView;
GtkWidget *subWidget;
GtkListStore *store;
struct GtkColumn {
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *label;
};
linear_vector<GtkColumn> column;
void append(const lstring &text);
void autoSizeColumns();
bool checked(unsigned row);
void modify(unsigned row, const lstring &text);
void reset();
bool selected();
unsigned selection();
void setCheckable(bool checkable);
void setChecked(unsigned row, bool checked);
void setHeaderText(const lstring &text);
void setHeaderVisible(bool visible);
void setSelected(bool selected);
void setSelection(unsigned row);
pListView(ListView &listView) : pWidget(listView), listView(listView) {}
void constructor();
void create();
void setFocused();
void setFont(Font &font);
};
struct pProgressBar : public pWidget {
ProgressBar &progressBar;
Geometry minimumGeometry();
void setPosition(unsigned position);
pProgressBar(ProgressBar &progressBar) : pWidget(progressBar), progressBar(progressBar) {}
void constructor();
};
struct pRadioBox : public pWidget {
RadioBox &radioBox;
bool checked();
Geometry minimumGeometry();
void setChecked();
void setGroup(const reference_array<RadioBox&> &group);
void setText(const string &text);
pRadioBox(RadioBox &radioBox) : pWidget(radioBox), radioBox(radioBox) {}
void constructor();
};
struct pTextEdit : public pWidget {
TextEdit &textEdit;
GtkWidget *subWidget;
GtkTextBuffer *textBuffer;
void setCursorPosition(unsigned position);
void setEditable(bool editable);
void setText(const string &text);
void setWordWrap(bool wordWrap);
string text();
pTextEdit(TextEdit &textEdit) : pWidget(textEdit), textEdit(textEdit) {}
void constructor();
};
struct pVerticalSlider : public pWidget {
VerticalSlider &verticalSlider;
Geometry minimumGeometry();
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
pVerticalSlider(VerticalSlider &verticalSlider) : pWidget(verticalSlider), verticalSlider(verticalSlider) {}
void constructor();
};
struct pViewport : public pWidget {
Viewport &viewport;
uintptr_t handle();
pViewport(Viewport &viewport) : pWidget(viewport), viewport(viewport) {}
void constructor();
};

View File

@@ -1,266 +0,0 @@
static bool HexEditor_keyPress(GtkWidget *widget, GdkEventKey *event, HexEditor *self) {
return self->keyPress(event->keyval);
}
static bool HexEditor_scroll(GtkRange *range, GtkScrollType scroll, gdouble value, HexEditor *self) {
self->scroll((unsigned)value);
return false;
}
void HexEditor::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
widget->parent = &parent;
hexEditor->size = 0;
hexEditor->offset = 0;
hexEditor->columns = 16;
hexEditor->rows = 16;
object->widget = gtk_hbox_new(false, 0);
gtk_widget_set_size_request(object->widget, width, height);
hexEditor->container = gtk_scrolled_window_new(0, 0);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(hexEditor->container), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(hexEditor->container), GTK_SHADOW_ETCHED_IN);
hexEditor->widget = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(hexEditor->widget), GTK_WRAP_NONE);
gtk_container_add(GTK_CONTAINER(hexEditor->container), hexEditor->widget);
g_signal_connect(G_OBJECT(hexEditor->widget), "key-press-event", G_CALLBACK(HexEditor_keyPress), (gpointer)this);
hexEditor->scroll = gtk_vscrollbar_new((GtkAdjustment*)0);
gtk_range_set_range(GTK_RANGE(hexEditor->scroll), 0, 256);
gtk_range_set_increments(GTK_RANGE(hexEditor->scroll), 1, 16);
gtk_widget_set_sensitive(hexEditor->scroll, false);
g_signal_connect(G_OBJECT(hexEditor->scroll), "change-value", G_CALLBACK(HexEditor_scroll), (gpointer)this);
gtk_box_pack_start(GTK_BOX(object->widget), hexEditor->container, true, true, 0);
gtk_box_pack_start(GTK_BOX(object->widget), hexEditor->scroll, false, false, 1);
object->textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(hexEditor->widget));
hexEditor->cursor = gtk_text_buffer_get_mark(object->textBuffer, "insert");
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_widget_show(hexEditor->scroll);
gtk_widget_show(hexEditor->widget);
gtk_widget_show(hexEditor->container);
gtk_widget_show(object->widget);
}
void HexEditor::setSize(unsigned size) {
hexEditor->size = size;
setScroll();
}
void HexEditor::setOffset(unsigned offset) {
hexEditor->offset = offset;
setScroll();
updateScroll();
}
void HexEditor::setColumns(unsigned columns) {
hexEditor->columns = columns;
setScroll();
}
void HexEditor::setRows(unsigned rows) {
hexEditor->rows = rows;
setScroll();
}
void HexEditor::update() {
if(!onRead) {
gtk_text_buffer_set_text(object->textBuffer, "", -1);
return;
}
unsigned position = cursorPosition();
string output;
unsigned offset = hexEditor->offset;
for(unsigned row = 0; row < hexEditor->rows; row++) {
output.append(hex<8>(offset));
output.append(" ");
string hexdata;
string ansidata = " ";
for(unsigned column = 0; column < hexEditor->columns; column++) {
if(offset < hexEditor->size) {
uint8_t data = onRead(offset++);
hexdata.append(hex<2>(data));
hexdata.append(" ");
char buffer[2] = { data >= 0x20 && data <= 0x7e ? (char)data : '.', 0 };
ansidata.append(buffer);
} else {
hexdata.append(" ");
ansidata.append(" ");
}
}
output.append(hexdata);
output.append(ansidata);
if(offset >= hexEditor->size) break;
if(row != hexEditor->rows - 1) output.append("\n");
}
gtk_text_buffer_set_text(object->textBuffer, output, -1);
if(position == 0) position = 10; //start at first position where hex values can be entered
setCursorPosition(position);
}
HexEditor::HexEditor() {
hexEditor = new HexEditor::Data;
}
//internal
bool HexEditor::keyPress(unsigned scancode) {
if(!onRead && !onWrite) return false;
unsigned position = cursorPosition();
unsigned lineWidth = 10 + (hexEditor->columns * 3) + 1 + (hexEditor->columns) + 1;
unsigned cursorY = position / lineWidth;
unsigned cursorX = position % lineWidth;
if(scancode == GDK_Home) {
setCursorPosition(cursorY * lineWidth + 10);
return true;
}
if(scancode == GDK_End) {
setCursorPosition(cursorY * lineWidth + 10 + (hexEditor->columns * 3 - 1));
return true;
}
if(scancode == GDK_Up) {
if(cursorY != 0) return false;
signed newOffset = hexEditor->offset - hexEditor->columns;
if(newOffset >= 0) {
setOffset(newOffset);
update();
}
return true;
}
if(scancode == GDK_Down) {
if(cursorY != hexEditor->rows - 1) return false;
signed newOffset = hexEditor->offset + hexEditor->columns;
if(newOffset + hexEditor->columns * hexEditor->rows - (hexEditor->columns - 1) <= hexEditor->size) {
setOffset(newOffset);
update();
}
return true;
}
if(scancode == GDK_Page_Up) {
signed newOffset = hexEditor->offset - hexEditor->columns * hexEditor->rows;
if(newOffset >= 0) {
setOffset(newOffset);
update();
} else {
setOffset(0);
update();
}
return true;
}
if(scancode == GDK_Page_Down) {
signed newOffset = hexEditor->offset + hexEditor->columns * hexEditor->rows;
for(unsigned n = 0; n < hexEditor->rows; n++) {
if(newOffset + hexEditor->columns * hexEditor->rows - (hexEditor->columns - 1) <= hexEditor->size) {
setOffset(newOffset);
update();
break;
}
newOffset -= hexEditor->columns;
}
return true;
}
//convert scancode to hex nibble
if(scancode >= '0' && scancode <= '9') scancode = scancode - '0';
else if(scancode >= 'A' && scancode <= 'F') scancode = scancode - 'A' + 10;
else if(scancode >= 'a' && scancode <= 'f') scancode = scancode - 'a' + 10;
else return false; //not a valid hex value
if(cursorX >= 10) {
//not on an offset
cursorX -= 10;
if((cursorX % 3) != 2) {
//not on a space
bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low
cursorX /= 3;
if(cursorX < hexEditor->columns) {
//not in ANSI region
unsigned offset = hexEditor->offset + (cursorY * hexEditor->columns + cursorX);
if(offset >= hexEditor->size) return false; //do not edit past end of file
uint8_t data = onRead(offset);
//write modified value
if(cursorNibble == 1) {
data = (data & 0xf0) | (scancode << 0);
} else {
data = (data & 0x0f) | (scancode << 4);
}
onWrite(offset, data);
//auto-advance cursor to next nibble/byte
position++;
if(cursorNibble && cursorX != hexEditor->columns - 1) position++;
setCursorPosition(position);
//refresh output to reflect modified data
update();
}
}
}
return true;
}
void HexEditor::scroll(unsigned position) {
unsigned rows = hexEditor->size / hexEditor->columns;
if(position >= rows) position = rows - 1;
setOffset(position * hexEditor->columns);
update();
}
void HexEditor::setScroll() {
unsigned rows = hexEditor->size / hexEditor->columns;
if(rows) rows--;
if(rows) {
gtk_range_set_range(GTK_RANGE(hexEditor->scroll), 0, rows);
gtk_widget_set_sensitive(hexEditor->scroll, true);
} else {
gtk_widget_set_sensitive(hexEditor->scroll, false);
}
}
void HexEditor::updateScroll() {
unsigned row = hexEditor->offset / hexEditor->columns;
gtk_range_set_value(GTK_RANGE(hexEditor->scroll), row);
}
unsigned HexEditor::cursorPosition() {
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(object->textBuffer, &iter, hexEditor->cursor);
return gtk_text_iter_get_offset(&iter);
}
void HexEditor::setCursorPosition(unsigned position) {
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(object->textBuffer, &iter, hexEditor->cursor);
//GTK+ will throw a hundred errors on the terminal
//if you set an iterator past the end of the text buffer
GtkTextIter endIter;
gtk_text_buffer_get_end_iter(object->textBuffer, &iter);
unsigned endPosition = gtk_text_iter_get_offset(&iter);
gtk_text_iter_set_offset(&iter, min(position, endPosition));
gtk_text_buffer_place_cursor(object->textBuffer, &iter);
}

View File

@@ -1,25 +0,0 @@
static void HorizontalSlider_change(HorizontalSlider *self) {
if(self->object->position == self->position()) return;
self->object->position = self->position();
if(self->onChange) self->onChange();
}
void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) {
object->position = 0;
length += (length == 0);
object->widget = gtk_hscale_new_with_range(0, length - 1, 1);
widget->parent = &parent;
gtk_scale_set_draw_value(GTK_SCALE(object->widget), false);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
unsigned HorizontalSlider::position() {
return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget));
}
void HorizontalSlider::setPosition(unsigned position) {
gtk_range_set_value(GTK_RANGE(object->widget), position);
}

View File

@@ -1,13 +0,0 @@
void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_label_new(text);
widget->parent = &parent;
gtk_misc_set_alignment(GTK_MISC(object->widget), 0.0, 0.5);
gtk_widget_set_size_request(object->widget, width, height);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void Label::setText(const string &text) {
gtk_label_set_text(GTK_LABEL(object->widget), text);
}

View File

@@ -1,195 +0,0 @@
static void ListBox_activate(ListBox *self) {
signed selection = -1;
if(auto position = self->selection()) selection = position();
self->listBox->selection = selection;
if(self->onActivate) self->onActivate();
}
static void ListBox_change(ListBox *self) {
signed selection = -1;
if(auto position = self->selection()) selection = position();
if(selection == self->listBox->selection) return;
self->listBox->selection = selection;
if(self->onChange) self->onChange();
}
static void ListBox_tick(GtkCellRendererToggle *cell, gchar *path_string, ListBox *self) {
unsigned index = decimal(path_string);
self->setChecked(index, !self->checked(index));
if(self->onTick) self->onTick(index);
}
void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
listBox->selection = -1;
object->widget = gtk_scrolled_window_new(0, 0);
widget->parent = &parent;
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN);
gtk_widget_set_size_request(object->widget, width, height);
lstring list;
list.split("\t", string("\t", text));
GType *v = (GType*)malloc(list.size() * sizeof(GType));
for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING);
listBox->store = gtk_list_store_newv(list.size(), v);
free(v);
object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store));
gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget);
g_object_unref(G_OBJECT(listBox->store));
for(unsigned i = 0; i < list.size(); i++) {
if(i == 0) {
listBox->column[i].renderer = gtk_cell_renderer_toggle_new();
listBox->column[i].column = gtk_tree_view_column_new_with_attributes(
"", listBox->column[i].renderer, "active", i, (void*)0
);
gtk_tree_view_column_set_resizable(listBox->column[i].column, false);
gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable);
g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this);
} else {
listBox->column[i].renderer = gtk_cell_renderer_text_new();
listBox->column[i].column = gtk_tree_view_column_new_with_attributes(
"", listBox->column[i].renderer, "text", i, (void*)0
);
gtk_tree_view_column_set_resizable(listBox->column[i].column, true);
}
listBox->column[i].label = gtk_label_new(list[i]);
gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label);
gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column);
gtk_widget_show(listBox->column[i].label);
}
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false);
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column
gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1);
g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this);
g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->subWidget);
gtk_widget_show(object->widget);
}
void ListBox::setFocused() {
gtk_widget_grab_focus(object->subWidget);
}
void ListBox::setHeaderVisible(bool visible) {
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), visible);
}
void ListBox::setCheckable(bool checkable) {
listBox->checkable = checkable;
if(object->subWidget) gtk_tree_view_column_set_visible(listBox->column[0].column, checkable);
}
void ListBox::setFont(Font &font) {
Widget::setFont(font);
unsigned columns = 1;
while(true) {
if(gtk_tree_view_get_column(GTK_TREE_VIEW(object->subWidget), columns) == 0) break;
columns++;
}
for(unsigned i = 0; i < columns; i++) {
gtk_widget_modify_font(listBox->column[i].label, font.font->font);
}
}
void ListBox::reset() {
listBox->selection = -1;
gtk_list_store_clear(GTK_LIST_STORE(listBox->store));
gtk_tree_view_set_model(GTK_TREE_VIEW(object->subWidget), GTK_TREE_MODEL(listBox->store));
//reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListBox is now empty
gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(object->widget), 0);
gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(object->widget), 0);
}
void ListBox::resizeColumnsToContent() {
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(object->subWidget));
}
void ListBox::addItem(const string &text) {
lstring list;
list.split("\t", text);
GtkTreeIter iter;
gtk_list_store_append(listBox->store, &iter);
unsigned index = 1;
foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1);
}
void ListBox::setItem(unsigned row, const string &text) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreeIter iter;
for(unsigned i = 0; i <= row; i++) {
if(i == 0) gtk_tree_model_get_iter_first(model, &iter);
else gtk_tree_model_iter_next(model, &iter);
}
lstring list;
list.split("\t", text);
unsigned index = 1;
foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1);
}
bool ListBox::checked(unsigned row) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreePath *path = gtk_tree_path_new_from_string(string(row));
GtkTreeIter iter;
bool state;
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, 0, &state, -1);
gtk_tree_path_free(path);
return state;
}
void ListBox::setChecked(unsigned row, bool checked) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreePath *path = gtk_tree_path_new_from_string(string(row));
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, path);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1);
gtk_tree_path_free(path);
}
optional<unsigned> ListBox::selection() {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter) == false) return { false, 0 };
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, 0 };
for(unsigned i = 1;; i++) {
if(gtk_tree_model_iter_next(model, &iter) == false) return { false, 0 };
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, i };
}
return { false, 0 };
}
void ListBox::setSelection(unsigned row) {
signed current = -1;
if(auto position = selection()) current = position();
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
gtk_tree_selection_unselect_all(selection);
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter) == false) return;
if(row == 0) {
gtk_tree_selection_select_iter(selection, &iter);
return;
}
for(unsigned i = 1;; i++) {
if(gtk_tree_model_iter_next(model, &iter) == false) return;
if(row == i) {
gtk_tree_selection_select_iter(selection, &iter);
return;
}
}
}
ListBox::ListBox() {
listBox = new ListBox::Data;
listBox->checkable = false;
}

View File

@@ -1,129 +0,0 @@
static void Action_setFont(GtkWidget *widget, gpointer font) {
if(font) {
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font);
}
}
}
bool Action::visible() {
return gtk_widget_get_visible(object->widget);
}
void Action::setVisible(bool visible) {
gtk_widget_set_visible(object->widget, visible);
}
bool Action::enabled() {
return gtk_widget_get_sensitive(object->widget);
}
void Action::setEnabled(bool enabled) {
gtk_widget_set_sensitive(object->widget, enabled);
}
Action::Action() {
action = new Action::Data;
action->font = 0;
}
void Menu::create(Window &parent, const string &text) {
action->font = parent.window->defaultFont;
object->menu = gtk_menu_new();
object->widget = gtk_menu_item_new_with_label(text);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_bar_append(parent.object->menu, object->widget);
gtk_widget_show(object->widget);
}
void Menu::create(Menu &parent, const string &text) {
action->font = parent.action->font;
object->menu = gtk_menu_new();
object->widget = gtk_menu_item_new_with_label(text);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
void MenuSeparator::create(Menu &parent) {
action->font = parent.action->font;
object->widget = gtk_separator_menu_item_new();
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
static void MenuItem_tick(MenuItem *self) {
if(self->onTick) self->onTick();
}
void MenuItem::create(Menu &parent, const string &text) {
action->font = parent.action->font;
object->widget = gtk_menu_item_new_with_label(text);
g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(MenuItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
static void MenuCheckItem_tick(MenuCheckItem *self) {
if(self->onTick && self->object->locked == false) self->onTick();
}
void MenuCheckItem::create(Menu &parent, const string &text) {
action->font = parent.action->font;
object->widget = gtk_check_menu_item_new_with_label(text);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuCheckItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
bool MenuCheckItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget));
}
void MenuCheckItem::setChecked(bool state) {
object->locked = true;
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), state);
object->locked = false;
}
static void MenuRadioItem_tick(MenuRadioItem *self) {
if(self->onTick && self->checked() && self->object->locked == false) self->onTick();
}
void MenuRadioItem::create(Menu &parent, const string &text) {
first = this;
action->font = parent.action->font;
object->parentMenu = &parent;
object->widget = gtk_radio_menu_item_new_with_label(0, text);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
void MenuRadioItem::create(MenuRadioItem &parent, const string &text) {
first = parent.first;
action->font = parent.action->font;
object->parentMenu = parent.object->parentMenu;
object->widget = gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(first->object->widget), text);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(object->parentMenu->object->menu), object->widget);
gtk_widget_show(object->widget);
}
bool MenuRadioItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget));
}
void MenuRadioItem::setChecked() {
object->locked = true;
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), true);
object->locked = false;
}

View File

@@ -8,13 +8,12 @@ static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons but
return MessageWindow::Response::Ok;
}
MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -22,13 +21,12 @@ MessageWindow::Response MessageWindow::information(Window &parent, const string
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -36,13 +34,12 @@ MessageWindow::Response MessageWindow::question(Window &parent, const string &te
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -50,13 +47,12 @@ MessageWindow::Response MessageWindow::warning(Window &parent, const string &tex
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));

View File

@@ -1,69 +0,0 @@
struct Object::Data {
bool locked;
GtkWidget *widget;
GtkWidget *subWidget;
GtkWidget *menuContainer;
GtkWidget *formContainer;
GtkWidget *statusContainer;
GtkWidget *menu;
GtkWidget *status;
Menu *parentMenu;
Window *parentWindow;
GtkTextBuffer *textBuffer;
unsigned position;
};
struct Font::Data {
PangoFontDescription *font;
};
struct Action::Data {
Font *font;
};
struct Widget::Data {
Window *parent;
};
struct Window::Data {
Font *defaultFont;
};
struct Canvas::Data {
uint32_t *bufferRGB;
uint32_t *bufferBGR;
unsigned pitch;
};
struct HexEditor::Data {
GtkWidget *container;
GtkWidget *widget;
GtkWidget *scroll;
GtkTextMark *cursor;
unsigned size;
unsigned offset;
unsigned columns;
unsigned rows;
};
struct ListBox::Data {
GtkListStore *store;
struct GtkColumn {
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *label;
};
linear_vector<GtkColumn> column;
bool checkable;
signed selection;
};
void Object::unused() {
}
Object::Object() {
OS::initialize();
object = new Object::Data;
object->locked = false;
}

View File

@@ -1,12 +0,0 @@
void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
object->widget = gtk_progress_bar_new();
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void ProgressBar::setPosition(unsigned position) {
position = position <= 100 ? position : 0;
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)position / 100.0);
}

View File

@@ -1,36 +0,0 @@
static void RadioBox_tick(RadioBox *self) {
if(self->onTick && self->checked() && self->object->locked == false) self->onTick();
}
void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
first = this;
object->parentWindow = &parent;
object->widget = gtk_radio_button_new_with_label(0, text);
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
first = parent.first;
object->parentWindow = parent.object->parentWindow;
object->widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(parent.object->widget), text);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this);
if(object->parentWindow->window->defaultFont) setFont(*object->parentWindow->window->defaultFont);
gtk_fixed_put(GTK_FIXED(object->parentWindow->object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
bool RadioBox::checked() {
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget));
}
void RadioBox::setChecked() {
object->locked = true;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), true);
object->locked = false;
}

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

@@ -0,0 +1,24 @@
static Settings settings;
void Settings::load() {
string path = { userpath(), ".config/phoenix/gtk.cfg" };
configuration::load(path);
}
void Settings::save() {
string path = { userpath(), ".config/" };
mkdir(path, 0755);
path.append("phoenix/");
mkdir(path, 0755);
path.append("gtk.cfg");
configuration::save(path);
}
Settings::Settings() {
attach(frameGeometryX = 4, "frameGeometryX");
attach(frameGeometryY = 24, "frameGeometryY");
attach(frameGeometryWidth = 8, "frameGeometryWidth");
attach(frameGeometryHeight = 28, "frameGeometryHeight");
attach(menuGeometryHeight = 20, "menuGeometryHeight");
attach(statusGeometryHeight = 20, "statusGeometryHeight");
}

View File

@@ -1,33 +0,0 @@
static void TextBox_activate(TextBox *self) {
if(self->onActivate) self->onActivate();
}
static void TextBox_change(TextBox *self) {
if(self->object->locked == false && self->onChange) self->onChange();
}
void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_entry_new();
widget->parent = &parent;
gtk_entry_set_text(GTK_ENTRY(object->widget), text);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(TextBox_activate), (gpointer)this);
g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(TextBox_change), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void TextBox::setEditable(bool editable) {
gtk_entry_set_editable(GTK_ENTRY(object->widget), editable);
}
string TextBox::text() {
return gtk_entry_get_text(GTK_ENTRY(object->widget));
}
void TextBox::setText(const string &text) {
object->locked = true;
gtk_entry_set_text(GTK_ENTRY(object->widget), text);
object->locked = false;
}

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