Compare commits

..
66 Commits
Author SHA1 Message Date
byuu 9fd379613a Update to bsnes v024 release.
This is an interim release between some major changes to the video mode support, which may take a long time to complete. It also fixes a bug with CGRAM access timing, re-adds the Sufami Turbo load menu, and adds support for the ST-010 coprocessor, used by F1 Race of Champions.
To load Sufami Turbo cartridges, stbios.bin must be placed inside a folder named bios in the bsnes folder. There is not currently a warning if this file is missing.
2007-10-01 09:03:44 +00:00
byuu aabf52d678 Update to bsnes v023r01? release.
Alright, I've posted the new WIP.

             Changelog:
             - CGRAM fix for WWF Super Wrestlemania
             - Updated to blargg's snes_ntsc library to version 0.2.2
             - Added ST and ST dual cart loading menu options (*)
             - Redesigned the video mode configuration panel a bit --
let me know what you think (**)

 (*) - You have to set path.bios to an -absolute- path, ./ is
currently broken and I need to fix that. So, set it to eg
"c:/path/to/bsnes/bios" where stbios.bin is inside that folder.
             (**) - The video menu obviously doesn't do anything, it's
just there for design advice / suggestions for now.

 You'll notice the icon is gone. This is because I built this version
with MinGW 4, and I'm not sure how to add the icon to MinGW apps.
You'll also notice it's ~6% faster on Core 2 processors as a result.

 The 16% speedup was only when PGO was enabled. But I can't enable
that, because it causes bsnes to crash randomly. GCC gets too risky
with its optimizations and ends up generating bad code (the GCC manual
states as much, I'm not just trying to blame problems in my app on GCC
here.)

 So, 6% is the best speedup we can do for now. Compare to v0.023
official if you like. You probably won't see the speedup on older
processors like the Pentium IV.

 EDIT: it seems like that MinGW vsnprintf problem is based on DLL
files on the local computer. Probably the MS VisualC runtime files.
The WIP works fine on my home PC (WinXP Pro), but not on my work PC
(Win2k). I'm going to have to stick with Visual C++ builds until I am
able to completely remove all sprintf-style calls from the emulator.

 If you get an error about memory at 0xffffffff which cannot be read,
you know why. Try building with Visual C++ if you have it, or maybe
there's some way to upgrade libc DLLs that the app is binding to.
Whatever DLL has vsnprintf is the one that needs to be updated, if at
all possible.

             Here's what the video config screen looks like at the
moment.

             [image]

 I tried putting the text at the top, that way there won't be any odd
gaps between the text and combo box dropdown, due to different sized
fonts on different platforms.

[No archive available]
2007-09-25 14:20:00 +00:00
byuu c9ca01fe20 Update to bsnes v023 release.
I've recently fixed a bug in bsnes that I feel is serious enough to warrant a new release, even though little else has changed.
I attempted to build this release with MinGW, but ran into problems with profiling and JMA support, so this release was built with Visual C++ once again.
Changelog:
    - Fixed serious bug in S-SMP incw and decw instructions -- fixes sound bug in Emerald Dragon
    - Added Nach's MinGW fixes -- can now be compiled with MinGW/GCC3 or MinGW/GCC4
    - Fixed const char* cast warnings in GCC 4.2, thanks to [vEX] for the feedback
    - Updated source to use latest libraries for libco, libui, etc.
    - Added new advanced options to adjust aspect ratio correction
    - Cleaned up source code a bit
2007-09-16 19:30:35 +00:00
byuu becf122aaa Update to bsnes v022r04? release.
Ok, I've posted a new WIP with the Emerald Dragon bug
fix. This time, I'm not posting the WIP publically. My last request
not to link directly to the file elsewhere was ignored, and ended up
on at least two emulation news sites (I won't mention names.) I'm not
trying to be a jerk about it, I really can't spare that kind of
bandwidth.

 If anyone has contributed any code or bug fixes, reported any
confirmed bugs, or is an emulator author I've spoken to in the past,
feel free to request a link to the WIP page from me in PM if desired.

               Sorry to everyone else. I'll have the new version out
as soon as possible.







> As for the AR advanced option, I think option 2 looks perfect.




 Done. I didn't add any sanity checks, so that people can have a
little fun with it. It's not going to be a GUI option, so only
advanced users can play with it anyway. Try setting a crazy aspect
like 3:1 and play a platform game. It's guaranteed to mess with your
mind.

 I also added the ... stuff, but I did not add the pause support back
in yet. Not sure if that'll make the next release, because it requires
some changes to the main loop functions that have been missing for a
while now. I usually like to leave more of a beta testing window
before messing with that stuff.







> byuu, you may know this, but on the front page of your web site, the
> link to your Philosophical Ramblings page is
> "http://localhost/index.php?page=articles/philosophy".




               Hahah, oops. No, I didn't notice that. I must have
copied the link from my browser instead of typing it manually.

 It's a stupid article, anyway. Didn't come out like I planned. I was
also reading some comments by Miguel de Icaza (supporting patent
pacts) and Linus Torvalds (bashing the hell out of C++ programmers),
and came across an interesting comment that got me thinking. Basically
that people who have any kind of notability really shouldn't go around
talking about things outside their specific expertise, because it
makes them look foolish when people take them too seriously (and
people do) as those two comments by de Icaza and Torvalds did. Lucky
for me I really _don't_ have any kind of notability, but it's a good
principle to adhere to anyway. Stick to talking about what I know
best. That goes against my whole personality though, so I probably
won't change at all anyway.

[No archive available]
2007-09-12 10:08:00 +00:00
byuu 1e130d7872 Update to bsnes v022r03 release.
Double post!

 I've uploaded a new WIP, which I'll make public (only to this forum,
please don't post about it anywhere else unless you mirror the file):







    http://byuu.org/files/bsnes_v022_wip03.zip




 This version adds all of Nach's MinGW fixes, updated libco, libui and
libfunctor, cleans up the code that detects if the main window has
focus or not (if not, ignore keyboard input), adds the new makefile
target win-mingw-lui, finally fixes all of the char* conversion
warnings with GCC 4.2.x (thanks [vEX]), and I'm sure there's more, but
I don't remember.

 The ZIP includes a Visual C++ generated binary, but it also works
with MinGW GCC4. It won't work with MinGW GCC3 because that one lacks
a C99-compliant vsnprintf function. You can hack your way around that
by editing src/lib/libstring_sprintf.cpp if you really want to use
MinGW GCC3.

 You may also need to change the CC / CPP variable names. I went with
the generic names mingw32-gcc and mingw32-g++, but the GCC4 binaries
have -sjlj or -dw2 appended to them. You'll also need to set
-I/path/to/directxheaders, or copy them all into
/path/to/mingw/include, since MinGW seems to ignore the include
environment variable.

 And finally, a bit of good news. It appears that MinGW GCC4 builds
binaries that are ~6% faster than Visual C++. That means with PGO
enabled, they should be at least ~16% faster than v0.022 official. If
I can figure out how to hide the ugly terminal window in the
background, I'll start making official releases with MinGW GCC4 from
now on.
2007-09-08 08:41:18 +00:00
byuu c57c733d7d Update to bsnes v022 release.
Today marks a milestone for bsnes, and possibly for SNES emulation as a whole. With this new release, bsnes' compatibility has now reached 100.0%, with zero game-specific hacks. With every last commercially released game tested by both FitzRoy and tetsuo55 for at least five minutes each, all known bugs have been resolved.
Now, needless to say, I am referring to the emulation of the base SNES unit. As many SNES cartridges contain additional coprocessors on their PCBs, there are still unplayable titles. So how can I claim compatibility of 100%? Because I don't consider special chips inside game cartridges as part of the base SNES hardware. I realize that many people enjoy these games, and I do actively attempt to emulate as many coprocessors as possible (six are supported thus far). However, coprocessors such as the SuperFX and SA-1 continue to pose very significant challenges.
So, after nearly three years of development, I've finally achieved my primary goal. But it wasn't a complete victory ... I've learned a lot over the years. Emulation accuracy is not black and white -- there are heavy costs to pay and forced tradeoffs to achieve it. I no longer believe there is only one absolute path for emulation, as I did in 2004.
So does this mean bsnes is now perfect? Of course not. There are many technical details that are not emulated correctly. This also does mean that there are no bugs, merely that there are no bugs that we are aware of. While absolute verification of 100% compatibility is obvioulsy impossible, even by actually beating every single game from start to finish, this very well should be the first time any SNES emulator could claim zero known bugs with all known games tested. I very much expect this announcement to entice many new users to begin actively searching for bugs, in an effort to discredit my above claim. My response? Go for it! I would very much appreciate any and all discovered bugs to be posted here, so that they can be verified and addressed.
One major thing that needs to be said, is that there consists of one major hack in all SNES emulators, including bsnes: the use of scanline-based PPU renderers. This necessitates global hacks in all emulators to minimize their inaccuracies. I was going to write up a very long post here, going into specifics, but I've decided an article would be a better place for that. I will hopefully be writing up this article in a few days to post here.
In the meantime, one very important issue does need to be addressed. This version fixes a bug in Uniracers 2-player mode, where the game writes to OAM during active display. Like other PPU global hacks, Uniracers required a special consession. But because this hack only affects one game, it can very fairly be seen as cheating. Suffice to say, bsnes does not contain a game-specific hack, and the change made to fix Uniracers affects all games, but I do still very much consider it to be a hack. The fix I have added is quite literally and honestly more accurate than the behavior of bsnes v0.021. Before, writes to OAM and CGRAM during active display went where a programmer would expect, which would cause bugs when ran on real hardware. Uniracers is the only game known to do this, and it is very dangerous to do so. The writes do go through, but not where one would expect. The access address basically changes as the screen is rendered. With a scanline-based PPU, it is not possible to emulate the individual 
steppings of the PPU, as there is not enough precision. Further, the entire SNES emulation community has virtually no information on how active display OAM and CGRAM writes work. Now, as Uniracers is the only game known to do this, I had the choice of either intentionally remapping the writes to an arbitrary location, or change it to the address Uniracers expects. Neither would be more accurate than the other, as both are completely wrong from a haradware standpoint. So the decision was to either fix Uniracers and deal with some calling it a game-specific hack, or to leave it broken with absolutely no gain to accuracy. Rather than decide for myself, I asked those who have supported me over the past three years for their opinions. The decision was unanimous to fix Uniracers. You can read the discussion, along with a more technical explanation of the issue, here. I will be addressing this topic in much greater detail in the article I will be writing up shortly.
Changelog:
    - Fixed buffer overflow that was manifesting as corrupted tiles in Lemmings 2
    - OAM and CGRAM addresses are now invalidated during active display, however the algorithms for how this address invalidation occurs is currently still unknown, so reads/writes are mapped to static addresses for now
    - Re-added cheat code editor.
    - Windows only: keypresses when main emulation window is not active are ignored once again
2007-08-04 19:54:35 +00:00
byuu e41aa25887 Update to bsnes v021r02? release.
I've posted a new private WIP. This one just adds the
cheat code editor back in again. Feedback on how it works is
appreciated. You'll notice it's a lot simpler than v0.019's cheat
editor ... I was going for simplicity this time. Editing a code means
deleting and re-adding it (or edit the text file directly). Yes, I
realize it's damn annoying entering codes because the emulator detects
your keypresses as controller presses (unless you're using a joypad).
Sorry, I still need to add code to determine if the active window is
the main emulator window or not for GTK+ before I can fix this on both
ports.

 Hopefully I can get that in before v0.022, but no promises. Worst
case, I'll add a dummy Window::active() function that always returns
true for GTK+ if needed.

 The cheat editor works the exact same on Windows and Linux, so this
should be the first release to allow Linux users to use it.

 Looking more at how useful bsnes is in its' current form ... I'm
simply not going to be able to walk away from it. Fuck it, I'll just
have to split the emulator and maintain two separate versions. It may
cost me some time, but whatever. It'll be good practice in trying to
streamline things to share as much code as possible. I'll keep them
together in one version as long as possible, too (using #defines and
such).

 If I do this, any suggestions on how to differentiate the two
versions? Different names? Different acronyms after bsnes (eg bsnes/AE
and bsnes/SE ...)? Different icons?







> One important point imo is the potential for "code longetivity".
> That is, I'd like the original, untouched code to continue to exist
> (while permitting derivative works) many decades from now.




 No matter the license, that won't change. People can close derived
works with PD, but it's their code that they added which becomes
closed, not mine. It won't make my code cease to be.

 It's not like it all matters that much. Regardless of license, anyone
is always free to get PD access to my code by asking. This is just me
trying to get a public consensus on whether or not I should allow
people to use my code without my permission, and to what extent I
should allow it.

 I was hoping the votes would be less 'all over the board' like the
Uniracers fix ... this vote isn't going very well. Sigh.

 It'd be annoying specifying licenses on everything. Maybe I'll just
bundle the core components (cpu, smp, ppu, dsp, memory, chip sans c4
(I don't own the rights to that)) and stick them in a separate
downloadable archive that's PD [or GPL w/permission exception]. That
way, I'm giving away the stuff that's important and can help others
the most, the emulation core. If someone can't be bothered to ask me
for mine, then they can write their own GUI. Call the package
something like 'libbsnes'. Meh.

[No archive available]
2007-08-03 08:23:00 +00:00
byuu 435f7d4371 Update to bsnes v021r01? release.
Alright, I've posted the new WIP.

             This one's really important, so please test it
thoroughly! :D
 I've ran it through my usual list of troublesome games, and
everything looks good, but it's possible I've overlooked something.

             The new config file settings are:
             ppu.hack.oam_address_invalidation
             ppu.hack.cgram_address_invalidation

 Set to true, OAM goes to 0x0218 (for Uniracers), CGRAM to 0x0000
(address is insignificant, we know of zero examples of this behavior,
so the address chosen does not matter for now). Set to false, the
writes are allowed and go where 'expected' (by programmers, not by
hardware).

 There's a slight difference in that OAM access is invalid even during
hblank, whereas CGRAM is obviously not (that's how games draw those
gradient fades and such).

             This WIP also has the Lemmings II fix.

             ---

 Now, I know I said I wouldn't bring this up again, but meh. So,
assuming I decide to go full force at this PPU renderer ... I still
want to let bsnes live on in its' current form, even if that means
losing my userbase to a competitor :(
 I'm planning for the next release to allow derivative works, in hopes
that someone will continue it. Does anyone have any objections to
that? Would it be better to use GPLv2/3 to ensure source availability
(even though I disagree with the notion of 'freedom through
restrictions' -- I liken it to becoming your enemies to defeat them),
or better to use PD to ensure the widest possible use of the code
(even if that means the source can be closed off to the public, and
the binary sold for profit -- which I also detest as immoral)? I
realize the latter means the value of all of my work will be lost, but
I never intended to profit from any of this anyway, so ...

 If you prefer GPL, please specify either v2 only, v2+ or v3. I can
use v3 and grant ZSNES an exception to use it under v2, so their v2
only license won't be a problem.

             Some examples:
             ZSNES is GPLv2, which got them the source to Zsnexbox.
 PocketNES is PD, which got the emulator used in commercial software
by Atlus, Hudson and Jaleco (though the assholes couldn't even be
bothered to send a thank you letter to the PocketNES devs).

 EDIT: I can also stick with the current license, a no-derivative one,
and do my best to maintain bsnes' old PPU renderer, if you like. But I
won't lie ... the pace of development _will_ slow down a lot on the
older version (it shouldn't affect my new PPU development speed much)
if we go with this option.

             Once again, I'll go with community opinion this time. I'm
personally not casting a vote for either.

[No archive available]
2007-08-02 08:46:00 +00:00
byuu a1980fab09 Update to bsnes v021 release.
This is a maintainence release. I am mostly releasing this for the sake of the recently released Der Langrisser translation.
Changelog:
Windows port can once again map joypads through the Input Configuration panel
Using enter or spacebar to assign a key should no longer instantly map those keys
F11 now toggles fullscreen mode
Esc now toggles menu on and off (use F11+Esc combined to hide UI completely)
Fixed a bug in King of Dragons (J, U, E), KOFF was not cleared during S-DSP power(), thanks to FitzRoy for the report, and blargg for assistance fixing the bug
Fixed serious crashing error with File->Load on Linux/amd64 port
Hopefully fixed min/max undefined error on GCC 4.2.0, but I am unable to test to verify
Fixed many cast const char* to char* warnings for GCC 4.2.0, but some probably remain, as again, I am unable to test as I lack GCC 4.2.0
Set XV_AUTO_COLORKEY to 1 for Video/Xv renderer. Should fix some video drivers where there was no output, especially after running mplayer, etc. Thanks to sinimas for the fix
Added clear_video() to Video/Xv renderer. Green edges at the bottom and right sides of the video output are now gone, and unloading a ROM will clear video
I have finally figured out how to poll the keyboard status in real-time through Xorg: the XQueryKeymap function. I will be rewriting the Linux key capture system to use this, instead of capturing window key up / down messages through GTK+. This will finally allow me to completely abstract the UI from the hardware video, audio and input interfaces: a necessary step toward Linux joypad support.
2007-06-10 19:27:46 +00:00
byuu ebb234ba5f Update to bsnes v020 01 release.
[No changelog available]
2007-06-05 15:50:59 +00:00
byuu 2cc7fe30b4 Update to bsnes v020 release.
Five months and 43 WIP releases in the making, today I am releasing bsnes v0.020. I'd really like to express my thanks to blargg, for he has written a new S-DSP emulator that is an impressive 32 times more precise than all existing S-DSP emulators. It is now bus-accurate, and should produce bit-perfect sound output to that of a real SNES, excepting very minor, very extreme edge cases. Not only did he do this, he went out of his way to develop a special version exclusively for bsnes to ease licensing concerns and take advantage of bsnes' unique features, notably cothreads. I can't thank him enough. Unfortunately, bsnes has taken a ~10% speed hit over v0.019 by using this new S-DSP emulator, but I must stress the speed hit is entirely due to the way bsnes is implemented. blargg's standalone S-DSP emulator is very, very fast. Anyone is free to take a look at his S-DSP emulator, as he has released it as open source under the LGPL, by visiting his homepage, here.
Unfortunately, the new cross-platform UI is not entirely finished. Some sacrifices had to be made to support libui. Specifically, the following features are missing from v0.019, but will hopefully be added back in future releases:
    - Fullscreen support
    - Input Configuration panel cannot capture joypad input. Joypad support is still present, but it must be mapped manually through the Advanced panel or through editing bsnes.cfg by hand
    - The Cheat Code Editor is missing, but cht files can still be used from bsnes v0.019, and created by hand
    - Sufami Turbo support is not accessible from the UI
    - The UI on Windows is slightly less polished due to compromises to allow the UI to be readable on Linux.
I am sorry for the rough edges listed above, but I wanted to get a new release out, as it has been over five months since the last release, and I really want the world to be able to experience blargg's new S-DSP emulator.
Changelog:
    - Added blargg's new S-DSP emulator, runs at 1.024mhz. Many thanks to blargg for this, as this puts all portions of SNES emulation except for the S-PPU at bus-accuracy
    - blargg's S-DSP core fixes bugs in both Koushien 2 (J) and Toy Story (U)
    - Corrected all S-SMP cycle timings to be hardware accurate. Thanks to blargg for creating an amazing test ROM that tested every possible opcode
    - Corrected S-CPU wai instruction timing, fixes Mortal Kombat II
    - Reverted HDMA sync emulation once more to fix Breath of Fire II (G) and Secret of Mana (U)
    - Completely rewrote user interface to use libui, which is a wrapper that allows the same code to produce the same UI on both Windows (through the Win32 API) and Linux (through the GTK+ API)
    - Corrected $2100.d7 OAM reset behavior, thanks to research from anomie
    - Massively revamped the Linux port, should compile with no warnings or errors now
    - Added 64-bit support to libco, tested on FreeBSD/amd64, should work on Linux as well
    - Revamped makefile with suggestions from Nach
    - Improved Linux Xv renderer to use the far more common YUY2 format, which should work on most Xorg drivers, allowing hardware accelerated video scaling
    - Completely rewrote config file system. bsnes.cfg is now saved to user's profile folder on both Windows and Linux, allowing multi-user support
    - A lot more work has been done behind the scenes, including massive code cleanups and portability improvements
You may download the new version on the main bsnes page.
2007-06-03 00:20:00 +00:00
byuu 5c3c872b78 Update to bsnes v019r41? release.
New WIP up.

 I've replaced the interface::input setup, since Visual C++ was having
problems with it. I wanted something that wasn't so seemingly directly
linked to SNESInterface, anyway. Now I have InputManager, which will
handle not only all of the joypad mappings, but the GUI shortcut keys
as well. Yes -- I finally have all the code in place to support user-
defined shortcut keys. See? Something good did come out of the rewrite
after all. Dynamic keyboard mapping works on Windows now, but there
probably won't be joypad capture support until v0.021.

 Further, I have added SHGetFolderPath to the Windows port. libbase.h
sadly requires shell32.lib now. I haven't tested this on 9x, but I
don't believe bsnes has worked on 9x in a long, long time now. I've
also heard you can copy shfolder.dll or something to use it on 9x
anyway.

 Anyway, the config file now saves in your 'Application Data' folder
on Windows, and in your local directory on Linux. There's no need to
worry about what happens when you update bsnes and don't delete the
file ... as I use a text-based config file, like ZSNES / PSR, no harm
will come of it. Old variables will be flushed out, new variables will
be added with default values upon first load of the new version.
Thanks again to Nach for the code and help with this.

             Lastly, I've added a bsnes license page. So instead of
debating whether to look up four letter English words in Perens',
Stallman's or Webster's dictionary, you can just link to that page
instead :)

Again, the license applies to current and previous versions of bsnes.
If and when it forks, the fork will likely be licensed in a way that
others can take over the old version.

 Opinions on how to fix contradictions / loopholes welcome, blanket
statements that it's totally flawed without describing why or how are
not. Thanks in advance.

[No archive available]
2007-05-30 05:00:00 +00:00
byuu 36bf915244 Update to bsnes v019r40 release.
Ok, here's a public WIP for everyone:







    http://byuu.cinnamonpirate.com/files/bsnes_v019_wip40.zip




               Please ... if you link to this post or file elsewhere,
please mirror it.

               Fixes since wip39:
               - menu enter event captured, audio no longer hangs when
entering the menu.
 - multiple click problems resolved for all menu items plus list box
controls. Behavior should now be the same on both Windows and Linux,
but further polish is definitely needed here.
               - buttons to set values on input config and advanced
panels are now disabled when no item is selected.

               Known problems:
 - Windows/VC++ port is still complaining about that
interface::input.bind() thing. I believe it is a compiler problem. I
am not working around it, as I prefer a real fix. If anyone can help,
please see src/ui/lui/settings/ui_inputconfig.cpp, look for that line.
It is #if !defined(_MSC_VER) blocked at the moment. Until this is
resolved, you must restart before input settings take effect.
 - Joypads cannot be auto polled on input config screen. You can set
the values manually on the advanced tab, they use the same values as
bsnes v019, IIRC.
 - When pressing enter (or spacebar on Linux) on the input config
panel, the dialog pops up and closes right away assigning that key. I
have no easy way to fix this, since I can't poll the realtime status
of those keys on Linux to wait for them to clear before showing the
input capture window. It would really be immensely useful to be able
to do that.
 - Linux with ati driver requires you to move the window one time to
make the image visible ... I have no idea why this is needed. nv and
nvidia drivers work fine. Use the gtk renderer if you don't like the
chroma blending that using YUY2 mode requires.
               - Linux port does not focus properly to panel list when
opening config screen.
 - Config file still saves to startup working directory, rather than
the user folder. Still planning to work on that.
 - UI is still pretty ugly on Windows, but overall it's not too bad.
Looks beautiful on Linux, though ... maybe if I could find a way to
enable theme support for Windows. I tried making a .manifest file and
using mt, and setting WINVER + _WIN32_WINNT to 0x0500, none of those
did anything.
 - Cheat code editor has not been reimplemented yet. Really the last
major thing holding back a new release, but the above are pretty
important, too.

               Let me know if anything else major pops up.







> Plus it prevents a smart user/admin from making their program
> directory read-only.




               Once again, blargg has the most convincing argument :)
               Wouldn't want that config file on a read-only medium,
eg CD-ROM.
               I was wanting to implement this on Windows anyway, but
this makes it something I simply have to do.

[No archive available]
2007-05-29 08:37:00 +00:00
byuu 045a0f7e79 Update to bsnes v019r24? release.
New WIP. This one adds a GDI renderer for windows. If
anyone wants to test it, edit bsnes.cfg and set system.video to "gdi".
It will be very slow, obviously. It's just there for the hell of it,
as another fallback I guess. I'd be interested if it didn't work for
someone.
I had to add the code to libui to support pixel buffer images, so now
I can add things like the controller art into the new lui port, and
it'll work on Windows and Linux. The best part is that I can make
these image buffers anywhere, so things like PPU VRAM / OAM / CGRAM
viewers in the debugger will now not only be possible, but trivial, to
add in the future.

 Refined libui a lot more, but I did not merge that into this bsnes
WIP, because it would break the source pretty bad. Still working on
the API, too, so I'll probably hold off a bit longer. After I get the
new libui merged in, I can start working on that configuration
settings window. That window is the only thing holding up a new
official release.

 I'm trying to figure out how the hell you enable WinXP themes now. I
tried making a manifest file, even attaching the manifest to the EXE
directly with the mt tool, but it's still drawing the controls using
the old win32 compatibility mode.







> I can see that he hesitates to add "MAXI" codes and has no multiple
> codes for any game, despite how prevalent I've found them to be.




 You have cartridges where it's the exact same game (eg bit-for-bit
identical ROM dumps), with the only difference being the PCB codes?
Care to cite an example? The last two digits may change for revisions
of the same game, obviously.

 That complicates things, but there's no harm in just picking one in
that case. If the game didn't work with that PCB, it wouldn't have
been released with it, so ...

[No archive available]
2007-04-11 12:51:00 +00:00
byuu a209e6ffbe Update to bsnes v019r23 release.
Ok, this is a very important WIP release. Note that
this file is rather large, please mirror it if you must link to it
elsewhere.







    http://byuu.cinnamonpirate.com/files/bsnes_v019_wip23.zip




               Included are two executables:
               bsnes_adsp.exe - This version uses anomie's S-DSP
emulator, clocked at 32khz
               bsnes_bdsp.exe - This version uses blargg's S-DSP
emulator, clocked at 1.024mhz

 Please note that blargg's code is experimental and in-progress. That
said, I have been unable to find any errors with it so far. I hope I
haven't missed anything blargg wanted me to do before release.
Everyone, please give your thanks to blargg for creating this emulator
and allowing me to use his code :)

 This day marks an important milestone, at least in bsnes, possibly in
the SNES emulation scene: the addition of a subsample-accurate S-DSP
emulator brings us one major step closer to the most faithful SNES
emulation that will ever be possible. Excepting bugs, this now gives
us bus-accurate S-CPU, S-SMP and S-DSP cores. It is not possible (nor
desirable) in software to get more precise than bus-level accesses.
The only core component remaining using an older, less faithful
approach is the S-PPU[1/2], and is not so coincidentally the source of
the only remaining bugs in bsnes. This will very likely be the biggest
leap forward in accuracy that will ever be seen for S-DSP emulation
from this date on.

 The old win32 interface is now completely broken, so I am forced to
distribute using lui. As such, I've fixed the NTSC/PAL mode switches,
and added software video filter selection to the UI. Any configuration
changes that are not in the menu will have to be done via the config
file for the time being. I have also added the log audio data option
back to the misc menu. If you are not able to get 60fps in bsnes, or
would like to analyze the audio output between adsp and bdsp in
another program, you can use this option. Also, I'm aware of the lui-
specific issues, such as audio repeating when entering menus. lui is
still a work in progress.

 Please test all of the games you can, and look for subtle audio
differences and the like. Bugs, improvements, whatever, would be very
useful to know. Please keep in mind that every commercial game ever
released was tested by both FitzRoy and tetsuo55, and there are
currently zero known problems with anomie's S-DSP emulator. Also note
that blargg's emulator will be slower, by nature of being more low-
level. I'll leave the decision on which core to enable by default to
you guys. Eventually, I'll have polymorphism fully functional, and
this will be a runtime-selectable option, and not require two separate
builds. But still, we unfortunately have to pick one to be the default
setting, which I hope does not offend anyone :(

 I'm very appreciative and in debt to both anomie and blargg for their
help with S-DSP emulation. They have both done a very large service to
us all by creating these cores, so I thank both of them again for all
their hard work, and for allowing me to use their work in bsnes.

[No archive available]
2007-03-07 10:27:00 +00:00
byuu 3bf672dd97 Update to bsnes v019r19? release.
Ok, added blargg's changes. Played four levels, seems to be working
fine.

 Posted a new WIP with this change. I also replaced libkeymap with a
new implementation of it, this one is designed to work with window key
messages, meaning we can finally have input configuration for GUI
events and such in the future, and Linux users will finally have input
support shortly.

             Still not now, though. Input on Windows might be a little
sketchy, as well.

             Just need to create an InputWM class for Linux.

[No archive available]
2007-03-06 10:15:00 +00:00
byuu 157ddf3e8f Update to bsnes v019r18? release.
Unfortunately, even an S/PDIF link from a real SNES isn't good enough,
as we can't verify/match its' CPU<>SMP communications. Our best bet
for verification is still the echo buffer.

I uploaded a new private WIP. This build is just demonstrating part of
the new UI. I'm trying to move back to putting everything commonly
used in the menubar, and moving all of the obscure/complex stuff out
into separate windows.

             So far, lots of stuff is still missing, and the speed
setting (not enable) doesn't work.

 How does the video mode configuration feel in this WIP compared to
v0.019's video settings panel? Easier, better, worse? I realize it
loses a bit of flexibility (eg with custom resolutions), but eh. I'd
rather go back to simplicity than feature bloat.

[No archive available]
2007-02-19 03:37:00 +00:00
byuu ea23bf53ae Update to bsnes v019r17 release.
Ok, as promised, a public WIP build:







    http://byuu.cinnamonpirate.com/files/bsnes_v019_wip17.zip




               As always, please be generous with this one. If you
must link to it elsewhere, please at least mirror it.

 This should finally take care of the Toy Story bug. Yes, the audio
should halt roughly. The developers felt the need to use an evil trick
to force the audio to release faster than it normally should.

[No archive available]
2007-02-18 04:29:00 +00:00
byuu d4598e1d01 Update to bsnes v019r13a release.
(Repost, since this got bumped by another page, but
updated message.)

 Ok, this build has TRAC's and my idea for an S-DSP EDL fix applied.
EDL writes take effect immediately, and echo index bounds checking
occurs before FIR filtering and echo buffer writes. Please test this
with all of the really really picky/sensitive audio games you're aware
of, and see if you notice a difference between this and v0.019
official. Obviously, the sound differences should only exist in echo
effects, but luckily just about every game out there uses the echo
buffer. Note any differences you find either way, but I'm particularly
interested if things get worse, which will imply this fix is incorrect
(assuming the difference is verified in hardware as being correct in
v0.019 official), and we can try out the fix idea suggested by DMV27.
If no one finds any new audio bugs, we'll assume the fix was correct.

 And no, there's no audio resampling in this. I think log audio data
might still work, if that'll make it easier. It might not, the win32
port is falling apart as I rewrite the cross-platform port.







    http://byuu.cinnamonpirate.com/files/bsnes_v019_wip13.zip




 If anyone insists on posting about this on some other site (I'd
prefer not, as always), please at least mirror the file.

 Update: audio logger works. I binary compared two files. The only
difference is that audio is being output four samples sooner now,
they're otherwise exact matches. Doesn't seem to be a bad thing by any
means.

[No archive available]
2007-02-09 08:50:00 +00:00
byuu f9a8564af0 Update to bsnes v019r11 release.
Ok, I tried my best to add the audio synchronization
method (drop video frames) yet again, and once again failed
completely.

 The below WIP is completely unusuable as it stands, so please don't
link to it, host it anywhere else, or even download it unless you can
help with the programming. I'm not going to be able to fix this myself
as I've tried countless times over the last two years in vain to fix
it.







    http://byuu.cinnamonpirate.com/files/bsnes_v019_wip11.zip




 The included config file is important: it uses the DirectDraw
renderer instead of the D3D renderer, and has triple buffering
enabled.

               The relevant code is in src/ui/video/ddraw.cpp and
src/ui/audio/dsound.cpp.

               The most important code is below, but obviously any
tests would need the above WIP to build and try out.







    void AudioDS::run(uint32 sample) {
                       uiVideo->tick();
                       data.buffer[data.buffer_pos++] = sample;

                       if(data.buffer_pos < latency)return;

                       uint32 ring_pos, pos, size;
                       do {
                       Sleep(1);
                       uiVideo->tick();
                       dsb_b->GetCurrentPosition(&pos, 0);
                       ring_pos = pos / data.ring_size;
                       } while(config::system.regulate_speed == true
    && ring_pos == data.ring_pos);

                       data.ring_pos = ring_pos;
                       void *output;
                       if(dsb_b->Lock(((data.ring_pos + 2) % 3) *
    data.ring_size,
                       data.ring_size, &output, &size, 0, 0, 0) ==
    DS_OK) {
                       //Audio::resample_hermite((uint32*)output,
    data.buffer, latency, data.buffer_pos);
                       memcpy(output, data.buffer, data.ring_size);
                       dsb_b->Unlock(output, size, 0, 0);
                       }

                       data.buffer_pos = 0;
                       }

                       bool VideoDD::lock(uint16 *&data, uint &pitch)
    {
                       if(video_buffer[video_active]->Lock(0, &ddsd,
    DDLOCK_WAIT, 0) != DD_OK) return false;
                       video_valid[video_active] = false;
                       pitch = ddsd.lPitch;
                       data = (uint16*)ddsd.lpSurface;
                       return data;
                       }

                       void VideoDD::unlock() {
                       video_buffer[video_active]->Unlock(0);
                       }

                       void VideoDD::refresh() {
                       video_valid[video_active] = true;
                       video_active ^= 1;
                       tick();
                       }

                       void VideoDD::tick() {
                       if(video_valid[0] == false && video_valid[1] ==
    false) return; //nothing to render
                       uint idx = video_valid[!video_active] == true ?
    !video_active : video_active;
                       // if(video_valid[!video_active] == false)
    return;
                       //uint idx = !video_active;

                       if(settings.triple_buffering == true) {
                       BOOL in_vblank;
                       lpdd7->GetVerticalBlankStatus(&in_vblank);
                       if(in_vblank == false) return;

                       //DWORD scanline;
                       // lpdd7->GetScanLine(&scanline);
                       // if(scanline < screen_height()) return;

                       //
    lpdd7->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
                       }

                       HRESULT hr;
                       RECT rd, rs;
                       snes.get_video_info(&vi);
                       SetRect(&rs, 0, 0, vi.width, vi.height);

                       POINT p = { 0, 0 };
                       ClientToScreen(hwnd, &p);
                       GetClientRect(hwnd, &rd);
                       OffsetRect(&rd, p.x, p.y);

                       hr = screen->Blt(&rd, video_buffer[idx], &rs,
    DDBLT_WAIT, 0);
                       video_valid[idx] = false;

                       if(hr == DDERR_SURFACELOST) {
                       screen->Restore();
                       video_buffer[0]->Restore();
                       video_buffer[1]->Restore();
                       }
                       }




               What I'm basically doing is:
 Audio keeps a ring buffer, and waits until the temporary buffer fills
up before forcing the emulator to sleep until the audio playback
catches up. Every time an audio sample is generated, and every time
the emulator sleeps for one millisecond, it gives Video a chance to
run.

 Video has two backbuffers (a poor man's triple buffering, since that
doesn't work in windowed mode for DDraw). The PPU renders the entire
screen line by line, but it doesn't go from the PPU to the video card
until Video::video_lock is called. At this time, the current buffer
sets a flag to say it's contents are invalid, then it draws to the
frame, then sets a flag saying the current contents are again valid.
Finally, it calls the Video tick function to finish.

 Every time the Video tick function is called from Audio (well over
32,000 times a second, so it should have good precision for detecting
vblank edges).

 First, it will see if any frames have completely rendered. If not, it
will give up and return. Next, it will see if "triple buffering"
(really a vsync now, but emulates triple buffering at least) is
enabled. If so, it will return and do nothing if not in vblank.
Otherwise, or if triple buffering is disabled, it will continue. Next,
it finds the most recently rendered video frame that was valid and
blits that to the screen, and then sets that frame to invalid, so that
it is not rendered again (though it wouldn't hurt, it wastes CPU time
to blit the same image twice).

 I've tried even adding in a 1ms interrupt timer to try and help with
any emulation code that might be freezing the emulator for over an
entire vblank (nothing in bsnes should be that intensive), and this
did not help either.

 Basically, it's like I'm missing an unbelievable amount of frames,
like five out of six end up never getting drawn at all, so the video
is so choppy it's completely unusable. In reality, only one frame
should be dropped every 11 seconds. And when I enable the resampler,
that should change to only one frame every 66 seconds.

 As a side note, I added a four-tap hermite resampler in. It sounds
good too, but I have no idea if it's better or worse than cubic.

[No archive available]
2007-02-03 13:10:00 +00:00
byuu 6d66b1136d Update to bsnes v019r09 release.
Alright, I'm in a semi-good mood.







    http://byuu.cinnamonpirate.com/files/bsnes_v019_wip9.zip




 This one uses the old win32 interface, and adds a new feature I'd
like people with sound troubles to try out. The config file now
contains "audio.latency". Don't mess with "audio.frequency", it won't
do you any good and gets overridden by the speed regulation settings
for now.

 The audio.latency is a precise measurement of the millisecond delay
between sound being output by a real SNES and hearing that same sound
in bsnes. It takes into account the current playback frequency, as
well as the three-ring buffering system used by bsnes' audio system.
 Formula: sample_latency = CURRENT_playback_frequency / 1000 *
config_file_latency * 3 (so 32khz + 75ms latency means each ring
buffer is 800 samples long). The new formula should make latency sound
better (it's consistent now) on fast / slow emulation speed throttling
settings as well.
 I also cut out the fourth ring, since it was redundant. This should
make bsnes appear ~25% more responsive to sound with the same buffer
latency. As a result, I increased the latency to 75ms (it was at ~45
before).
 I'd like to know what the lowest good value is that works on 95% of
sound cards, so I can use that. I'll let people with cheap sound cards
increase their latency setting manually (eventually it will be an
option in the GUI).

               **NOTE:** this version does nothing for triple
buffering/vsync/whatever. You must _disable_ triple buffering to try
out the latency settings. This version is strictly to test audio
playback support.

 I also added in my audio point resampler. Good god, it sounds
terrible. Regardless of the latency setting (either really high or
really low), the pitch difference between each audio ring is
_extremely_ noticeable. The code is there now in
src/ui/audio/dsound.cpp : AudioDS::run_videosync(), if anyone would
like to take a look. I'll hold my breath ;)

               -----

 Comparisons against ZSNES at this point are rather silly. Aside from
much more flexible timings, it probably has a nice audio resampler,
which I don't. If I faked CPU/SMP clock timings, I could get the SNES
spitting out 60 frames a second and 32khz audio a second. I'm not
going to do that, so I have to figure out how to resample the two. All
of my attempts at resampling video _and_ audio have both failed
miserably to date. I really only need one of those to work to get
smooth video+audio, but both would be nice so the user can decide
what's more important to them.

I don't care to add 2xSaI. I'm planning on redoing the filter stuff
soon to support 32-bit output for Xv, so if someone wants to add 2xSaI
support to bsnes after that, I'll add it in. Otherwise, HQ2x is
superior and Scale2x looks about the same, yet is way faster.

 Regarding the IPS thing, exactly. As I said, IPS is a bad format. You
can't tell if you need to patch against a headered or unheadered ROM
unless you read the documentation that fuckheads like Cowering remove
in their ROM sets ("at least it's already prepatched"), or try
patching twice to see which one works. UPS will eliminate both of
these problems. Readmes will be included inside the patches, and UPS
will work regardless if your ROM has a header or not. It will also be
reversible. It'll be better in every regard over IPS, so I have no
reason to support IPS.

 Lastly, I don't have any intention of working on fixing DeJap's
patch, regardless of where the problem is, as I have no way to run the
game on my copier. Maybe when and if the last two serious bugs
(Uniracers and Koushien 2) get fixed, I'll take a look at it then.
2007-01-31 00:00:24 +00:00
byuu b01f18c34c Update to bsnes v019r01? release.
First screenshots of libui in bsnes:

             [image] [image]

 Same exact codebase. The current WIP is obviously missing any
semblance of a GUI, other than the menubar and a ROM file loader.

 Lots of issues on both ports, of course. I'm aware of the audio
repeating issue on the Windows port and already know how to fix it
(had the same problem with the old Windows UI). Linux of course simply
has no audio or input.

 I'm planning on moving the framerate counter to display inside the
image, rather than on the titlebar this time. That of course won't
happen anytime soon. I don't expect to be adding fullscreen support
back in anytime soon, either.

 Once this port gets stable enough, I intend to remove the "ui/win"
and "ui/sdl" ports completely. After that, I'm going to have to start
seriously rewriting a lot of internal stuff.

 I'm also planning to go with a simpler user interface this time
around. bsnes v0.019 had too many options and features. I think I may
scale back this time and make things a lot simpler. Move a lot of the
control settings back into the menubar, rather than in the custom
options panel (which will most likely still exist).

[No archive available]
2007-01-15 04:25:00 +00:00
byuu 1ebdb69516 Update to bsnes v019 release.
I´m releasing bsnes v0.019 today. This version contains Bandai Sufami Turbo support, new IRQ emulation code, and some various bugfixes.
Unfortunately, this release is not entirely cause for celebration. Due to fatal errors in Microsoft´s "enterprise class" c++ compiler package, I am no longer able to compile bsnes with profile guided optimizations. I have tested v0.018 with and without these optimizations, and the difference is a 40% speedup when PGO is used, even more significant than I had previously believed. However, bsnes has now become too complex for Visual C++ to handle. Unfortunately, there is nothing I can do about this, except wait for Microsoft to fix their compiler.
(Warning: this paragraph contains personal opinions, skip it if you can´t handle that) As if this wasn´t enough, I´m now doing my best to wean my dependence from Microsoft´s line of operating systems, as I´m particularly concerned about the black box nature of Vista and its´ DRM control mechanisms. This isn´t a road I wish to begin traveling down, and thusly have no interest in upgrading to future versions of Windows. Therefore, as of late, I´ve been writing a UI wrapper that will allow me to code applications that are truly platform independent. The biggest goal for this library is to design a GUI for bsnes that runs virtually identically on both Windows and Linux/BSD. This is mostly complete, however there were many tricks I used in bsnes using the win32 API that I simply cannot do with GTK+ on Linux/BSD, such as the memory editor window subclassing. I will be porting bsnes to use this new UI wrapper, and in turn this will lessen the attractiveness / functionality of the bsnes UI to a certain degree.
Perhaps the most devastating news is that I am still contemplating the idea of designing a dot-based PPU renderer for bsnes. As if the loss of PGO wasn´t bad enough, this will likely eat away an unimaginable level of performance as well. I can only estimate the speed loss being between 100-500%. Yes, it will be that bad. And despite weeks of planning, I cannot think of a way to allow a scanline-based and dot-based renderer to coexist as selectable options, given their massive differences in implementation.
And let´s not even joke about SA-1 or SuperFX support ... those processors are each four to eight times more powerful than the SNES´ main CPU.
All of these speed losses will basically make bsnes mostly irrelevant as an alternative to ZSNES, SNES9x et al. Although I believe I really came close to a viable alternative with v0.018, I know that I cannot both create a mainstream emulator, as well as keep with my original goal to emulate the SNES as accurately as possible.
The past few months have been very tough for me; trying to decide which of the above two goals to pursue. I´ve still not absolutely made up my mind. But for now, I´ve been sitting on a mostly untouched version of bsnes for the last few months, and have decided to release it to the public, profile guided optimizations be damned.
I´m once again asking for help, if anyone can figure out why bsnes won´t compile with PGO support, please let me know. I´d very much like to get one last PGO build of bsnes released before starting on a dot-based PPU renderer. But given the usual response I get from these requests for help, I´d suggest no one getting their hopes up that bsnes will ever be as fast as it once was again.
The new version can be downloaded at the usual place. I´m leaving v0.018 up, as it may very well be the last stable, fast version of bsnes ever released.
2007-01-01 21:04:34 +00:00
byuu add0f74387 Update to bsnes v018r10? release.
Ok, Sufami Turbo is finished. Now I just need to add back in
cheat/patch loading, and that should do for src/cart modifications for
a while.
             Maybe I'll add split ROM support while I'm at it just for
fun.

             There's now _some_ safety code regarding ST loading, but
it's not all there. Specifically, if a file fails to load, then you
won't get any errors, the game will just not work, obviously. I now
protect against loading oversized ROMs and SRAM files.

The database now lists each Sufami Turbo cart only once, and the cart
loading code handles matching up two ROMs from the database. It is
also now possible to play ST games with invalid checksums, so eg
translations/hacks of these games should now be possible, however
unlikely.

             FF:MQ (E) is fixed now too, so we're back to
FAVOR_ACCURACY in WIP builds again.

 A little more src/cart polishing and I'm going to start documenting
all that's known about IRQs, and try and figure out how they work once
and for all (hahah, yeah right -- I give it 2-3 weeks after fixing it
again before more problems are found).

[No archive available]
2006-11-07 08:55:00 +00:00
byuu b20f70f333 Update to bsnes v018r09? release.
Ok, the new WIP is extremely fragile with ST stuff, but it should work
if you're careful.

             It took a _lot_ of rewriting to get those damn dual carts
booting. Right now, everything but the SD Gundam games are in the
database. I need to think of a way of combining those. So, the only
other dualable game is SD Ultra Battle - Ultraman Densetsu + Seven
Densetsu. Otherwise, test with just one ST cartridge at a time.

Anyway, it's definitely a work in progress, so be gentle with it. You
need "stbios.bin" in bsnes.cfg::path.bios for it to work. It probably
won't even give you an error if it isn't there.

             Suggestions for how to layout the file menu are welcome.

[No archive available]
2006-11-06 07:51:00 +00:00
byuu 9aebf7bc6b Update to bsnes v018r08? release.
Ok, the new WIP adds ppu.hack.obj_cache = [true/false], and renames
the scanline render pos to ppu.hack.scanline_render_position = [dec].

             OBJ cache defaults to off now, as two bugs are better
than four.

 Made SDP a bit more friendly to view now. I may port that style over
to my main website, too. Specifically the non fixed width part.

[No archive available]
2006-11-03 07:58:00 +00:00
byuu a7bf219d5d Update to bsnes v018r07? release.
> Overall, I have a small list of possibles. Will wait until after
> R-Type to explore.




               Damn :(
 I don't think the R-Type III fix will correct anything else. But,
cross your fingers I guess. The new WIP fixes the aforementioned game.

 My SNES tests seem to indicate that writing #$20 to $4200 when
VCOUNTER==VIRQ will trigger an IRQ, even after an IRQ has already
fired on said line. My tests today indicate that it will not trigger
an IRQ under the above circumstances when the I flag is set. I don't
know why, it's the only thing other than the final IRQ trigger test
that cares what the I flag is set to. I'm not happy with the fix, but
it's the only explanation I can come up with, and all IRQ sensitive
games are running, as well all IRQ tests are still passing. So for
now, it'll have to do.

 I'm going to attempt to document all of SNES IRQs and see if I can
figure out a more simple method of emulating them, but I'm not
hopeful.

 I also removed the "guessed" entries from my database. I've decided
not to add anything unless we definitively know its' PCB ID, or in the
case of ST games, if it doesn't have one.

 Lastly, rewrote my SDP page on my site. It now uses XHTML 1.0 + pure
CSS2, so it should be a little easier on the eyes and a lot easier to
write documentation pages for.

[No archive available]
2006-11-01 09:05:00 +00:00
byuu 04118be59a Update to bsnes v018r04 release.
Ok, _please_ be courteous to my webhost and only download this WIP if
you're going to test it on a processor that hasn't been tested thus
far.

             byuu.org/files/bsnes_v018_wip4.zip
             byuu.org/files/bsnes_tests.zip

 This has two separate builds. Neither have PGO, SSE, SSE2, ZIP or JMA
support. They are identical except for the FAVOR_ flag define and
title of the program.

             FAVOR_ACCURACY [bsnes_accurate.exe]:
             - Always tests OAM RTO flags even on skipped frames
             - Tests NMI/IRQ trigger every clock cycle

             FAVOR_SPEED [bsnes_fast.exe]:
             - Only tests OAM RTO flags on rendered frames (always
with no frameskipping)
             - Tests NMI/IRQ trigger using ranges

 If you'd like to test, please run demo_mode3.smc on both versions of
bsnes, turn off speed regulation, and report the framerate both with a
frameskip of zero and a frameskip of nine (max), along with your
processor speed.

 The other test ROMs are just to verify that IRQ behavior is still
reliable in both versions. A blue screen indicates passing, they all
pass on both versions. Don't expect test_* ROMs to pass on other
emulators, but demo_* ones should.

             Example (my main PC):
             AMD Athlon 3500+

             Accurate:
             - 121.5 fps w/o frameskipping
             - 171 fps w/max frameskipping

             Fast:
             - 146.5 fps w/o frameskipping
             - 271.5 fps w/max frameskipping

             -----

             As you can see, there are _major_ speed differences on my
A64. Personally, I'm all for accuracy, but I also want people to
actually be able to use this program in the interim. Perhaps in the
future when a low end computer is a current low-end Core 2 Duo, we can
remove all of the "speedhack" code. And in the meantime, the full 100%
precision is there for people who have the CPU power to afford it.

             -----

             If anyone wants to try and help, heh.
 src/cpu/scpu/timing/irqtiming_accurate.cpp and
src/cpu/scpu/timing/irqtiming_fast.cpp are the two versions of the IRQ
testing code. If you see any ways to optimize either (preferrably the
former, obviously), I'd greatly appreciate it. Understand that both
the CPU counters (VCOUNTER, HCLOCK) and the IRQ timing positions
(VIRQPOS, HIRQPOS) can wrap not only the horizontal clock position
(1362->0), but the vertical position as well (261->0). And also that
they are "misaligned" by 10 clocks (which is really more of an
internal CPU IC delay thing, we aren't entirely sure why the
difference is there). You probably shouldn't mess with the code if you
don't understand the implications of this on eg range testing :/
2006-10-20 03:53:34 +00:00
byuu f24d17859f Update to bsnes v018r01? release.
I've written a new scheduler for bsnes to take 100% full advantage of
cooperative multithreading. Now, bsnes only performs jumps directly
from one thread to another (CPU->SMP instead of CPU->main->SMP), and
even then only when absolutely needed (eg CPU is accessing SMP when
CPU is currently ahead of SMP).
This unfortunately makes bCPU and bSMP no longer compile. However, it
does yield some impressive speed gains. From 109fps to 125fps.
             By comparison, bsnes v0.017 yielded 128fps with my test
ROM.
 The speed gain though is dependant upon how utilized the CPU<>SMP
communication is, the difference in speed between v0.017 and my WIP
can be anywhere between 1% and 10%, with the WIP always being slower.
 The better news is that this is still without IRQs fully optimized. I
don't know how easy it will be to optimize these, if it's even doable
at all... but if I can, that would yield another very important speed
increase, making the next release the fastest ever. Here's to hoping.
 The bad news though is that cothreading's advantages are pretty much
maxed out completely now. Don't expect any future leaps in performance
from this. Still, overall... a 40% total speed increase and double the
processor synchronization precision was definitely worth the effort,
even for the potential loss of savestates.

 The scheduler should also make sPPU much faster when and if that's
ever started upon, but that's still going to take a very significant
speed hit over bPPU.

 One last benefit of the scheduler is that the new synchronization
method isn't limited to only two clocks. I can now easily add another
clock, eg for SFX/SA-1. Not that I'll be emulating either of those
within the next year or two, though. Just saying...

 I might also make two schedulers, one for cothreaded cores and one
for non-cothreaded cores. One thing is for certain though, I won't be
writing schedulers for every combination of cothreaded<>non-cothreaded
cores (there's 4 of them, CPU, SMP, PPU and DSP). And this will also
rule out run-time polymorphism's compile-time option, so expect that
to change to a compile-time only setting, meaning possibly two
versions of bsnes in the future.

 Now then, I also fixed up S-CPU emulation mode opcodes. Direct page
wrapping, stack wrapping with native mode opcodes and processor status
flag fixes. No games use emulation mode, but accuracy is always nice.

[No archive available]
2006-10-18 05:33:00 +00:00
byuu 35fd80bde7 Update to bsnes v018 release.
I began working on bsnes on October 14th, 2004. I am releasing bsnes v0.018 today to celebrate bsnes' two year anniversary. Please note that this release incurs a ~15% speed reduction since v0.017, due to IRQ and S-SMP timing improvements.
Changelog:
    - Fixed many critical errors in IRQ timing, should be *very* close to real hardware now
    - Corrected major CPU timing bug involving CPU I/O condition 4
    - Corrected bug with generic HiROM / LoROM memory maps
    - Corrected bug involving HDMA indirect channel termination [anomie]
    - OAM address reset now occurs when screen display is enabled, per recent research
    - Readded full DMA, HDMA and HDMA init bus sync timing
    - Added preliminary emulation of S-SMP $00f0 TEST register (6 of 8 bits are supported)
    - Readded emulation of known timing differences between CPU revisions 1 and 2
    - Config file can now control scanline-based PPU render position. This will only be needed until a proper dot-based PPU renderer is added
    - Removed core debugging hooks so that debugging console can remain in public releases, it now functions as a tracer and memory editor
    - Config file paths once again work correctly even if missing trailing backslash
    - Video configuration simplified, sorry in advance to those who enjoyed the profile mode used before
    - Added new configuration screen to control some emulation settings
    - Replaced bsnes program icon with a much nicer one [FitzRoy]
    - Optimized memory speed detection algorithm
    - Preliminary UPS soft-patching support (do not use this yet!)
    - Decreased memory usage and optimized generic libraries used by bsnes (/src/lib)
    - Now caching OAM by one line, somewhat similar to a real SNES. Fixes Winter Gold, but causes line rendering error in Mega lo Mania
    - Lots more, as usual
The following games have been fixed since v0.017 by the above bugfixes:
    - Battle Blaze (J, U)
    - Circuit USA (J)
    - F1 Grand Prix (J)
    - Funaki Masakatsu no Hybrid Wrestler - Tougi Denshou (J)
    - Jumbo Ozaki no Hole in One (J)
    - Mahjongg Taikai II (J)
    - RPG Tsukuru - Super Dante (J)
    - Robocop Versus The Terminator (U, E)
    - Sink or Swim (U, E)
    - Street Racer (J)
    - Touge Densetsu Saisoku Battle (J)
    - Winter Olympics (U, E)
2006-10-14 05:34:24 +00:00
byuu ccf1c00b58 Update to bsnes v017r16? release.
Ok, reverted the SPCRAM initialization pattern, which
should fix Kamen Rider SD.
 Verified DMA timing steps, I had them right. Still need to verify
HDMA/HDMA init, but they're almost definitely the same anyway.
 Also, I noticed the spc700.txt doc by anomie on romhacking.net was
more recent than mine, and had info on $00f0 - TEST o_O
 So, went ahead and added emulation for 5 out of 8 of these bits.
Notably, the CPU speed control bits and the RAM write enable bit. The
other three aren't well understood enough to add support for them just
yet.
 Now, the CPU speed control in the S-SMP means the SMP core is taking
a significant speed hit to support this register. ~5% total speed hit,
though I can probably get that number down a little with some more
optimizations. I know the register is never used by any games, but you
know how I am. I added support for it anyway.
 Note that the WIP doesn't like my inlining combination and is taking
a much more significant speed hit with global optimizations turned on,
so the WIP is ~13% slower than the last one.







> On a side note, kernel streaming method works with event
> notification per audio packet you feed into it, and that
> notification receives full precision time slices even without
> setting the timer resolution manually. At least, when I was using
> kernel streaming in my NES emulator, it didn't need vsync to output
> almost a smooth 60fps, while WaveOut mode outputs in bursts and
> requires vsync to smooth out the frames.




 If you wouldn't mind turning that into a compatible derived Audio
class, I'd love to add this as an option into bsnes :)
 It'll be drop-in and compile, so you don't have to worry about me not
adding the code this time. No problem if you don't have the time /
desire / patience to do this.
 Although, I wouldn't want to do this if it requires 3rd-party
libraries / loading a special .sys driver into the kernel space /
Windows DDK to compile / something else crazy like that.

[No archive available]
2006-10-04 06:27:00 +00:00
byuu f4520d41ec Update to bsnes v017r06? release.
New WIP should fix: RPG Tsukuru, Circuit USA, Jumbo Ozaki no Hole in
One (not a permanent fix, I'm not entirely happy with the HDMA timing,
but at least the name entry screen works again for now), and Taz-
Mania.

 The two games you said started flickering since v0.017.07 might be
fixed now, but I'm not worried about these horizontal-line issues
regardless of when they started occurring at the moment. The other
ones you said would be fixed by setting HCLOCK=256 should be fixed as
well, as this is the new default value.

 Super Mario Kart's line doesn't appear to flicker now, but I think
it's because I'm technically running the emulation a little too fast
again, due to the Ozaki fix. Another game you shouldn't expect to stay
fixed, and again another game I'm not worried about remaining fixed.

 Koushien 2 and Mahjongg Taikai 2 are very likely still broken.
Uniracers definitely is. These appear to be the only three serious
known bugs remaining.

[No archive available]
2006-09-20 04:06:00 +00:00
byuu e308cf4275 Update to bsnes v017 release.
- This version adds major accuracy improvements, countless bugfixes and DSP-1 support. At the time of this release, the only remaining known bug in bsnes is with Uniracers 2-player mode, with well over 300+ games tested.
Changelog:
    - DSP-1 support added [Andreas Naive, byuu]
    - Added cooperative multithreading library, written by myself
    - Rewritten CPU core, now bus accurate
    - Rewritten APU core, now bus accurate
    - Added cartridge database
    - Added several PCB mappers, thanks to research from Overload
    - Added several games to database, fixing several mapping-related bugs
    - Improved mirroring [Nach, grinvader, byuu]
    - vscroll bug in hires, interlaced mode fixed. Fixes RPM racing
    - RTO X=256 bug corrected. Fixes Super Conflict title screen [anomie]
    - Fixed bug in NTSC filter with hires games
    - Updated snes_ntsc to version 2.0.1 [blargg]
    - Fixed bugs in HiROM / LoROM memory mapping. Fixes countless games
    - Fixed major bugs in HDMA routine. Fixes ToP, Mortal Kombat and Genjuu Ryodan
    - Added out-of-order execution to CPU, APU synchronization for major speedup with no accuracy loss
    - IRQs are now delayed after H/DMA transfers. Fixes Wild Guns
    - HDMA transfers now kill active DMA channels that are on the same channel. Fixes Bugs Bunny and World Class Rugby. Special thanks to zones for researching this
    - CPU emulation mode accuracy was improved
    - Cleaned up port-specific code to ease porting
    - Created unified Makefile, used by all ports [Nach]
    - Created GTK+ port of bsnes (although input is currently broken)
    - WRAM is now initialized to 0x55, SRAM to 0xff. Fixes Power Drive, Death Brade and RPM Racing
    - Fixed extreme NMI / IRQ edge case. Fixes Chou Aniki
    - Adjusted PAL execution speed. Fixes Earthworm Jim 2 (E) sound effects
    - Fixed auto joypad polling bug. Fixes La Wares
    - Fixed H/DMA bug that was preventing saves from working in Secret of Evermore
    - bsnes low loads d3dx9_*.dll dynamically at runtime, it is no longer required
    - Added support for 239-line PAL mode rendering
    - As usual, there have been much more changes I've forgotten about since the last release
    - Two C4 bugs fixed. Mega Man X2 / X3 have no remaining known bugs [anomie, byuu]
2006-08-27 03:01:06 +00:00
byuu 192e53bb87 Update to bsnes v016r52 release.
bsnes now builds with no warnings on Linux:
               http://byuu.cinnamonpirate.com/images/desktop082106.png
               However, input is not working unless you build the non-
GTK+ port (see below for more info).

 I'm planning on releasing next weekend. This will likely be the last
public WIP, unless something major is found before the weekend:
               byuu.cinnamonpirate.com/files/bsnes_v016_wip52.zip <-
copy/paste link







> If you can actually get it going fast in an all-in-one window like
> that it'd be cool. I normally just punt and have the GUI separate
> from the emulator output (GTK or Qt for the UI, SDL for the output)
> but it'd be nice for my NEStopia port if I could make it "one piece"
> like the Win32 original




 I can. Please take a look at my above sourcecode, and check your
private messages for another note. Specifically, src/ui/video/sdl.cpp
and src/ui/gtk/gtk_mainwindow.cpp. I am able to merge the SDL output
into the GTK+ window by setting the environment variable
"SDL_WINDOWID=%ld", GDK_WINDOW_XWINDOW(mydrawingbox->window).
 One important thing to note is that you must not initialize SDL video
until the render window has been realized. Simply showing the window
is not enough. You need to also clear all pending events in GTK+ after
showing the window before calling SDL video init, or it will die.
               You can do that with this code:






    gtk_widget_show(mainwindow);
                       while(gtk_events_pending() == true) {
                       gtk_main_iteration_do(false);
                       }




 However, one problem I am having is that by calling
gtk_main_iteration_do(), it steals all SDL input, and I'm not able to
poll any keypresses. This happens whether I embed the SDL video output
into the GTK+ window or not. The only way to get SDL input is to
ignore all GTK+ events, effectively freezing the window completely.

               I don't suppose you'd mind sharing how you got SDL
input working with GTK+ with me?
2006-08-21 00:43:46 +00:00
byuu 0ed9edfcdb Update to bsnes v016r46 release.
wip46 up. Adds all kinds of things, please test.

 First, no more d3dx9_27.dll requirement to run the application, but
screenshots still work if you have any d3dx9_nn.dll files.
 I specifically want to know if any of the other versions (24, 30,
etc) cause the emulator to crash when use. I'm pretty sure the
function is backwards-compatible, but we should probably make sure
before I make the next release and start getting bugreports about
screenshots crashing the program.
             Note: there is no error message for failed screen
captures, I'll add that in eventually.

 Next, the video options finally enable/disable controls depending on
certain settings. Should make using the video options a little easier.

 Next, to enable SDL audio on Windows and remove the win32 port's
wMain.hwnd reference, I now pass GetDesktopWindow() to DirectSound's
SetCooperativeLevel function, since no sound comes out if you pass a
null handle. This is because I don't know how to get the window handle
from SDL, and I prefer to keep port-specific code out of there if
possible.
             Note: SDL is not a windows port, but it builds on
windows, and thus needs DirectSound to output audio on windows.
 I'm hoping this doesn't cause audio problems for anyone else, but
honestly I have no idea what DSound uses the window handle with
DSSCL_PRIORITY for anyway.

 The $2100 luminance stuff was improved by adding rounding support to
the double-to-int casts, so fades should appear a little smoother now
in games.

 Possibly fixed a bug where RTO wasn't being calculated when
brightness=0 and the screen is enabled. Didn't see any improvements in
the three known bugged games.

[No archive available]
2006-08-11 06:59:00 +00:00
byuu 764fe1974a Update to bsnes v016r44 release.
[No changelog available]
2006-08-08 02:02:38 +00:00
byuu a55d640459 Update to bsnes v016r42 release.
Ok, one semi-large change if anyone wants to test.

             byuu.cinnamonpirate.com/files/bsnes_v016_wip42.zip

 This is built for maximum speed. No debugger, PGO enabled, favor
speed, no c++ EH (so no ZIP/JMA), and a new addition: links against
msvcrt instead of libcmt.

 By using msvcrt and some evil linker hacks I was finally able to
build the SDL port again on Windows. So now I just need to focus on
cleaning that up so the next release will build on Linux out of the
box. Anyway, I tried it on the non-SDL port for the hell of it, and
noticed not only a 20% drop in EXE size, but a ~10-11% speedup as
well. Only problem is it requires msvcr80.dll, and I have no idea how
common that file is. So, that's what this wip is for. Does this
version work for you, and if it does, does it run faster? A direct FPS
comparison between v0.016 and v0.016.42 would be helpful if you're not
sure.
2006-08-04 01:27:04 +00:00
byuu 6010bffe5d Update to bsnes v016r38 release.
Ok, this WIP rewrites the input code and modifies the PAL clock speed.
Fairly major changes. Ideally, this will wipe out four bugs without
causing any new ones since wip37.

             Bug fixes :
             Earthworm Jim 2 (E) - adjusted PAL CPU clock speed.
Please test for *new* sound problems in PAL games
             La Wares (J) + Galivan 2 (J) - no longer return 0 when
auto joypad is off for polling $4218-$421f
             Super Conflict (J) - added anomie's new OAM RTO findings
to fix title screen

 The input code was almost completely rewritten to simulate real
hardware more. As such, it's very possible there are new input bugs.

             Ok, so then
byuu.cinnamonpirate.com/files/bsnes_v016_wip38.zip
 Please only download if you intend to test games and report feedback.
This version is slower than normal, lacks ZIP+JMA loading, and has the
debugger enabled (that is only useful to me, it lacks a functional
user interface) which slows down emulation even more. eg you're better
off with v0.016 official if you just want to run games.
             As always, please don't post this link anywhere else, or
I will be forced to remove the file to conserve bandwidth.

 If anyone posts bugs that hasn't tested against wip37, can I please
have someone with wip37 verify/deny the bug presence in wip37 as well
as in 016 official? wip37 isn't on my website because I don't have a
lot of web space to spare.

             Thank you to everyone in advance for helping.
2006-07-27 23:56:42 +00:00
byuu e492268025 Update to bsnes v016r27a release.
Ok, I tried converting the switch/case table to a jump table for both
CPU+APU cores. Results? EXE is 70kb larger, compile time is 5-10%
slower, and speed is identical. Needless to say I reverted that change
back. I then tried narrowing down the cause of the PGO error. Found
out it was Dai Kaijuu Monogatari. If I don't run that, I can build
with PGO. Unfortunately, this is the ROM I use to stress optimize
color add/sub. So as a result, this game will run a little slowly now
(sort of like how Chrono Trigger's OPT title screen effects were
before). But, better one game than all, right?

             byuu.org/files/bsnes_v016_wip27a.zip

 Once again, please do not submit news about this to an emulation
site. The file will be removed if I notice anyone mentioning it
anywhere.

             That will be 20-25% faster than wip27, but otherwise
everything is identical.

 DSP1: there's either a bug in op02, op06, or in the getSr/getDr/setDr
functions. We have so far been unable to spot the error and correct
it. Help is always welcome, as always. Please consider DSP-1 support
as not being there at all. I doubt any games will work right with it
right now :(

             This is how interlace works :
             I call each frame a "field", meaning even or odd fields
on your television / monitor.
             When interlace is off, I draw to the even fields every
time, so you don't notice anything.
 However, when interlace is on, I alternate between which one I draw
to each field. So depending on your frameskip, this can cause serious
problems for interlace mode. I also only physically draw to "half" the
resolution each field, much like a real TV would. This makes 512x448
mode just as fast as 512x224 mode.
 I can't think of an easy way to cheat the system with frameskipping.
Luckily, very very few games use interlace at all. Most use hires
512x224 and that's it.
2006-07-09 05:32:10 +00:00
byuu a36c26c997 Update to bsnes v016r27 release.
Here's a WIP to try out, it's 20-40% slower than it
should be, due to PGO crashing the compiler*.

               Please copy and paste link, and _do not_ post this on
emulation news sites or I will remove the file.

               byuu.org/files/bsnes_v016_wip27.zip

 Even though it's slower, could I get some people to try running
through a bunch of games and look for new bugs? Given I rewrote the
entire CPU+APU, it's possible some new bugs crept in.

               * No release this weekend. Please be sure to thank
Microsoft personally for the delay.







    rc /r /fobsnes.res bsnes.rc
                       cl /Febsnes.exe /nologo /O2 /GL /EHsc main.obj
    libco.obj libstring.obj
                       libconfig.obj libbpf.obj reader.obj cart.obj
    cheat.obj memory.obj bmemory.obj
                       cpu.obj scpu.obj bcpu.obj apu.obj sapu.obj
    bapu.obj bdsp.obj ppu.obj bppu.ob
                       j snes.obj srtc.obj sdd1.obj c4.obj dsp1.obj
    dsp2.obj obc1.obj adler32.obj co
                       mpress.obj crc32.obj deflate.obj gzio.obj
    inffast.obj inflate.obj inftrees.obj
                       ioapi.obj trees.obj unzip.obj zip.obj zutil.obj
    jma.obj jcrc32.obj lzmadec.obj
                       7zlzma.obj iiostrm.obj inbyte.obj lzma.obj
    winout.obj bsnes.res kernel32.lib use
                       r32.lib gdi32.lib comdlg32.lib comctl32.lib
    d3d9.lib d3dx9.lib ddraw.lib dsound
                       .lib dinput8.lib dxguid.lib /link
    /PGD:bsnes.pgd /LTCG:PGOPTIMIZE
                       Merging bsnes!1.pgc
                       Generating code
                       \bsnes\src\apu\sapu\core\core.cpp(16) : fatal
    error C1001: An internal er
                       ror has occurred in the compiler.
                       (compiler file
    'f:\rtm\vctools\compiler\utc\src\P2\main.c[0x10CB9ABB:0x00000025]
                       ', line 182)
                       To work around this problem, try simplifying or
    changing the program near the l
                       ocations listed above.
                       Please choose the Technical Support command on
    the Visual C++
                       Help menu, or open the Technical Support help
    file for more information

                       LINK : fatal error LNK1000: Internal error
    during IMAGE::BuildImage




               What is on sapu\core\core.cpp(16) that's too complex
for Visual c++ to handle?







    status.in_opcode = false;




               Please, if anyone can simplify that for me, let me
know.

 Seriously, though, if anyone can take a look at the source and fix
this compiler error I'd really appreciate it, and I'll get a release
out this weekend. I'm using Visual C++ 2005 Professional. Otherwise
I'll have to set it aside because I don't have time.

[No archive available]
2006-07-09 01:38:00 +00:00
byuu a3945e5772 Update to bsnes v016 release.
- Added Direct3D renderer with options for disabling hardware filtering and scanlines
    - Screenshots can now be captured in BMP, JPEG, or PNG format
    - Added config file option to specify default ROM and SRAM paths
    - Config file is always loaded from path to bsnes executable
    - Added support for analog mode joypad input
    - Up to 32 joypads can be used at once now
    - Fixed bug regarding enabling interlace mid-frame
    - Moved PPU rendering to V=240, from V=0
    - Started on new debugger. So far only debug messages and memory editor added
    - Added joypad axis resistance option for analog input mode
    - Added config file option to set window style attributes
    - Added color adjustment settings for brightness, contrast, gamma, and scanline intensity
    - Added grayscale, sepia, and invert color settings
    - Added NTSC filter by blargg, HQ2x filter by MaxSt, and Scale2x filter
    - PPU now renders scanline 224
    - Revampled about box
    - Added Game Genie / PAR cheat code support + editor, saves codes to .cht files
    - HDMA channels are no longer disabled when starting DMA, fixes Dracula X [DMV27]
    - Fixes to OAM priority mode (not perfect), fixes Final Fantasy: Mystic Quest [DMV27]
    - Fixed ENDX sound bug, fixes voices in Earthworm Jim 2 [DMV27]
    - bsnes should now compile with MinGW [DMV27]
    - Added DSP-2 support
    - Added OBC-1 support
    - Major rewrite of SNES address bus mirroring and MMIO handlers
    - Many address mirroring corrections, fixes Dezaemon, etc
    - Blocked invalid (H)DMA transfers, fixes Kirby's Super Funhouse
    - Wrote Win32 API wrapper and ported all GUI code to use it, should help to create Linux GUI later on
    - Revampled input system, should lead to customizable GUI shortcut keys later on
    - Fixed numerous bugs with input registers. Fixes many games that previous had their intro cut off (Super Conflict, etc), and many that never accepted input (Super Double Dragon, etc)
    - Moved auto joypad strobing from V=225 to V=227
    - Killed OAM table caching and window range caching, as they were actually hindering speed
    - Rewrote input configuration screen to show currently mapped keys
    - Greatly enhanced configuration options for each video profile
    - Modified fullscreen mode to exit to windowed mode when menu is activated, use F11 to toggle fullscreen mode
    - Fixed bugs in txs, wai, brk, cop, and rti opcodes [DMV27]
    - Fixed bug with emulation-mode IRQs [DMV27]
    - Initializing DMA registers to $ff [DMV27]
    - Memory writes now update CPU MDR register (open bus) [DMV27]
    - Improved ROM header detection, fixes Chou Jikuu Yousai Macross [DMV27]
    - Reading OAM no longer updates OAM latch
    - Writing to OAM high table no longer updates OAM latch
    - Writing CGRAM now updates CGRAM latch
    - Improved pseudo-hires rendering [blargg]
    - Much, much more
2006-04-25 15:51:10 +00:00
byuu 6b6233b3af Update to bsnes v015 rc3 release.
[No changelog available]
2006-04-22 01:02:32 +00:00
byuu 9f63cb1b99 Update to bsnes v015 rc2 release.
[No changelog available]
2006-04-20 00:26:54 +00:00
byuu 49c39e0e4d Update to bsnes v015 release.
- Added GZ / ZIP / JMA archive support [Nach, NSRT team]
    - Fixed bug in APU ADDW/SUBW opcode flags, thanks to DMV27, anomymous for info
    - Mosaic support is now (mostly) hardware accurate, thanks to TRAC for info
    - Fixed a bug in SC tilemap clipping, fixes Seiken Densetsu 3
    - Emulated pseudo-hires mode, uses a fairly poor color filter to simulate TV effect, the same one that SNES9x and Super Sleuth use
    - Rewrote the ROM loading code to be more port-friendly, and improved header detection
    - Added C4 emulation -- mostly correct. Only minor bugs remain, possibly not C4 related [Nach, byuu], also uses code from zsKnight, Overload, and anomie
    - Fixed noise channel generation for DSP, fixes Dual Orb 2 opening. Thanks to DMV27 for info
    - Fixed bug with DSP VxSRCN registers, fixes horrible sound corruption in Mortal Kombat 2/3
    - Modified DSP KON register reading to act according to anomie's research, while still allowing Der Langrisser, etc. to play sounds correctly
    - Fixed a bug in CPU BCD math, fixes numbers in SimEarth, thanks to DMV27 for info
    - Rewrote the windows port from scratch
    - -- Added triple buffering support (buggy)
    - -- Added DirectInput (joypad) support, allows both keyboard and joypad to be mapped to the same SNES controller button. Only one controller supported for this release, will be improved shortly
    - -- Added pause key (mapped to Pause/Break)
    - -- bsnes no longer consumes CPU time when paused or when no ROM is loaded
    - -- Updated DirectDraw to 7, and added video mode configuration options to configuration file
    - -- Video modes can specify screen width+height, refresh rate, and render width+height
    - -- Added CTRL+[1-0] hotkeys for swapping video modes
    - -- Added +/- hotkeys for adjusting frameskipping rate
    - -- Added adjustable speed regulation. There are five modes, all can be adjusted inside the configuration file. CTRL+[+/-] will adjust the speed mode.
    - -- Added PPU options to toggle any BG / OAM layers with any priority, HDMA effects, and offset per tile effects
    - -- Added option to accept invalid button combinations (up+down, left+right) to joypad config menu
    - -- bsnes now properly clears the main window when unloading games
    - [code] Made destructors for base classes virtual, so the correct destructors will be called now
2005-12-03 21:05:52 +00:00
byuu 7dec0b2a3c Update to bsnes v014 release.
This version adds speed regulation, greatly improves PPU rendering, and increases speed by ~30% over the previous version.
Changelog:
    - Rewrote offset-per-tile mode emulation, should be correct now. Fixes Chrono Trigger, Contra III, Tetris Attack, etc.
    - Fixed a bug with HDMA occuring during interrupts. Fixes Tales of Phantasia souond test screen
    - Updated compiler to Visual Studio 2005, and enabled profile guided optimizations
    - Added conditional compilation of debugging functions (faster without them)
    - Added conditional compilation of core classes as pointers (allowing polymorphism) or objects (allowing inlining). The latter results in a speed increase
    - Small fixes to BG and OAM rendering routines
    - Corrected sprite tile bounds wrapping
    - Corrected sprite rendering in hires video modes
    - Rewrote color add/sub routines, should be correct now. Fixes Illusion of Gaia menu, etc.
    - Optimized video blitting routines, will temporarilly break mixed video mode screenshots
    - Prevented selecting menu options via return key from being recognized as keypresses by the emulator
    - Added system speed regulation (60hz/NTSC or 50hz/PAL)! Many thanks to kode54, GIGO, and Richard Bannister for their assistance
I disabled the debugger and polymorphism, and enabled profile guided optimizations for this build, to maximize speed. The debugger and polymorphism can be re-enabled via uncommenting the respective #defines in src/base.h and recompiling, or bsnes v0.013 can be used. I may start releasing two separate builds in the future... not sure yet.
2005-11-12 16:49:26 +00:00
byuu f288280ceb Update to bsnes v013r02 release.
[No changelog available]
2005-10-25 23:25:28 +00:00
byuu c6c5f4669c Update to bsnes v013 release.
- Greatly improved HDMA timing and accuracy with help from anomie and DMV27 -- fixes bugs in Energy Breaker and Street Fighter Alpha 2
    - Fixed a problem with color add/sub code -- fixes opening battle in Tales of Phantasia and clouds in Energy Breaker
    - Temporarily added DMV27's bugfix for the DSP KON register -- fixes sound in Der Langrisser, but this is not a hardware-accurate fix
    - Disabled VRAM writes outside of vblank -- fixes Hook, but breaks many PD ROMs and fan translations (Roto's BS Zelda hack, Gideon Zhi's Ys 4 translation, etc). I might add an option in the future to toggle this behavior, but for now these games will no longer work. Please keep in mind these games will not run properly on real SNES hardware, either.
    - Improved frameskipping code thanks to a suggestion from Richard Bannister
    - Misc. other code cleanups and improvements (notably in the color table generation code)
    - bsnes is now endian-safe and runs on Mac OS X
    - Added caching support for window clipping tables resulting in a slight speedup. Please let me know if you spot any errors as a result of this change.
2005-10-23 23:32:30 +00:00
byuu 397b9c4505 Update to bsnes v012 release.
Changelog:
    - Added S-DSP emulation
    - Added sound output support via DirectSound -- no sound buffering though, so sound is muted by default
    - Added option to record raw sound output to WAV files
    - Added multiple color adjustment filters to the video output
    - Added mode3/4 direct color support
    - Added mode7 direct color and mosaic support
    - Greatly improved mode7 rendering algorithm thanks to anomie
    - Fixed mode7 screen repitition and EXTBG effects
    - Greatly increased accuracy of NMI and IRQ timing, and emulated many newly discovered hardware quirks involving the two
    - A few speed improvements courtesy of Nach for profiling the code for me
I'm now looking for assistance with sound buffering. Specifically, I need help modifying the DirectSound code to allow the emulator to be ran between 50%-400% normal speed, while keeping the sound output relatively good. If you have experience with this and can help, please get in touch with me (setsunakun0 at hotmail dot com).
2005-10-02 00:38:34 +00:00
byuu 7e2cfb6d40 Update to bsnes v011 release.
- Fixed Mode 0 color palette index problem. Fixes ToP, DQ5, etc.
    - Improved LoROM memory mapper to support 32mbit images. Fixes Tokimeki Memorial, etc.
    - Added full S-DD1 support, SFA2 and Star Ocean are now playable. Special thanks to Andreas Naive
    - Updated BGnxOFS / Mode7 registers with anomie's latest findings
    - Added basic ROM mirroring support. Fixes copy protection issues in MMX, etc.
    - Rewrote string library to work better on gcc/linux
    - Cleaned up S-RTC/S-DD1 emulation to make way for future add-on chip emulation
    - Rewrote DMA code, now runs cycle-by-cycle
    - Rewrote HDMA code, now allows HDMA to be enabled mid-frame, fixes many games
    - Fixed a bug in Mode7 vertical screen flip mode. Fixes FF5 title screen, etc.
    - Greatly improved IRQ triggering. Fixes Der Langrisser, etc.
    - Added full support for open bus. This includes PPU1 and PPU2 open bus support
    - Modified CPU core back to cycle-based system. Slower, but improves debugger
    - Implemented temporary fix for debugger to handle new cycle-based cores
    - Modified CGRAM to ignore highest bit, since it is not used at all by the SNES, and is impossible to read on real hardware. Lowers memory usage by ~1.2mb
    - Added mostly accurate PAL timing support. This should increase compatibility by ~30% or so
    - More stuff I'm forgetting at the moment...
2005-08-26 20:38:00 +00:00
byuu 970dcea0ac Update to bsnes v010 release.
bsnes now supports SPC700 emulation (no DSP or sound support, however), and has greatly improved compatibility. It also now contains a keyboard-only joypad configuration tool.
2005-08-03 21:22:42 +00:00
byuu 402c146a53 Update to bsnes v009 release.
- Fixed non-interlaced display modes from not drawing every other frame
    - Changed OAM halve to skip every other scanline in 224-height modes
    - Updated renderer to properly support games that switch resolutions mid-frame
    - Fixed VRAM address remapping modes, fixes DQ3R, FF: MQ
    - Fixed a bug in main color window clipping affecting BGs
    - Added video color curve option, thanks to Overload for the idea + color table
    - Added vblank, FPS counter, and DDraw surface memory options to settings menu
    - Added fullscreen modes 640x480 and 1024x768 to video modes
    - Added option to toggle the menubar on and off by pressing the escape key
    - Mode3 was not rendering sprites
    - Priorities were wrong for modes 2-4, thanks to anomie for info
    - Fixed a serious bug in IRQ interrupts. May not be perfect, but helps many games
2005-06-26 11:07:38 +00:00
byuu a471c150c9 Update to bsnes v008 release.
moving the window + main color window clipping into the bg/oam/mode7 rendering routines themselves, I was able to greatly simplify the most complicated part of rendering: the final pass where color add/sub effects are applied. As a result, the new PPU core is not only ~35% faster (on graphics intensive screens, even faster on simpler screens), but more accurate as well. Awesome.
In celebration, I´m releasing bsnes v0.008. I can actually run all games I have at >60fps on my Athlon 1.67ghz PC. Probably not something to brag about, though ...
Oh, and I also updated the keyboard polling code to only capture keypresses if the main window has focus. I´ve been meaning to do this for the better part of a year now, but never got around to it.
If, for some reason, you still want to use the old renderer, you can uncomment the first line in src/ppu/bppu/bppu.h and recompile the emulator yourself. Or you can use v0.007a, I´ll leave it up for a bit.
2005-06-21 09:13:40 +00:00
byuu ea38ea2537 Update to bsnes v007a release.
[No changelog available]
2005-06-13 04:34:10 +00:00
byuu 09b326ae86 Update to bsnes v007 release.
I have done quite a bit, so I´ll try my best to recap most of the fixes since the last release...
    - HDMA was not running during DMA transfers
    - Emulator did not recognize any filetype other than .smc
    - Added configuration file support and imported my vector/string/config libraries into bsnes
    - Added option to use system RAM instead of video RAM for display, this can greatly increase speed on certain video cards
    - Increased speed by ~15% by adding 256x224 renderer (still very buggy when the SNES mixes video modes mid-frame)
    - mvn/mvp opcodes were not setting the DB register
    - Fixed joypad input in many games (Super Mario: All Stars, Dragon Quest III, etc.)
    - Major speedup with frakeskip option
    - Fixed default aspect ratio when emulator is first started
There´s probably a lot more, but that´s all I remember offhand... this release should be a lot closer to the quality of v0.005a, but still needs a bit more polishing.
2005-06-12 08:15:22 +00:00
byuu a60f667b25 Update to bsnes v006 release.
The rewrite is now complete. I finished adding frameskip, and fixed some crashing issues with loading multiple ROMs. I wrapped all malloc/free calls with memalloc/memfree, which are custom functions that log each call, and let you pass sprintf-style arguments to them to debug memory leaks. Don´t see any, nor have I noticed any in bsnes; but it makes a nice test tool anyway.
I found out today that the mvn/mvp instructions actually set the DB register to the destination bank. Weird. This fixed Final Fantasy V, Chrono Trigger, and Dragon Quest III (which now runs as a result) graphics. Not all of them, but quite a few. I had also previously been setting the M/X flags of the P register when xce was executed. I never anticipated a game using xce while in native mode to switch to... native mode... but apparently, Chrono Trigger does. And my code was incorrect. So now Chrono Trigger gets past the name select screen, but dies a few screens after still.
The only really major flaw I am aware of that bsnes v0.005 did not have is with the battle screens in Squaresoft games. I get horrible flickering and "crushed scanlines" every other frame on them, for some reason. I´ll try and track that down for the next release, but I didn´t want to hold up a new release any longer. As a result, I´ll leave up v0.005a for the time being until I get this problem fixed.
2005-05-13 12:03:02 +00:00
byuu 6f7cf1bd31 Update to bsnes v005a release.
[No changelog available]
2005-02-06 04:04:32 +00:00
byuu 8751bc433f Update to bsnes v005 release.
Released a new version of bsnes today. Version 0.0.005 is up on the main page now.
The changes since .004 wip10 include: DRAM timing fix (now occurs mid-opcode), memory address speed fix, added CPU cycle condition 4 to all opcodes that use it, and fixed a bug where DRAM refresh was not changing start positions every other scanline.
With the new version, Chrono Trigger should get a little further, and Final Fantasy 5 should get in-game now. The only problem FF5 seems to have is sprite corrution in the menus. I´m too busy with the CPU timing to mess around with the PPU right now, though. Also, the APU is not bridged to the CPU in this release, so compatibility is (still) really low.
After I get the APU working fairly well, I´ll (try and) start keeping a what´s new file so that people can know what all changed between public releases of the emulator without having to follow this page. Sorry for the inconvenience in the mean time.

[No archive available]
2005-02-05 00:00:00 +00:00
byuu afbee40bc0 Update to bsnes v004 release.
[No changelog available]

[No archive available]
2004-12-24 00:00:00 +00:00
byuu 81e9be64d2 Update to bsnes v003r01 release.
[No changelog available]

[No archive available]
2004-12-13 00:00:00 +00:00
byuu 0cd4972d11 Update to bsnes v002 ir11 release.
Doubled the width of sprites when the width of the screen is 512. The SNES Test Program now runs through all of the character test correctly. The windows are off-by-one though.
Added the very bizarre 00:4300-00:437f memory region that is unique from 7e:4300-7e:437f. Then I added read support from $2180. This allowed the SNES Test Program to not crash after failing the electronics test, and it also fixed the horrible graphics corruption in Der Langrisser.
The fonts were still failing, so I went in with my tracer and found out what was causing it. The following code:
	gx816->regs.pc += (signed char)(arg + 2);
This is the code used to increment the pc counter after relative branches. The problem is that I had parinthesis around the + 2 as well as arg. Therefore, if arg was 0x7e, 0x7f, 0xfe, or 0xff, it would change the sign. This would in turn make the program counter off by 256 bytes. Very bad. Fixing this fixed the font in Der Langrisser, and also fixed Super Mario World, when Yoshi spits a turtle out, the game will no longer crash.
Next, via the Der Langrisser world map, I realized that anomie´s description of the BG/mode7 register latches was reversed: the BG registers (may) share a common latch between all of them, but the mode7 registers do not. I removed the common latches from the mode7 regs, but I haven´t added the latch to the BG registers yet.
I also realized that clearing the entire screen on a BG mode change would probably not be a good thing for games that change video modes mid-frame. So I tried working on getting the weird single-frame garble that pops up every minute or so to go away, but to no avail. I´m having a difficult time tracking down the exact cause of the error, but I believe it to be a buffer overflow, or something along those lines.

[No archive available]
2004-12-11 00:00:00 +00:00
byuu 938609818f Update to bsnes v002 ir9 release.
[No changelog available]
2004-12-07 01:12:32 +00:00
byuu 487491e6f2 Update to bsnes v002 ir7 release.
The bug in the Bahamut Lagoon mode7 screens turned out to be that I never added the dividend result to $4216/$4217. It always returned the multiplication of $4202/$4203. This also fixed the sprites in battles for Dai Kaijuu Monogatari. I added a bunch of window stuff, including a double size windowed mode, and a fullscreen mode. Lots of fixes to the interface, as well as code cleanup. It's still pretty buggy, there's a lot of variables to keep up with in the GUI at present...
I also added better tile caching, I just cache all tiles when they are rendered, for all 3 color depths (4, 16, 256). Seems to help a little. A portion of the speed gain was lost by fixing the dividend result, though. Very strange, since I wouldn't think much work would go into a simple line of code that's called 20 times a frame probably at best... oh well.

[No archive available]
2004-12-05 00:00:00 +00:00
byuu a6ccc7e5d2 Update to unreleased version.
First, I fixed some issues with the mode7 / scroll regs thanks to info from anomie. Qwertie's mode7 demo runs without modification now. Next, I fixed a small glitch with tiles getting cut off on the right edge of the screen, fixes some flickering on the edges in Zelda 3. Finally, I added tile caching to all 4 BG layers. It didn't really increase speed by much (~10%), and upped the RAM requirements by 1.2mb... I left the old code in case I need to fall back on it. My only guess (hope) is that the bottleneck lies somewhere else...

[No archive available]
2004-12-04 00:00:00 +00:00
byuu 39dd39cc09 Update to unreleased version.
Had a small glitch in the OAM $2102/$2103 register settings, fixed that and now both Zelda 3 and Super Mario World always show sprites perfectly. The only error to my knowledge in the below screenshots is that the large version of the mario sprite in the third pic is not shown at the top left of the screen. Which could be because I have no mosaic support. But at any rate, both games should be completely playable now, albeit very slowly. I tried to speed up things by caching some stuff in the PPU, but I couldn't find a nice way to implement it that would result in a significant speed increase. Oh yeah, Zelda won't follow you in Zelda 3, but you can just go to the throne room and push the picture to the right yourself, and she'll appear when you reach the sanctuary.
I also updated the debugger, it uses a smaller font, which is a bit harder to read, but saves a lot of screen space. This will definately be needed when I add in more of the debugging windows in the future, so it's better to get used to it now than later.

[No archive available]
2004-11-27 00:00:00 +00:00
byuu 1a017ff697 Update to unreleased version.
Added SRAM support. It's compatible with other emulators. It maps both the LoROM SRAM between $700000-$7dffff, and HiROM SRAM between $3n6000-$3n7fff (where n = 0 - f).
Modified the screen to redraw at scanline 224, instead of 0. Fixes some flickering between screens in some games.
Moved the lighting adjustment code ($2100 bits 0-3) directly into the PPU, rather than outside in the platform-specific code. This is because games have the ability to adjust this value per scanline. Also allowed the games to control the background enable ($2100 bit 7) flag per scanline now. This allowed Bahamut Lagoon's dragon intro to work.
I then added mode2 support, so now all that's missing is modes 5 and 6 (interlaced).
I also created an easy #define to select between the public domain mode7 code I wrote, and the snes9x mode7 code. I'll go through one of these days and rewrite the snes9x code in a clean environment (e.g. without using the current mode7 file as a reference) to avoid the issue altogether, but for now... the snes9x code is twice as fast, and that's good enough for me.
Lastly, I also added in a trace checkbox to enable tracing. As such, the zip file of my work up now has debug mode enabled by default. I really need an overhaul of the gui implementation, and a configuration file. I have a very nice string library system (that I wrote myself) that I would love to take advantage of in this project.
Here's some screenshots of the dragon intro. Lots of HDMA here, showing that I pretty much have HDMA down, now. I remember zsnes used to have a problem with these screens, and had to do something with an HDMA hack to get around it. They figured out what the problem was, eventually. I was actually quite surprised my emulator does not suffer from this same problem. Lucky for me, as they never did mention what the problem was to begin with. The HDMA is technically drawing too soon (at the start of the scanline instead of the end) which probably messes things up a bit, and I'm guessing the scroll registers are screwing up in one or two of the below pics. No mosaic support either.

[No archive available]
2004-11-21 00:00:00 +00:00
byuu 591adfcb25 Update to unreleased version.
Initial release

[No archive available]
2004-10-14 00:00:00 +00:00
1412 changed files with 52596 additions and 196687 deletions

No files matched your search

-93
View File
@@ -1,93 +0,0 @@
include nall/Makefile
snes := snes
profile := compatibility
ui := ui-qt
# compiler
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
flags := -O3 -fomit-frame-pointer -I. -I$(snes)
link :=
objects :=
# profile-guided instrumentation
# flags += -fprofile-generate
# link += -lgcov
# profile-guided optimization
# flags += -fprofile-use
# platform
ifeq ($(platform),x)
link += -s -ldl -lX11 -lXext
else ifeq ($(platform),osx)
else ifeq ($(platform),win)
link += -mwindows
# link += -mconsole
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
else
unknown_platform: help;
endif
# implicit rules
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(c) $(flags) $1 -c $< -o $@, \
$(if $(filter %.cpp,$<), \
$(cpp) $(flags) $1 -c $< -o $@ \
) \
) \
)
%.o: $<; $(call compile)
all: build;
include $(snes)/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 ui-qt/data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
install -D -m 644 ui-qt/data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
gconftool-2 --type bool --set /desktop/gnome/interface/menus_have_icons true
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
-@$(call delete,obj/*.o)
-@$(call delete,obj/*.a)
-@$(call delete,obj/*.so)
-@$(call delete,obj/*.dylib)
-@$(call delete,obj/*.dll)
-@$(call delete,*.res)
-@$(call delete,*.pgd)
-@$(call delete,*.pgc)
-@$(call delete,*.ilk)
-@$(call delete,*.pdb)
-@$(call delete,*.manifest)
archive-all:
tar -cjf bsnes.tar.bz2 launcher libco nall obj out phoenix ruby snes ui-phoenix ui-qt Makefile cc.bat clean.bat sync.sh
help:;
-2
View File
@@ -1,2 +0,0 @@
@mingw32-make -j 2
@pause
-1
View File
@@ -1 +0,0 @@
@mingw32-make clean
-9
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

-4
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
-2
View File
@@ -1,2 +0,0 @@
clear
g++ -std=gnu++0x -s -O3 -fomit-frame-pointer -I.. -o ../out/bsnes launcher.cpp
-54
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;
}
-2
View File
@@ -1,2 +0,0 @@
1 24 "bsnes.Manifest"
IDI_ICON1 ICON DISCARDABLE "bsnes.ico"
-104
View File
@@ -1,104 +0,0 @@
/*
libco.amd64 (2009-10-12)
author: byuu
license: public domain
*/
#define LIBCO_C
#include "libco.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local long long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (*co_swap)(cothread_t, cothread_t) = 0;
#ifdef _WIN32
//ABI: Win64
static unsigned char co_swap_function[] = {
0x48, 0x89, 0x22, 0x48, 0x8B, 0x21, 0x58, 0x48, 0x89, 0x6A, 0x08, 0x48, 0x89, 0x72, 0x10, 0x48,
0x89, 0x7A, 0x18, 0x48, 0x89, 0x5A, 0x20, 0x4C, 0x89, 0x62, 0x28, 0x4C, 0x89, 0x6A, 0x30, 0x4C,
0x89, 0x72, 0x38, 0x4C, 0x89, 0x7A, 0x40, 0x48, 0x81, 0xC2, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
0xE2, 0xF0, 0x0F, 0x29, 0x32, 0x0F, 0x29, 0x7A, 0x10, 0x44, 0x0F, 0x29, 0x42, 0x20, 0x44, 0x0F,
0x29, 0x4A, 0x30, 0x44, 0x0F, 0x29, 0x52, 0x40, 0x44, 0x0F, 0x29, 0x5A, 0x50, 0x44, 0x0F, 0x29,
0x62, 0x60, 0x44, 0x0F, 0x29, 0x6A, 0x70, 0x44, 0x0F, 0x29, 0xB2, 0x80, 0x00, 0x00, 0x00, 0x44,
0x0F, 0x29, 0xBA, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x69, 0x08, 0x48, 0x8B, 0x71, 0x10, 0x48,
0x8B, 0x79, 0x18, 0x48, 0x8B, 0x59, 0x20, 0x4C, 0x8B, 0x61, 0x28, 0x4C, 0x8B, 0x69, 0x30, 0x4C,
0x8B, 0x71, 0x38, 0x4C, 0x8B, 0x79, 0x40, 0x48, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
0xE1, 0xF0, 0x0F, 0x29, 0x31, 0x0F, 0x29, 0x79, 0x10, 0x44, 0x0F, 0x29, 0x41, 0x20, 0x44, 0x0F,
0x29, 0x49, 0x30, 0x44, 0x0F, 0x29, 0x51, 0x40, 0x44, 0x0F, 0x29, 0x59, 0x50, 0x44, 0x0F, 0x29,
0x61, 0x60, 0x44, 0x0F, 0x29, 0x69, 0x70, 0x44, 0x0F, 0x29, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x44,
0x0F, 0x29, 0xB9, 0x90, 0x00, 0x00, 0x00, 0xFF, 0xE0,
};
#include <windows.h>
void co_init() {
DWORD old_privileges;
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
}
#else
//ABI: SystemV
static unsigned char co_swap_function[] = {
0x48, 0x89, 0x26, 0x48, 0x8B, 0x27, 0x58, 0x48, 0x89, 0x6E, 0x08, 0x48, 0x89, 0x5E, 0x10, 0x4C,
0x89, 0x66, 0x18, 0x4C, 0x89, 0x6E, 0x20, 0x4C, 0x89, 0x76, 0x28, 0x4C, 0x89, 0x7E, 0x30, 0x48,
0x8B, 0x6F, 0x08, 0x48, 0x8B, 0x5F, 0x10, 0x4C, 0x8B, 0x67, 0x18, 0x4C, 0x8B, 0x6F, 0x20, 0x4C,
0x8B, 0x77, 0x28, 0x4C, 0x8B, 0x7F, 0x30, 0xFF, 0xE0,
};
#include <unistd.h>
#include <sys/mman.h>
void co_init() {
unsigned long long addr = (unsigned long long)co_swap_function;
unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
#endif
static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
size += 512; /* allocate additional space for storage */
size &= ~15; /* align stack to 16-byte boundary */
if(handle = (cothread_t)malloc(size)) {
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
*--p = (long long)crash; /* crash if entrypoint returns */
*--p = (long long)entrypoint; /* start of function */
*(long long*)handle = (long long)p; /* stack pointer */
}
return handle;
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
#ifdef __cplusplus
}
#endif
-51
View File
@@ -1,51 +0,0 @@
/*
libco.win (2008-01-28)
authors: Nach, byuu
license: public domain
*/
#define LIBCO_C
#include "libco.h"
#define WINVER 0x0400
#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local cothread_t co_active_ = 0;
static void __stdcall co_thunk(void *coentry) {
((void (*)(void))coentry)();
}
cothread_t co_active() {
if(!co_active_) {
ConvertThreadToFiber(0);
co_active_ = GetCurrentFiber();
}
return co_active_;
}
cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) {
if(!co_active_) {
ConvertThreadToFiber(0);
co_active_ = GetCurrentFiber();
}
return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry);
}
void co_delete(cothread_t cothread) {
DeleteFiber(cothread);
}
void co_switch(cothread_t cothread) {
co_active_ = cothread;
SwitchToFiber(cothread);
}
#ifdef __cplusplus
}
#endif
-23
View File
@@ -1,23 +0,0 @@
/*
libco
auto-selection module
license: public domain
*/
#if defined(__GNUC__) && defined(__i386__)
#include "x86.c"
#elif defined(__GNUC__) && defined(__amd64__)
#include "amd64.c"
#elif defined(__GNUC__) && defined(__powerpc__) && defined(__ELF__)
#include "ppc-elf.c"
#elif defined(__GNUC__)
#include "sjlj.c"
#elif defined(_MSC_VER) && defined(_M_IX86)
#include "x86.c"
#elif defined(_MSC_VER) && defined(_M_AMD64)
#include "amd64.c"
#elif defined(_MSC_VER)
#include "fiber.c"
#else
#error "libco: unsupported processor, compiler or operating system"
#endif
-34
View File
@@ -1,34 +0,0 @@
/*
libco
version: 0.15 (2009-10-12)
license: public domain
*/
#ifndef LIBCO_H
#define LIBCO_H
#ifdef LIBCO_C
#ifdef LIBCO_MP
#define thread_local __thread
#else
#define thread_local
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void* cothread_t;
cothread_t co_active();
cothread_t co_create(unsigned int, void (*)(void));
void co_delete(cothread_t);
void co_switch(cothread_t);
#ifdef __cplusplus
}
#endif
/* ifndef LIBCO_H */
#endif
-325
View File
@@ -1,325 +0,0 @@
/*
* libco.ppc-elf
* author: Kernigh
* license: public domain
*
* PowerPC 32-bit ELF implementation of libco (for compile with GCC),
* ported from PowerPC Mac OS X implementation (ppc.s) by Vas Crabb.
* This ELF version works for OpenBSD, and might also work for FreeBSD,
* NetBSD and Linux.
*
* Note 1: This implementation does not handle the AltiVec/VMX
* registers, because the ELF ABI does not mention them,
* and my OpenBSD system is not using them.
*
* Note 2: If you want position-independent code, then you must
* define __PIC__. gcc -fpic or -fPIC defines __PIC__, but
* gcc -fpie or -fPIE might not. If you want to use -fpie
* or -fPIE, then you might need a manual definition:
* gcc -fpie -D__PIC__=1
* gcc -fPIE -D__PIC__=2
*
* The ELF ABI is "System V Application Binary Interface, PowerPC
* Processor Supplement", which you can get from
* <http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf>
* (PDF file, hosted by Linux Foundation).
*
* ELF and Mac OS X use similar conventions to allocate the registers,
* and to pass arguments and return values through registers. The main
* differences are that ELF has a slightly different stack format, that
* symbols are different (and without an extra underscore at the start),
* and that the assembly syntax is different.
*
* A function may destroy the values of volatile registers, but must
* preserve the values of nonvolatile registers. So the co_switch()
* function only saves the nonvolatile registers.
*
* [nonvolatile registers in ELF]
* %r1, %r14..%r31
* %f14..%f31
* %cr2..%cr4 in cr
*
* [volatile registers in ELF]
* %r0, %r3..%r10
* %f0..%f13
* %cr0, %cr1, %cr5..%cr7 in cr
* ctr, lr, xer
*
* lr (link register) is volatile, but it contains the return address,
* so co_switch must save lr.
*
* %r13 is the small data pointer. This is constant across threads, so
* co_switch() does not touch %r13.
*
* %r2 is a reserved register, so co_switch() does not touch %r2. Some
* systems might borrow an idea from the PowerPC Embedded ABI, and might
* use %r2 as a small read-only data pointer, which is constant across
* threads.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef void * cothread_t;
/*
* co_active_context is either in a global offset table (if we are
* compiling -fPIC or -fPIE) or has an absolute position.
*/
static void *co_main_stack_pointer;
static cothread_t co_active_context = &co_main_stack_pointer;
extern cothread_t co_active() {
return co_active_context;
}
/*
* Embedded assembly.
*
* We are not using the percent-sign substitution feature,
* so we must write "%r1", not "%%r1".
*
* We always write 'bl malloc@plt', not 'bl malloc'. The '@plt'
* is necessary in position-indepent code and seems to have no
* significant effect in fixed-position code.
*
* We never use the 'lmw' or 'stmw' instructions. The ELF ABI
* mentions that these instructions "are usually slower than
* a sequence of other instructions that have the same effect."
* We instead use sequences of 'lwz' or 'stz' instructions.
*/
__asm__("\n"
"### embedded assembly \n"
".section \".text\" \n"
" .balign 4 \n"
" \n"
/*
* void co_switch(co_thread to %r3)
*
* Allocate our stack frame of 240 bytes:
* Old New Value
* 4(%r1) 244(%r1) return address, used by us
* 0(%r1) 240(%r1) frame pointer
* 232(%r1) %f31
* 224(%r1) %f30
* ...
* 96(%r1) %f14
* 92(%r1) %r31
* 88(%r1) %r30
* ...
* 24(%r1) %r14
* 20(%r1) condition register
* 8(%r1) padding of 12 bytes
* 4(%r1) return address, never used
* 0(%r1) frame pointer
*
* Save our registers in our stack frame.
* Save our stack pointer in 0(%r4).
* Switch to the stack of the other thread.
* Restore registers and return.
*/
" .globl co_switch \n"
" .type co_switch, @function \n"
"co_switch: \n"
" mflr %r0 # %r0 = return address \n"
" mfcr %r9 # %r9 = condition register \n"
" stwu %r1, -240(%r1) # allocate stack frame \n"
" \n"
" stw %r0, 244(%r1) # save return address \n"
" stfd %f31, 232(%r1) # save floating-point regs \n"
" stfd %f30, 224(%r1) \n"
" stfd %f29, 216(%r1) \n"
" stfd %f28, 208(%r1) \n"
" stfd %f27, 200(%r1) \n"
" stfd %f26, 192(%r1) \n"
" stfd %f25, 184(%r1) \n"
" stfd %f24, 176(%r1) \n"
" stfd %f23, 168(%r1) \n"
" stfd %f22, 160(%r1) \n"
" stfd %f21, 152(%r1) \n"
" stfd %f20, 144(%r1) \n"
" stfd %f19, 136(%r1) \n"
" stfd %f18, 128(%r1) \n"
" stfd %f17, 120(%r1) \n"
" stfd %f16, 112(%r1) \n"
" stfd %f16, 104(%r1) \n"
" stfd %f14, 96(%r1) \n"
" stw %r31, 92(%r1) # save general-purpose regs \n"
" stw %r30, 88(%r1) \n"
" stw %r29, 84(%r1) \n"
" stw %r28, 80(%r1) \n"
" stw %r27, 76(%r1) \n"
" stw %r26, 72(%r1) \n"
" stw %r25, 68(%r1) \n"
" stw %r24, 64(%r1) \n"
" stw %r23, 60(%r1) \n"
" stw %r22, 56(%r1) \n"
" stw %r21, 52(%r1) \n"
" stw %r20, 48(%r1) \n"
" stw %r19, 44(%r1) \n"
" stw %r18, 40(%r1) \n"
" stw %r17, 36(%r1) \n"
" stw %r16, 32(%r1) \n"
" stw %r15, 28(%r1) \n"
" stw %r14, 24(%r1) \n"
" stw %r9, 20(%r1) # save condition reg \n"
" \n"
" # save current context, set new context \n"
" # %r4 = co_active_context \n"
" # co_active_context = %r3 \n"
#if __PIC__ == 2
" # position-independent code, large model (-fPIC) \n"
" bl _GLOBAL_OFFSET_TABLE_@local-4 \n"
" mflr %r8 # %r8 = address of got \n"
" addis %r7, %r8, co_active_context@got@ha \n"
" lwz %r6, co_active_context@got@l(%r7) \n"
" lwz %r4, 0(%r6) \n"
" stw %r3, 0(%r6) \n"
#elif __PIC__ == 1
" # position-independent code, small model (-fpic) \n"
" bl _GLOBAL_OFFSET_TABLE_@local-4 \n"
" mflr %r8 # %r8 = address of got \n"
" lwz %r7, co_active_context@got(%r8) \n"
" lwz %r4, 0(%r7) \n"
" stw %r3, 0(%r7) \n"
#else
" # fixed-position code \n"
" lis %r8, co_active_context@ha \n"
" lwz %r4, co_active_context@l(%r8) \n"
" stw %r3, co_active_context@l(%r8) \n"
#endif
" \n"
" # save current stack pointer \n"
" stw %r1, 0(%r4) \n"
" # get new stack pointer \n"
" lwz %r1, 0(%r3) \n"
" \n"
" lwz %r0, 244(%r1) # get return address \n"
" lfd %f31, 232(%r1) # restore floating-point regs \n"
" lfd %f30, 224(%r1) \n"
" lfd %f29, 216(%r1) \n"
" lfd %f28, 208(%r1) \n"
" lfd %f27, 200(%r1) \n"
" lfd %f26, 192(%r1) \n"
" lfd %f25, 184(%r1) \n"
" lfd %f24, 176(%r1) \n"
" lfd %f23, 168(%r1) \n"
" lfd %f22, 160(%r1) \n"
" lfd %f21, 152(%r1) \n"
" lfd %f20, 144(%r1) \n"
" lfd %f19, 136(%r1) \n"
" lfd %f18, 128(%r1) \n"
" lfd %f17, 120(%r1) \n"
" lfd %f16, 112(%r1) \n"
" lfd %f16, 104(%r1) \n"
" lfd %f14, 96(%r1) \n"
" lwz %r31, 92(%r1) # restore general-purpose regs \n"
" lwz %r30, 88(%r1) \n"
" lwz %r29, 84(%r1) \n"
" lwz %r28, 80(%r1) \n"
" lwz %r27, 76(%r1) \n"
" lwz %r26, 72(%r1) \n"
" lwz %r25, 68(%r1) \n"
" lwz %r24, 64(%r1) \n"
" lwz %r23, 60(%r1) \n"
" lwz %r22, 56(%r1) \n"
" lwz %r21, 52(%r1) \n"
" lwz %r20, 48(%r1) \n"
" lwz %r19, 44(%r1) \n"
" lwz %r18, 40(%r1) \n"
" lwz %r17, 36(%r1) \n"
" lwz %r16, 32(%r1) \n"
" lwz %r15, 28(%r1) \n"
" lwz %r14, 24(%r1) \n"
" lwz %r9, 20(%r1) # get condition reg \n"
" \n"
" addi %r1, %r1, 240 # free stack frame \n"
" mtlr %r0 # restore return address \n"
" mtcr %r9 # restore condition register \n"
" blr # return \n"
" .size co_switch, . - co_switch \n"
" \n"
/*
* cothread_t %r3 co_create(unsigned int stack_size %r3,
* void (*coentry %r4)())
*
* Allocate a new stack, such that when you co_switch to that
* stack, then co_switch returns to coentry.
*/
" .globl co_create \n"
" .type co_create, @function \n"
"co_create: \n"
" mflr %r0 # %r0 = return address \n"
" stwu %r1, -16(%r1) # allocate my stack frame \n"
" stw %r0, 20(%r1) # save return address \n"
" stw %r31, 12(%r1) # save %r31 \n"
" stw %r30, 8(%r1) # save %r30 \n"
" \n"
" mr %r30, %r3 # %r30 = stack_size \n"
" mr %r31, %r4 # %r31 = coentry \n"
" \n"
" # Call malloc(stack_size %r3) to allocate stack; \n"
" # malloc() probably uses good alignment. \n"
" # \n"
" bl malloc@plt # returns %r3 = low end \n"
" cmpwi %r3, 0 # if returned NULL, \n"
" beq- 1f # then abort \n"
" \n"
" # we return %r3 = low end of stack \n"
" add %r4, %r3, %r30 # %r4 = high end of stack \n"
" \n"
" # uncomment if malloc() uses wrong alignment \n"
" #rlwinm %r4,%r4,0,0,27 # force 16-byte alignment \n"
" \n"
/*
* Allocate two stack frames:
* 16 bytes for stack frame with return address
* 240 bytes for co_switch stack frame
*
* Old New Value
* -8(%r4) 248(%r5) padding of 8 bytes
* -12(%r4) 244(%r5) return address = coentry
* -16(%r4) 240(%r5) frame pointer = NULL
* 232(%r5) %f31 = 0
* ...
* 20(%r5) condition register = 0
* 0(%r5) frame pointer
*/
" li %r9, (240-20)/4+1 \n"
" addi %r5, %r4, -16 # allocate first stack frame \n"
" li %r0, 0 \n"
" stwu %r5, -240(%r5) # allocate second stack frame \n"
" li %r8, 20 \n"
" mtctr %r9 # loop %r9 times \n"
"2: # loop to store zero to 20(%r5) through 240(%r5) \n"
" stwx %r0, %r5, %r8 \n"
" addi %r8, %r8, 4 # index += 4 \n"
" bdnz+ 2b # ctr -= 1, branch if nonzero \n"
" \n"
" stw %r31, 244(%r5) # return address = coentry \n"
" stw %r5, 0(%r3) # save stack pointer \n"
" \n"
" lwz %r0, 20(%r1) # get return address \n"
" lwz %r31, 12(%r1) # restore %r31 \n"
" lwz %r30, 8(%r1) # restore %r30 \n"
" mtlr %r0 # restore return address \n"
" addi %r1, %r1, 16 # free stack frame \n"
" blr # return \n"
" \n"
"1: b abort@plt # branch 1f to abort \n"
" .size co_create, . - co_create \n"
" \n"
/*
* void co_delete(cothread_t) => void free(void *)
*/
" .globl co_delete \n"
" .type co_delete, @function \n"
"co_delete: \n"
" b free@plt \n"
" \n"
);
#ifdef __cplusplus
}
#endif
-478
View File
@@ -1,478 +0,0 @@
;*****
;libco.ppc (2007-11-29)
;author: Vas Crabb
;license: public domain
;
;cross-platform PowerPC implementation of libco
;special thanks to byuu for writing the original version
;
;[ABI compatibility]
;- gcc; mac os x; ppc
;
;[nonvolatile registers]
;- GPR1, GPR13 - GPR31
;- FPR14 - FPR31
;- V20 - V31
;- VRSAVE, CR2 - CR4
;
;[volatile registers]
;- GPR0, GPR2 - GPR12
;- FPR0 - FPR13
;- V0 - V19
;- LR, CTR, XER, CR0, CR1, CR5 - CR7
;*****
;Declare some target-specific stuff
.section __TEXT,__text,regular,pure_instructions
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.machine ppc
;Constants
.cstring
.align 2
_sysctl_altivec:
.ascii "hw.optional.altivec\0"
;Declare space for variables
.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX
.lcomm _co_primary_buffer,1024,2 ;buffer (will be zeroed by loader)
.data
.align 2
_co_active_context:
.long _co_primary_buffer
.text
.align 2
;Declare exported names
.globl _co_active
.globl _co_create
.globl _co_delete
.globl _co_switch
;*****
;extern "C" cothread_t co_active();
;return = GPR3
;*****
_co_active:
mflr r0 ;GPR0 = return address
bcl 20,31,L_co_active$spb
L_co_active$spb:
mflr r2 ;GPR2 set for position-independance
addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3
lwz r3,lo16(_co_active_context-L_co_active$spb)(r3)
mtlr r0 ;LR = return address
blr ;return
;*****
;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)());
;GPR3 = heapsize
;GPR4 = coentry
;return = GPR3
;*****
_co_create:
mflr r0 ;GPR0 = return address
stmw r30,-8(r1) ;save GPR30 and GPR31
stw r0,8(r1) ;save return address
stwu r1,-(2*4+16+24)(r1) ;allocate 16 bytes for locals/parameters
;create heap space (stack + register storage)
addi r31,r3,1024-24 ;subtract space for linkage
mr r30,r4 ;GPR30 = coentry
addi r3,r3,1024 ;allocate extra memory for contextual info
bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024)
add r4,r3,r31 ;GPR4 points to top-of-stack
rlwinm r5,r4,0,0,27 ;force 16-byte alignment
;store thread entry point + registers, so that first call to co_switch will execute coentry
stw r30,8(r5) ;store entry point
addi r6,0,2+19+18*2+12*4+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE
addi r0,0,0
addi r7,0,4 ;start at 4(GPR5)
mtctr r6
L_co_create$clear_loop:
stwx r0,r5,r7 ;clear a word
addi r7,r7,-4 ;increment pointer
bdnz L_co_create$clear_loop ;loop
stwu r5,-448(r5) ;store top of stack
;initialize context memory heap and return
stw r5,0(r3) ;*cothread_t = stack heap pointer (GPR1)
lwz r1,0(r1) ;deallocate stack frame
lwz r8,8(r1) ;fetch return address
lmw r30,-8(r1) ;restore GPR30 and GPR31
mtlr r8 ;return address in LR
blr ;return
;*****
;extern "C" void co_delete(cothread_t cothread);
;GPR3 = cothread
;*****
_co_delete:
b L_free$stub ;free(GPR3)
;*****
;extern "C" void co_switch(cothread_t cothread);
;GPR3 = cothread
;*****
;
;Frame looks like:
;
;Old New Value
; 8(r1) 456(r1) Saved LR
; 4(r1) 452(r1) Saved CR
; 0(r1) 448(r1) Old GPR1
; -4(r1) 444(r1) Saved GPR31
; -8(r1) 440(r1) Saved GPR30
;... ... ...
; -72(r1) 376(r1) Saved GPR14
; -76(r1) 372(r1) Saved GPR13
; -80(r1) 368(r1) Saved VRSAVE
; -84(r1) 364(r1) +++
; -88(r1) 360(r1) Saved FPR31
; -92(r1) 356(r1) +++
; -96(r1) 352(r1) Saved FPR30
;... ... ...
;-212(r1) 236(r1) +++
;-216(r1) 232(r1) Saved FPR15
;-220(r1) 228(r1) +++
;-224(r1) 224(r1) Saved FPR14
;-228(r1) 220(r1) +++ value
;-232(r1) 216(r1) +++ len
;-236(r1) 212(r1) +++
;-240(r1) 208(r1) Saved VR31
;-244(r1) 204(r1) +++
;-248(r1) 200(r1) +++
;-252(r1) 196(r1) +++
;-256(r1) 192(r1) Saved VR30
;... ... ...
;-388(r1) 60(r1) +++
;-392(r1) 56(r1) +++
;-396(r1) 52(r1) +++
;-400(r1) 48(r1) Saved VR21
;-404(r1) 44(r1) +++
;-408(r1) 40(r1) +++ Param 5 (GPR7)
;-412(r1) 36(r1) +++ Param 4 (GPR6)
;-416(r1) 32(r1) Saved VR20 Param 3 (GPR5)
;-420(r1) 28(r1) - Param 2 (GPR4)
;-424(r1) 24(r1) - Param 1 (GPR3)
;-428(r1) 20(r1) - Reserved
;-432(r1) 16(r1) - Reserved
;-436(r1) 12(r1) - Reserved
;-440(r1) 8(r1) - New LR
;-444(r1) 4(r1) - New CR
;-448(r1) 0(r1) Saved GPR1
_co_switch:
stmw r13,-76(r1) ;save preserved GPRs
stfd f14,-224(r1) ;save preserved FPRs
stfd f15,-216(r1)
stfd f16,-208(r1)
stfd f17,-200(r1)
stfd f18,-192(r1)
stfd f19,-184(r1)
stfd f20,-176(r1)
stfd f21,-168(r1)
stfd f22,-160(r1)
stfd f23,-152(r1)
stfd f24,-144(r1)
stfd f25,-136(r1)
stfd f26,-128(r1)
stfd f27,-120(r1)
stfd f28,-112(r1)
stfd f29,-104(r1)
stfd f30,-96(r1)
stfd f31,-88(r1)
mflr r0 ;save return address
stw r0,8(r1)
mfcr r2 ;save condition codes
stw r2,4(r1)
stwu r1,-448(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE)
mr r30,r3 ;save new context pointer
bcl 20,31,L_co_switch$spb ;get address of co_active_context
L_co_switch$spb:
mflr r31
addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags
lwz r8,lo16(_co_environ-L_co_switch$spb)(r29)
andis. r9,r8,0x8000 ;is it initialised?
bne+ L_co_switch$initialised
addi r0,0,4 ;len = sizeof(int)
stw r0,216(r1)
addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec"
addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb)
addi r4,r1,220 ;GPR4 = &value
addi r5,r1,216 ;GPR5 = &len
addi r6,0,0 ;newp = 0
addi r7,0,0 ;newlen = 0
bl L_sysctlbyname$stub ;call sysctlbyname
lwz r2,220(r1) ;fetch result
addis r8,0,0x8000 ;set initialised bit
cmpwi cr5,r3,0 ;assume error means not present
cmpwi cr6,r2,0 ;test result
blt- cr5,L_co_switch$store_environ
beq cr6,L_co_switch$store_environ
oris r8,r8,0x4000 ;set the flag to say we have it!
L_co_switch$store_environ:
stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags
L_co_switch$initialised:
andis. r10,r8,0x4000 ;do we have Altivec/VMX?
beq L_co_switch$save_no_vmx
mfspr r11,256 ;save VRSAVE
andi. r0,r11,0x0FFF ;short-circuit if it's zero
stw r11,368(r1)
beq L_co_switch$save_no_vmx
andi. r0,r11,0x0800 ;check bit 20
addi r2,0,32 ;starting index
beq L_co_switch$save_skip_vr20
stvx v20,r1,r2 ;save VR20
L_co_switch$save_skip_vr20:
addi r2,r2,16 ;stride
andi. r0,r11,0x0400 ;check bit 21
beq L_co_switch$save_skip_vr21
stvx v21,r1,r2 ;save VR21
L_co_switch$save_skip_vr21:
addi r2,r2,16 ;stride
andi. r0,r11,0x0200 ;check bit 22
beq L_co_switch$save_skip_vr22
stvx v22,r1,r2 ;save VR22
L_co_switch$save_skip_vr22:
addi r2,r2,16 ;stride
andi. r0,r11,0x0100 ;check bit 23
beq L_co_switch$save_skip_vr23
stvx v23,r1,r2 ;save VR23
L_co_switch$save_skip_vr23:
addi r2,r2,16 ;stride
andi. r0,r11,0x0080 ;check bit 24
beq L_co_switch$save_skip_vr24
stvx v24,r1,r2 ;save VR24
L_co_switch$save_skip_vr24:
addi r2,r2,16 ;stride
andi. r0,r11,0x0040 ;check bit 25
beq L_co_switch$save_skip_vr25
stvx v25,r1,r2 ;save VR25
L_co_switch$save_skip_vr25:
addi r2,r2,16 ;stride
andi. r0,r11,0x0020 ;check bit 26
beq L_co_switch$save_skip_vr26
stvx v26,r1,r2 ;save VR26
L_co_switch$save_skip_vr26:
addi r2,r2,16 ;stride
andi. r0,r11,0x0010 ;check bit 27
beq L_co_switch$save_skip_vr27
stvx v27,r1,r2 ;save VR27
L_co_switch$save_skip_vr27:
addi r2,r2,16 ;stride
andi. r0,r11,0x0008 ;check bit 28
beq L_co_switch$save_skip_vr28
stvx v28,r1,r2 ;save VR28
L_co_switch$save_skip_vr28:
addi r2,r2,16 ;stride
andi. r0,r11,0x0004 ;check bit 29
beq L_co_switch$save_skip_vr29
stvx v29,r1,r2 ;save VR29
L_co_switch$save_skip_vr29:
addi r2,r2,16 ;stride
andi. r0,r11,0x0002 ;check bit 30
beq L_co_switch$save_skip_vr30
stvx v30,r1,r2 ;save VR30
L_co_switch$save_skip_vr30:
addi r2,r2,16 ;stride
andi. r0,r11,0x0001 ;check bit 31
beq L_co_switch$save_skip_vr31
stvx v31,r1,r2 ;save VR31
L_co_switch$save_skip_vr31:
L_co_switch$save_no_vmx:
addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context
lwz r5,lo16(_co_active_context-L_co_switch$spb)(r4)
stw r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context
stw r1,0(r5) ;save current stack pointer
lwz r1,0(r30) ;get new stack pointer
andis. r10,r8,0x4000 ;do we have Altivec/VMX?
beq L_co_switch$restore_no_vmx
lwz r11,368(r1) ;restore VRSAVE
andi. r0,r11,0x0FFF ;short-circuit if it's zero
mtspr 256,r11
beq L_co_switch$restore_no_vmx
andi. r0,r11,0x0800 ;check bit 20
addi r2,0,32 ;starting index
beq L_co_switch$restore_skip_vr20
lvx v20,r1,r2 ;restore VR20
L_co_switch$restore_skip_vr20:
addi r2,r2,16 ;stride
andi. r0,r11,0x0400 ;check bit 21
beq L_co_switch$restore_skip_vr21
lvx v21,r1,r2 ;restore VR21
L_co_switch$restore_skip_vr21:
addi r2,r2,16 ;stride
andi. r0,r11,0x0200 ;check bit 22
beq L_co_switch$restore_skip_vr22
lvx v22,r1,r2 ;restore VR22
L_co_switch$restore_skip_vr22:
addi r2,r2,16 ;stride
andi. r0,r11,0x0100 ;check bit 23
beq L_co_switch$restore_skip_vr23
lvx v23,r1,r2 ;restore VR23
L_co_switch$restore_skip_vr23:
addi r2,r2,16 ;stride
andi. r0,r11,0x0080 ;check bit 24
beq L_co_switch$restore_skip_vr24
lvx v24,r1,r2 ;restore VR24
L_co_switch$restore_skip_vr24:
addi r2,r2,16 ;stride
andi. r0,r11,0x0040 ;check bit 25
beq L_co_switch$restore_skip_vr25
lvx v25,r1,r2 ;restore VR25
L_co_switch$restore_skip_vr25:
addi r2,r2,16 ;stride
andi. r0,r11,0x0020 ;check bit 26
beq L_co_switch$restore_skip_vr26
lvx v26,r1,r2 ;restore VR26
L_co_switch$restore_skip_vr26:
addi r2,r2,16 ;stride
andi. r0,r11,0x0010 ;check bit 27
beq L_co_switch$restore_skip_vr27
lvx v27,r1,r2 ;restore VR27
L_co_switch$restore_skip_vr27:
addi r2,r2,16 ;stride
andi. r0,r11,0x0008 ;check bit 28
beq L_co_switch$restore_skip_vr28
lvx v28,r1,r2 ;restore VR28
L_co_switch$restore_skip_vr28:
addi r2,r2,16 ;stride
andi. r0,r11,0x0004 ;check bit 29
beq L_co_switch$restore_skip_vr29
lvx v29,r1,r2 ;restore VR29
L_co_switch$restore_skip_vr29:
addi r2,r2,16 ;stride
andi. r0,r11,0x0002 ;check bit 30
beq L_co_switch$restore_skip_vr30
lvx v30,r1,r2 ;restore VR30
L_co_switch$restore_skip_vr30:
addi r2,r2,16 ;stride
andi. r0,r11,0x0001 ;check bit 31
beq L_co_switch$restore_skip_vr31
lvx v31,r1,r2 ;restore VR31
L_co_switch$restore_skip_vr31:
L_co_switch$restore_no_vmx:
lwz r1,0(r1) ;deallocate stack frame
lwz r6,8(r1) ;return address in GPR6
lwz r7,4(r1) ;condition codes in GPR7
addi r0,0,0 ;make thread main crash if it returns
lmw r13,-76(r1) ;restore preserved GPRs
lfd f14,-224(r1) ;restore preserved FPRs
lfd f15,-216(r1)
lfd f16,-208(r1)
lfd f17,-200(r1)
lfd f18,-192(r1)
lfd f19,-184(r1)
lfd f20,-176(r1)
lfd f21,-168(r1)
lfd f22,-160(r1)
lfd f23,-152(r1)
lfd f24,-144(r1)
lfd f25,-136(r1)
lfd f26,-128(r1)
lfd f27,-120(r1)
lfd f28,-112(r1)
lfd f29,-104(r1)
lfd f30,-96(r1)
lfd f31,-88(r1)
mtlr r0
mtctr r6 ;restore return address
mtcrf 32,r7 ;restore preserved condition codes
mtcrf 16,r7
mtcrf 8,r7
bctr ;return
;Import external functions
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.align 5
L_malloc$stub:
.indirect_symbol _malloc
mflr r0
bcl 20,31,L_malloc$spb
L_malloc$spb:
mflr r11
addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb)
mtlr r0
lwzu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11)
mtctr r12
bctr
.lazy_symbol_pointer
L_malloc$lazy_ptr:
.indirect_symbol _malloc
.long dyld_stub_binding_helper
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.align 5
L_free$stub:
.indirect_symbol _free
mflr r0
bcl 20,31,L_free$spb
L_free$spb:
mflr r11
addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb)
mtlr r0
lwzu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11)
mtctr r12
bctr
.lazy_symbol_pointer
L_free$lazy_ptr:
.indirect_symbol _free
.long dyld_stub_binding_helper
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.align 5
L_sysctlbyname$stub:
.indirect_symbol _sysctlbyname
mflr r0
bcl 20,31,L_sysctlbyname$spb
L_sysctlbyname$spb:
mflr r11
addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)
mtlr r0
lwzu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11)
mtctr r12
bctr
.lazy_symbol_pointer
L_sysctlbyname$lazy_ptr:
.indirect_symbol _sysctlbyname
.long dyld_stub_binding_helper
;This needs to be here!
.subsections_via_symbols
-513
View File
@@ -1,513 +0,0 @@
;*****
;libco.ppc64 (2007-12-05)
;author: Vas Crabb
;license: public domain
;
;cross-platform 64-bit PowerPC implementation of libco
;special thanks to byuu for writing the original version
;
;[ABI compatibility]
;- gcc; mac os x; ppc64
;
;[nonvolatile registers]
;- GPR1, GPR13 - GPR31
;- FPR14 - FPR31
;- V20 - V31
;- VRSAVE, CR2 - CR4
;
;[volatile registers]
;- GPR0, GPR2 - GPR12
;- FPR0 - FPR13
;- V0 - V19
;- LR, CTR, XER, CR0, CR1, CR5 - CR7
;*****
;Declare some target-specific stuff
.section __TEXT,__text,regular,pure_instructions
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.machine ppc64
;Constants
.cstring
.align 3
_sysctl_altivec:
.ascii "hw.optional.altivec\0"
;Declare space for variables
.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX
.lcomm _co_primary_buffer,1024,3 ;buffer (will be zeroed by loader)
.data
.align 3
_co_active_context:
.quad _co_primary_buffer
.text
.align 2
;Declare exported names
.globl _co_active
.globl _co_create
.globl _co_delete
.globl _co_switch
;*****
;extern "C" cothread_t co_active();
;return = GPR3
;*****
_co_active:
mflr r0 ;GPR0 = return address
bcl 20,31,L_co_active$spb
L_co_active$spb:
mflr r2 ;GPR2 set for position-independance
addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3
ld r3,lo16(_co_active_context-L_co_active$spb)(r3)
mtlr r0 ;LR = return address
blr ;return
;*****
;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)());
;GPR3 = heapsize
;GPR4 = coentry
;return = GPR3
;*****
_co_create:
mflr r0 ;GPR0 = return address
std r30,-16(r1) ;save GPR30 and GPR31
std r31,-8(r1)
std r0,16(r1) ;save return address
stdu r1,-(2*8+16+48)(r1) ;allocate 16 bytes for locals/parameters
;create heap space (stack + register storage)
addi r31,r3,1024-48 ;subtract space for linkage
mr r30,r4 ;GPR30 = coentry
addi r3,r3,1024 ;allocate extra memory for contextual info
bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024)
add r4,r3,r31 ;GPR4 points to top-of-stack
rldicr r5,r4,0,59 ;force 16-byte alignment
;store thread entry point + registers, so that first call to co_switch will execute coentry
std r30,16(r5) ;store entry point
addi r6,0,2+19+18+12*2+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE
addi r0,0,0
addi r7,0,8 ;start at 8(GPR5)
mtctr r6
L_co_create$clear_loop:
stdx r0,r5,r7 ;clear a double
addi r7,r7,-8 ;increment pointer
bdnz L_co_create$clear_loop ;loop
stdu r5,-544(r5) ;store top of stack
;initialize context memory heap and return
addis r9,0,0x8000 ;GPR13 not set (system TLS)
std r5,0(r3) ;*cothread_t = stack heap pointer (GPR1)
stw r9,8(r3) ;this is a flag word
ld r1,0(r1) ;deallocate stack frame
ld r8,16(r1) ;fetch return address
ld r30,-16(r1) ;restore GPR30 and GPR31
ld r31,-8(r1)
mtlr r8 ;return address in LR
blr ;return
;*****
;extern "C" void co_delete(cothread_t cothread);
;GPR3 = cothread
;*****
_co_delete:
b L_free$stub ;free(GPR3)
;*****
;extern "C" void co_switch(cothread_t cothread);
;GPR3 = cothread
;*****
;
;Frame looks like:
;
;Old New Value
; 16(r1) 560(r1) Saved LR
; 8(r1) 552(r1) Saved CR
; 0(r1) 544(r1) Old GPR1
; -8(r1) 536(r1) Saved GPR31
; -16(r1) 528(r1) Saved GPR30
;... ... ...
;-144(r1) 400(r1) Saved GPR14
;-152(r1) 392(r1) Saved GPR13
;-160(r1) 384(r1) Saved FPR31
;-168(r1) 376(r1) Saved FPR30
;... ... ...
;-288(r1) 256(r1) Saved FPR15
;-296(r1) 248(r1) Saved FPR14
;-304(r1) 240(r1) Saved VRSAVE
;-312(r1) 232(r1) +++ value
;-320(r1) 224(r1) Saved VR31 len
;-328(r1) 216(r1) +++
;-336(r1) 208(r1) Saved VR30
;... ... ...
;-456(r1) 88(r1) +++
;-464(r1) 80(r1) Saved VR22 Param 5 (GPR7)
;-472(r1) 72(r1) +++ Param 4 (GPR6)
;-480(r1) 64(r1) Saved VR21 Param 3 (GPR5)
;-488(r1) 56(r1) +++ Param 2 (GPR4)
;-496(r1) 48(r1) Saved VR20 Param 1 (GPR3)
;-504(r1) 40(r1) - Reserved
;-512(r1) 32(r1) - Reserved
;-520(r1) 24(r1) - Reserved
;-528(r1) 16(r1) - New LR
;-536(r1) 8(r1) - New CR
;-544(r1) 0(r1) Saved GPR1
_co_switch:
std r13,-152(r1) ;save preserved GPRs
std r14,-144(r1)
std r15,-136(r1)
std r16,-128(r1)
std r17,-120(r1)
std r18,-112(r1)
std r19,-104(r1)
std r20,-96(r1)
std r21,-88(r1)
std r22,-80(r1)
std r23,-72(r1)
std r24,-64(r1)
std r25,-56(r1)
std r26,-48(r1)
std r27,-40(r1)
std r28,-32(r1)
std r29,-24(r1)
std r30,-16(r1)
std r31,-8(r1)
mflr r0 ;save return address
std r0,16(r1)
mfcr r2 ;save condition codes
stw r2,8(r1)
stdu r1,-544(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE)
stfd f14,248(r1) ;save preserved FPRs
stfd f15,256(r1)
stfd f16,264(r1)
stfd f17,272(r1)
stfd f18,280(r1)
stfd f19,288(r1)
stfd f20,296(r1)
stfd f21,304(r1)
stfd f22,312(r1)
stfd f23,320(r1)
stfd f24,328(r1)
stfd f25,336(r1)
stfd f26,344(r1)
stfd f27,352(r1)
stfd f28,360(r1)
stfd f29,368(r1)
stfd f30,376(r1)
stfd f31,384(r1)
mr r30,r3 ;save new context pointer
bcl 20,31,L_co_switch$spb ;get address of co_active_context
L_co_switch$spb:
mflr r31
addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags
lwz r8,lo16(_co_environ-L_co_switch$spb)(r29)
andis. r9,r8,0x8000 ;is it initialised?
bne+ L_co_switch$initialised
addi r0,0,4 ;len = sizeof(int)
std r0,224(r1)
addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec"
addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb)
addi r4,r1,232 ;GPR4 = &value
addi r5,r1,224 ;GPR5 = &len
addi r6,0,0 ;newp = 0
addi r7,0,0 ;newlen = 0
bl L_sysctlbyname$stub ;call sysctlbyname
lwz r2,232(r1) ;fetch result
addis r8,0,0x8000 ;set initialised bit
cmpdi cr5,r3,0 ;assume error means not present
cmpwi cr6,r2,0 ;test result
blt- cr5,L_co_switch$store_environ
beq cr6,L_co_switch$store_environ
oris r8,r8,0x4000 ;set the flag to say we have it!
L_co_switch$store_environ:
stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags
L_co_switch$initialised:
andis. r10,r8,0x4000 ;do we have Altivec/VMX?
beq L_co_switch$save_no_vmx
mfspr r11,256 ;save VRSAVE
andi. r0,r11,0x0FFF ;short-circuit if it's zero
stw r11,240(r1)
beq L_co_switch$save_no_vmx
andi. r0,r11,0x0800 ;check bit 20
addi r2,0,48 ;starting index
beq L_co_switch$save_skip_vr20
stvx v20,r1,r2 ;save VR20
L_co_switch$save_skip_vr20:
addi r2,r2,16 ;stride
andi. r0,r11,0x0400 ;check bit 21
beq L_co_switch$save_skip_vr21
stvx v21,r1,r2 ;save VR21
L_co_switch$save_skip_vr21:
addi r2,r2,16 ;stride
andi. r0,r11,0x0200 ;check bit 22
beq L_co_switch$save_skip_vr22
stvx v22,r1,r2 ;save VR22
L_co_switch$save_skip_vr22:
addi r2,r2,16 ;stride
andi. r0,r11,0x0100 ;check bit 23
beq L_co_switch$save_skip_vr23
stvx v23,r1,r2 ;save VR23
L_co_switch$save_skip_vr23:
addi r2,r2,16 ;stride
andi. r0,r11,0x0080 ;check bit 24
beq L_co_switch$save_skip_vr24
stvx v24,r1,r2 ;save VR24
L_co_switch$save_skip_vr24:
addi r2,r2,16 ;stride
andi. r0,r11,0x0040 ;check bit 25
beq L_co_switch$save_skip_vr25
stvx v25,r1,r2 ;save VR25
L_co_switch$save_skip_vr25:
addi r2,r2,16 ;stride
andi. r0,r11,0x0020 ;check bit 26
beq L_co_switch$save_skip_vr26
stvx v26,r1,r2 ;save VR26
L_co_switch$save_skip_vr26:
addi r2,r2,16 ;stride
andi. r0,r11,0x0010 ;check bit 27
beq L_co_switch$save_skip_vr27
stvx v27,r1,r2 ;save VR27
L_co_switch$save_skip_vr27:
addi r2,r2,16 ;stride
andi. r0,r11,0x0008 ;check bit 28
beq L_co_switch$save_skip_vr28
stvx v28,r1,r2 ;save VR28
L_co_switch$save_skip_vr28:
addi r2,r2,16 ;stride
andi. r0,r11,0x0004 ;check bit 29
beq L_co_switch$save_skip_vr29
stvx v29,r1,r2 ;save VR29
L_co_switch$save_skip_vr29:
addi r2,r2,16 ;stride
andi. r0,r11,0x0002 ;check bit 30
beq L_co_switch$save_skip_vr30
stvx v30,r1,r2 ;save VR30
L_co_switch$save_skip_vr30:
addi r2,r2,16 ;stride
andi. r0,r11,0x0001 ;check bit 31
beq L_co_switch$save_skip_vr31
stvx v31,r1,r2 ;save VR31
L_co_switch$save_skip_vr31:
L_co_switch$save_no_vmx:
addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context
ld r5,lo16(_co_active_context-L_co_switch$spb)(r4)
std r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context
std r1,0(r5) ;save current stack pointer
ld r1,0(r30) ;get new stack pointer
lwz r12,8(r30) ;have we already set GPR13 (system TLS)?
andis. r0,r12,0x8000
beq+ L_co_switch$gpr13_set
std r13,392(r1)
xoris r12,r12,0x8000
stw r12,8(r30)
L_co_switch$gpr13_set:
andis. r10,r8,0x4000 ;do we have Altivec/VMX?
beq L_co_switch$restore_no_vmx
lwz r11,240(r1) ;restore VRSAVE
andi. r0,r11,0x0FFF ;short-circuit if it's zero
mtspr 256,r11
beq L_co_switch$restore_no_vmx
andi. r0,r11,0x0800 ;check bit 20
addi r2,0,48 ;starting index
beq L_co_switch$restore_skip_vr20
lvx v20,r1,r2 ;restore VR20
L_co_switch$restore_skip_vr20:
addi r2,r2,16 ;stride
andi. r0,r11,0x0400 ;check bit 21
beq L_co_switch$restore_skip_vr21
lvx v21,r1,r2 ;restore VR21
L_co_switch$restore_skip_vr21:
addi r2,r2,16 ;stride
andi. r0,r11,0x0200 ;check bit 22
beq L_co_switch$restore_skip_vr22
lvx v22,r1,r2 ;restore VR22
L_co_switch$restore_skip_vr22:
addi r2,r2,16 ;stride
andi. r0,r11,0x0100 ;check bit 23
beq L_co_switch$restore_skip_vr23
lvx v23,r1,r2 ;restore VR23
L_co_switch$restore_skip_vr23:
addi r2,r2,16 ;stride
andi. r0,r11,0x0080 ;check bit 24
beq L_co_switch$restore_skip_vr24
lvx v24,r1,r2 ;restore VR24
L_co_switch$restore_skip_vr24:
addi r2,r2,16 ;stride
andi. r0,r11,0x0040 ;check bit 25
beq L_co_switch$restore_skip_vr25
lvx v25,r1,r2 ;restore VR25
L_co_switch$restore_skip_vr25:
addi r2,r2,16 ;stride
andi. r0,r11,0x0020 ;check bit 26
beq L_co_switch$restore_skip_vr26
lvx v26,r1,r2 ;restore VR26
L_co_switch$restore_skip_vr26:
addi r2,r2,16 ;stride
andi. r0,r11,0x0010 ;check bit 27
beq L_co_switch$restore_skip_vr27
lvx v27,r1,r2 ;restore VR27
L_co_switch$restore_skip_vr27:
addi r2,r2,16 ;stride
andi. r0,r11,0x0008 ;check bit 28
beq L_co_switch$restore_skip_vr28
lvx v28,r1,r2 ;restore VR28
L_co_switch$restore_skip_vr28:
addi r2,r2,16 ;stride
andi. r0,r11,0x0004 ;check bit 29
beq L_co_switch$restore_skip_vr29
lvx v29,r1,r2 ;restore VR29
L_co_switch$restore_skip_vr29:
addi r2,r2,16 ;stride
andi. r0,r11,0x0002 ;check bit 30
beq L_co_switch$restore_skip_vr30
lvx v30,r1,r2 ;restore VR30
L_co_switch$restore_skip_vr30:
addi r2,r2,16 ;stride
andi. r0,r11,0x0001 ;check bit 31
beq L_co_switch$restore_skip_vr31
lvx v31,r1,r2 ;restore VR31
L_co_switch$restore_skip_vr31:
L_co_switch$restore_no_vmx:
lfd f14,248(r1) ;restore preserved FPRs
lfd f15,256(r1)
lfd f16,264(r1)
lfd f17,272(r1)
lfd f18,280(r1)
lfd f19,288(r1)
lfd f20,296(r1)
lfd f21,304(r1)
lfd f22,312(r1)
lfd f23,320(r1)
lfd f24,328(r1)
lfd f25,336(r1)
lfd f26,344(r1)
lfd f27,352(r1)
lfd f28,360(r1)
lfd f29,368(r1)
lfd f30,376(r1)
lfd f31,384(r1)
addi r0,0,0 ;make thread main crash if it returns
ld r1,0(r1) ;deallocate stack frame
ld r6,16(r1) ;return address in GPR6
lwz r7,8(r1) ;condition codes in GPR7
ld r13,-152(r1) ;restore preserved GPRs
ld r14,-144(r1)
ld r15,-136(r1)
ld r16,-128(r1)
ld r17,-120(r1)
ld r18,-112(r1)
ld r19,-104(r1)
ld r20,-96(r1)
ld r21,-88(r1)
ld r22,-80(r1)
ld r23,-72(r1)
ld r24,-64(r1)
ld r25,-56(r1)
ld r26,-48(r1)
ld r27,-40(r1)
ld r28,-32(r1)
ld r29,-24(r1)
ld r30,-16(r1)
ld r31,-8(r1)
mtlr r0
mtctr r6 ;restore return address
mtcrf 32,r7 ;restore preserved condition codes
mtcrf 16,r7
mtcrf 8,r7
bctr ;return
;Import external functions
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.align 5
L_malloc$stub:
.indirect_symbol _malloc
mflr r0
bcl 20,31,L_malloc$spb
L_malloc$spb:
mflr r11
addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb)
mtlr r0
ldu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11)
mtctr r12
bctr
.lazy_symbol_pointer
L_malloc$lazy_ptr:
.indirect_symbol _malloc
.quad dyld_stub_binding_helper
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.align 5
L_free$stub:
.indirect_symbol _free
mflr r0
bcl 20,31,L_free$spb
L_free$spb:
mflr r11
addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb)
mtlr r0
ldu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11)
mtctr r12
bctr
.lazy_symbol_pointer
L_free$lazy_ptr:
.indirect_symbol _free
.quad dyld_stub_binding_helper
.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
.align 5
L_sysctlbyname$stub:
.indirect_symbol _sysctlbyname
mflr r0
bcl 20,31,L_sysctlbyname$spb
L_sysctlbyname$spb:
mflr r11
addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)
mtlr r0
ldu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11)
mtctr r12
bctr
.lazy_symbol_pointer
L_sysctlbyname$lazy_ptr:
.indirect_symbol _sysctlbyname
.quad dyld_stub_binding_helper
;This needs to be here!
.subsections_via_symbols
-102
View File
@@ -1,102 +0,0 @@
/*
libco.sjlj (2008-01-28)
author: Nach
license: public domain
*/
/*
* Note this was designed for UNIX systems. Based on ideas expressed in a paper
* by Ralf Engelschall.
* For SJLJ on other systems, one would want to rewrite springboard() and
* co_create() and hack the jmb_buf stack pointer.
*/
#define LIBCO_C
#include "libco.h"
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
sigjmp_buf context;
void (*coentry)(void);
void *stack;
} cothread_struct;
static thread_local cothread_struct co_primary;
static thread_local cothread_struct *creating, *co_running = 0;
static void springboard(int ignored) {
if(sigsetjmp(creating->context, 0)) {
co_running->coentry();
}
}
cothread_t co_active() {
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;
}
cothread_t co_create(unsigned int size, void (*coentry)(void)) {
if(!co_running) co_running = &co_primary;
cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct));
if(thread) {
struct sigaction handler;
struct sigaction old_handler;
stack_t stack;
stack_t old_stack;
thread->coentry = thread->stack = 0;
stack.ss_flags = 0;
stack.ss_size = size;
thread->stack = stack.ss_sp = malloc(size);
if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) {
handler.sa_handler = springboard;
handler.sa_flags = SA_ONSTACK;
sigemptyset(&handler.sa_mask);
creating = thread;
if(!sigaction(SIGUSR1, &handler, &old_handler)) {
if(!raise(SIGUSR1)) {
thread->coentry = coentry;
}
sigaltstack(&old_stack, 0);
sigaction(SIGUSR1, &old_handler, 0);
}
}
if(thread->coentry != coentry) {
co_delete(thread);
thread = 0;
}
}
return (cothread_t)thread;
}
void co_delete(cothread_t cothread) {
if(cothread) {
if(((cothread_struct*)cothread)->stack) {
free(((cothread_struct*)cothread)->stack);
}
free(cothread);
}
}
void co_switch(cothread_t cothread) {
if(!sigsetjmp(co_running->context, 0)) {
co_running = (cothread_struct*)cothread;
siglongjmp(co_running->context, 1);
}
}
#ifdef __cplusplus
}
#endif
-67
View File
@@ -1,67 +0,0 @@
/*
libco.ucontext (2008-01-28)
author: Nach
license: public domain
*/
/*
* WARNING: the overhead of POSIX ucontext is very high,
* assembly versions of libco or libco_sjlj should be much faster
*
* This library only exists for two reasons:
* 1 - as an initial test for the viability of a ucontext implementation
* 2 - to demonstrate the power and speed of libco over existing implementations,
* such as pth (which defaults to wrapping ucontext on unix targets)
*
* Use this library only as a *last resort*
*/
#define LIBCO_C
#include "libco.h"
#include <stdlib.h>
#include <ucontext.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local ucontext_t co_primary;
static thread_local ucontext_t *co_running = 0;
cothread_t co_active() {
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;
}
cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) {
if(!co_running) co_running = &co_primary;
ucontext_t *thread = (ucontext_t*)malloc(sizeof(ucontext_t));
if(thread) {
if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) {
thread->uc_link = co_running;
thread->uc_stack.ss_size = heapsize;
makecontext(thread, coentry, 0);
} else {
co_delete((cothread_t)thread);
thread = 0;
}
}
return (cothread_t)thread;
}
void co_delete(cothread_t cothread) {
if(cothread) {
if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); }
free(cothread);
}
}
void co_switch(cothread_t cothread) {
ucontext_t *old_thread = co_running;
co_running = (ucontext_t*)cothread;
swapcontext(old_thread, co_running);
}
#ifdef __cplusplus
}
#endif
-93
View File
@@ -1,93 +0,0 @@
/*
libco.x86 (2009-10-12)
author: byuu
license: public domain
*/
#define LIBCO_C
#include "libco.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_MSC_VER)
#define fastcall __fastcall
#elif defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#else
#error "libco: please define fastcall macro"
#endif
static thread_local long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
//ABI: fastcall
static unsigned char co_swap_function[] = {
0x89, 0x22, 0x8B, 0x21, 0x58, 0x89, 0x6A, 0x04, 0x89, 0x72, 0x08, 0x89, 0x7A, 0x0C, 0x89, 0x5A,
0x10, 0x8B, 0x69, 0x04, 0x8B, 0x71, 0x08, 0x8B, 0x79, 0x0C, 0x8B, 0x59, 0x10, 0xFF, 0xE0,
};
#ifdef _WIN32
#include <windows.h>
void co_init() {
DWORD old_privileges;
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
}
#else
#include <unistd.h>
#include <sys/mman.h>
void co_init() {
unsigned long addr = (unsigned long)co_swap_function;
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
#endif
static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
size += 256; /* allocate additional space for storage */
size &= ~15; /* align stack to 16-byte boundary */
if(handle = (cothread_t)malloc(size)) {
long *p = (long*)((char*)handle + size); /* seek to top of stack */
*--p = (long)crash; /* crash if entrypoint returns */
*--p = (long)entrypoint; /* start of function */
*(long*)handle = (long)p; /* stack pointer */
}
return handle;
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
#ifdef __cplusplus
}
#endif
-109
View File
@@ -1,109 +0,0 @@
# Makefile
# author: byuu
# license: public domain
[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
[0-9] = 0 1 2 3 4 5 6 7 8 9
[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ?
[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup])
[space] :=
[space] +=
#####
# platform detection
#####
ifeq ($(platform),)
uname := $(shell uname -a)
ifeq ($(uname),)
platform := win
delete = del $(subst /,\,$1)
else ifneq ($(findstring Darwin,$(uname)),)
platform := osx
delete = rm -f $1
else
platform := x
delete = rm -f $1
endif
endif
ifeq ($(compiler),)
ifeq ($(platform),win)
compiler := gcc
else ifeq ($(platform),osx)
compiler := gcc-mp-4.4
else
compiler := gcc
endif
endif
ifeq ($(prefix),)
prefix := /usr/local
endif
#####
# function rwildcard(directory, pattern)
#####
rwildcard = \
$(strip \
$(filter $(if $2,$2,%), \
$(foreach f, \
$(wildcard $1*), \
$(eval t = $(call rwildcard,$f/)) \
$(if $t,$t,$f) \
) \
) \
)
#####
# function strtr(source, from, to)
#####
strtr = \
$(eval __temp := $1) \
$(strip \
$(foreach c, \
$(join $(addsuffix :,$2),$3), \
$(eval __temp := \
$(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \
) \
) \
$(__temp) \
)
#####
# function strupper(source)
#####
strupper = $(call strtr,$1,$([a-z]),$([A-Z]))
#####
# function strlower(source)
#####
strlower = $(call strtr,$1,$([A-Z]),$([a-z]))
#####
# function strlen(source)
#####
strlen = \
$(eval __temp := $(subst $([space]),_,$1)) \
$(words \
$(strip \
$(foreach c, \
$([all]), \
$(eval __temp := \
$(subst $c,$c ,$(__temp)) \
) \
) \
$(__temp) \
) \
)
#####
# function streq(source)
#####
streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1)
#####
# function strne(source)
#####
strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,)
-17
View File
@@ -1,17 +0,0 @@
#ifndef NALL_ALGORITHM_HPP
#define NALL_ALGORITHM_HPP
#undef min
#undef max
namespace nall {
template<typename T, typename U> T min(const T &t, const U &u) {
return t < u ? t : u;
}
template<typename T, typename U> T max(const T &t, const U &u) {
return t > u ? t : u;
}
}
#endif
-74
View File
@@ -1,74 +0,0 @@
#ifndef NALL_ANY_HPP
#define NALL_ANY_HPP
#include <typeinfo>
#include <type_traits>
#include <nall/static.hpp>
namespace nall {
class any {
public:
bool empty() const { return container; }
const std::type_info& type() const { return container ? container->type() : typeid(void); }
template<typename T> any& operator=(const T& value_) {
typedef typename static_if<
std::is_array<T>::value,
typename std::remove_extent<typename std::add_const<T>::type>::type*,
T
>::type auto_t;
if(type() == typeid(auto_t)) {
static_cast<holder<auto_t>*>(container)->value = (auto_t)value_;
} else {
if(container) delete container;
container = new holder<auto_t>((auto_t)value_);
}
return *this;
}
any() : container(0) {}
template<typename T> any(const T& value_) : container(0) { operator=(value_); }
private:
struct placeholder {
virtual const std::type_info& type() const = 0;
} *container;
template<typename T> struct holder : placeholder {
T value;
const std::type_info& type() const { return typeid(T); }
holder(const T& value_) : value(value_) {}
};
template<typename T> friend T any_cast(any&);
template<typename T> friend T any_cast(const any&);
template<typename T> friend T* any_cast(any*);
template<typename T> friend const T* any_cast(const any*);
};
template<typename T> T any_cast(any &value) {
typedef typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T any_cast(const any &value) {
typedef const typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T* any_cast(any *value) {
if(!value || value->type() != typeid(T)) return 0;
return &static_cast<any::holder<T>*>(value->container)->value;
}
template<typename T> const T* any_cast(const any *value) {
if(!value || value->type() != typeid(T)) return 0;
return &static_cast<any::holder<T>*>(value->container)->value;
}
}
#endif
-141
View File
@@ -1,141 +0,0 @@
#ifndef NALL_ARRAY_HPP
#define NALL_ARRAY_HPP
#include <stdlib.h>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/concept.hpp>
#include <nall/foreach.hpp>
#include <nall/utility.hpp>
namespace nall {
//dynamic vector array
//neither constructor nor destructor is ever invoked;
//thus, this should only be used for POD objects.
template<typename T> class array {
protected:
T *pool;
unsigned poolsize, buffersize;
public:
unsigned size() const { return buffersize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = 0;
poolsize = 0;
buffersize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (T*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
buffersize = min(buffersize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
buffersize = newsize;
}
T* get(unsigned minsize = 0) {
if(minsize > buffersize) resize(minsize);
if(minsize > buffersize) throw "array[] out of bounds";
return pool;
}
void append(const T data) {
operator[](buffersize) = data;
}
template<typename U> void insert(unsigned index, const U list) {
unsigned listsize = container_size(list);
resize(buffersize + listsize);
memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T));
foreach(item, list) pool[index++] = item;
}
void insert(unsigned index, const T item) {
insert(index, array<T>{ item });
}
void remove(unsigned index, unsigned count = 1) {
for(unsigned i = index; count + i < buffersize; i++) {
pool[i] = pool[count + i];
}
if(count + index >= buffersize) resize(index); //every element >= index was removed
else resize(buffersize - count);
}
optional<unsigned> find(const T data) {
for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return { true, i };
return { false, 0 };
}
void clear() {
memset(pool, 0, buffersize * sizeof(T));
}
array() : pool(0), poolsize(0), buffersize(0) {
}
array(std::initializer_list<T> list) : pool(0), poolsize(0), buffersize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
}
~array() {
reset();
}
//copy
array& operator=(const array &source) {
if(pool) free(pool);
buffersize = source.buffersize;
poolsize = source.poolsize;
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects
return *this;
}
array(const array &source) : pool(0), poolsize(0), buffersize(0) {
operator=(source);
}
//move
array& operator=(array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
buffersize = source.buffersize;
source.pool = 0;
source.reset();
return *this;
}
array(array &&source) : pool(0), poolsize(0), buffersize(0) {
operator=(std::move(source));
}
//index
inline T& operator[](unsigned index) {
if(index >= buffersize) resize(index + 1);
if(index >= buffersize) throw "array[] out of bounds";
return pool[index];
}
inline const T& operator[](unsigned index) const {
if(index >= buffersize) throw "array[] out of bounds";
return pool[index];
}
};
template<typename T> struct has_size<array<T>> { enum { value = true }; };
}
#endif
-90
View File
@@ -1,90 +0,0 @@
#ifndef NALL_BASE64_HPP
#define NALL_BASE64_HPP
#include <string.h>
#include <nall/stdint.hpp>
namespace nall {
class base64 {
public:
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
output = new char[inlength * 8 / 6 + 6]();
unsigned i = 0, o = 0;
while(i < inlength) {
switch(i % 3) {
case 0: {
output[o++] = enc(input[i] >> 2);
output[o] = enc((input[i] & 3) << 4);
} break;
case 1: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 4));
output[o] = enc((input[i] & 15) << 2);
} break;
case 2: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 6));
output[o++] = enc(input[i] & 63);
} break;
}
i++;
}
return true;
}
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
unsigned inlength = strlen(input), infix = 0;
output = new uint8_t[inlength]();
unsigned i = 0, o = 0;
while(i < inlength) {
uint8_t x = dec(input[i]);
switch(i++ & 3) {
case 0: {
output[o] = x << 2;
} break;
case 1: {
output[o++] |= x >> 4;
output[o] = (x & 15) << 4;
} break;
case 2: {
output[o++] |= x >> 2;
output[o] = (x & 3) << 6;
} break;
case 3: {
output[o++] |= x;
} break;
}
}
outlength = o;
return true;
}
private:
static char enc(uint8_t n) {
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}
static uint8_t dec(char n) {
if(n >= 'A' && n <= 'Z') return n - 'A';
if(n >= 'a' && n <= 'z') return n - 'a' + 26;
if(n >= '0' && n <= '9') return n - '0' + 52;
if(n == '-') return 62;
if(n == '_') return 63;
return 0;
}
};
}
#endif
-51
View File
@@ -1,51 +0,0 @@
#ifndef NALL_BIT_HPP
#define NALL_BIT_HPP
namespace nall {
template<int bits> inline unsigned uclamp(const unsigned x) {
enum { y = (1U << bits) - 1 };
return y + ((x - y) & -(x < y)); //min(x, y);
}
template<int bits> inline unsigned uclip(const unsigned x) {
enum { m = (1U << bits) - 1 };
return (x & m);
}
template<int bits> inline signed sclamp(const signed x) {
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
return (x > m) ? m : (x < -b) ? -b : x;
}
template<int bits> inline signed sclip(const signed x) {
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
return ((x & m) ^ b) - b;
}
namespace bit {
//lowest(0b1110) == 0b0010
template<typename T> inline T lowest(const T x) {
return x & -x;
}
//clear_lowest(0b1110) == 0b1100
template<typename T> inline T clear_lowest(const T x) {
return x & (x - 1);
}
//set_lowest(0b0101) == 0b0111
template<typename T> inline T set_lowest(const T x) {
return x | (x + 1);
}
//round up to next highest single bit:
//round(15) == 16, round(16) == 16, round(17) == 32
inline unsigned round(unsigned x) {
if((x & (x - 1)) == 0) return x;
while(x & (x - 1)) x &= x - 1;
return x << 1;
}
}
}
#endif
-34
View File
@@ -1,34 +0,0 @@
#ifndef NALL_CONCEPT_HPP
#define NALL_CONCEPT_HPP
#include <nall/static.hpp>
#include <nall/utility.hpp>
namespace nall {
//unsigned count() const;
template<typename T> struct has_count { enum { value = false }; };
//unsigned length() const;
template<typename T> struct has_length { enum { value = false }; };
//unsigned size() const;
template<typename T> struct has_size { enum { value = false }; };
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_count<T>>::type = 0) {
return object.count();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_length<T>>::type = 0) {
return object.length();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_size<T>>::type = 0) {
return object.size();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<std::is_array<T>>::type = 0) {
return sizeof(T) / sizeof(typename std::remove_extent<T>::type);
}
}
#endif
-123
View File
@@ -1,123 +0,0 @@
#ifndef NALL_CONFIG_HPP
#define NALL_CONFIG_HPP
#include <nall/file.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
namespace configuration_traits {
template<typename T> struct is_boolean { enum { value = false }; };
template<> struct is_boolean<bool> { enum { value = true }; };
template<typename T> struct is_signed { enum { value = false }; };
template<> struct is_signed<signed> { enum { value = true }; };
template<typename T> struct is_unsigned { enum { value = false }; };
template<> struct is_unsigned<unsigned> { enum { value = true }; };
template<typename T> struct is_double { enum { value = false }; };
template<> struct is_double<double> { enum { value = true }; };
template<typename T> struct is_string { enum { value = false }; };
template<> struct is_string<string> { enum { value = true }; };
}
class configuration {
public:
enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t };
struct item_t {
uintptr_t data;
string name;
string desc;
type_t type;
string get() const {
switch(type) {
case boolean_t: return string() << *(bool*)data;
case signed_t: return string() << *(signed*)data;
case unsigned_t: return string() << *(unsigned*)data;
case double_t: return string() << *(double*)data;
case string_t: return string() << "\"" << *(string*)data << "\"";
}
return "???";
}
void set(string s) {
switch(type) {
case boolean_t: *(bool*)data = (s == "true"); break;
case signed_t: *(signed*)data = strsigned(s); break;
case unsigned_t: *(unsigned*)data = strunsigned(s); break;
case double_t: *(double*)data = strdouble(s); break;
case string_t: s.trim("\""); *(string*)data = s; break;
}
}
};
linear_vector<item_t> list;
template<typename T>
void attach(T &data, const char *name, const char *desc = "") {
unsigned n = list.size();
list[n].data = (uintptr_t)&data;
list[n].name = name;
list[n].desc = desc;
if(configuration_traits::is_boolean<T>::value) list[n].type = boolean_t;
else if(configuration_traits::is_signed<T>::value) list[n].type = signed_t;
else if(configuration_traits::is_unsigned<T>::value) list[n].type = unsigned_t;
else if(configuration_traits::is_double<T>::value) list[n].type = double_t;
else if(configuration_traits::is_string<T>::value) list[n].type = string_t;
else list[n].type = unknown_t;
}
virtual bool load(const char *filename) {
string data;
if(data.readfile(filename) == true) {
data.replace("\r", "");
lstring line;
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0;
if(!qstrpos(line[i], " = ")) continue;
lstring part;
part.qsplit(" = ", line[i]);
part[0].trim();
part[1].trim();
for(unsigned n = 0; n < list.size(); n++) {
if(part[0] == list[n].name) {
list[n].set(part[1]);
break;
}
}
}
return true;
} else {
return false;
}
}
virtual bool save(const char *filename) const {
file fp;
if(fp.open(filename, file::mode_write)) {
for(unsigned i = 0; i < list.size(); i++) {
string output;
output << list[i].name << " = " << list[i].get();
if(list[i].desc != "") output << " # " << list[i].desc;
output << "\r\n";
fp.print(output);
}
fp.close();
return true;
} else {
return false;
}
}
};
}
#endif
-66
View File
@@ -1,66 +0,0 @@
#ifndef NALL_CRC32_HPP
#define NALL_CRC32_HPP
#include <nall/stdint.hpp>
namespace nall {
const uint32_t crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) {
return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff];
}
inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) {
uint32_t crc32 = ~0;
for(unsigned i = 0; i < length; i++) {
crc32 = crc32_adjust(crc32, data[i]);
}
return ~crc32;
}
}
#endif
-30
View File
@@ -1,30 +0,0 @@
#ifndef NALL_DETECT_HPP
#define NALL_DETECT_HPP
/* Compiler detection */
#if defined(__GNUC__)
#define COMPILER_GCC
#elif defined(_MSC_VER)
#define COMPILER_VISUALC
#endif
/* Platform detection */
#if defined(_WIN32)
#define PLATFORM_WIN
#elif defined(__APPLE__)
#define PLATFORM_OSX
#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define PLATFORM_X
#endif
/* Endian detection */
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
#define ARCH_LSB
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
#define ARCH_MSB
#endif
#endif
-75
View File
@@ -1,75 +0,0 @@
#ifndef NALL_DICTIONARY_HPP
#define NALL_DICTIONARY_HPP
#include <nall/array.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
namespace nall {
class dictionary {
public:
string operator[](const char *input) {
for(unsigned i = 0; i < index_input.size(); i++) {
if(index_input[i] == input) return index_output[i];
}
//no match, use input; remove input identifier, if one exists
if(strbegin(input, "{{")) {
if(auto pos = strpos(input, "}}")) {
string temp = substr(input, pos() + 2);
return temp;
}
}
return input;
}
bool import(const char *filename) {
string data;
if(data.readfile(filename) == false) return false;
data.ltrim_once("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
data.replace("\r", "");
lstring line;
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
lstring part;
//format: "Input" = "Output"
part.qsplit("=", line[i]);
if(part.size() != 2) continue;
//remove whitespace
part[0].trim();
part[1].trim();
//remove quotes
part[0].trim_once("\"");
part[1].trim_once("\"");
unsigned n = index_input.size();
index_input[n] = part[0];
index_output[n] = part[1];
}
return true;
}
void reset() {
index_input.reset();
index_output.reset();
}
~dictionary() {
reset();
}
dictionary& operator=(const dictionary&) = delete;
dictionary(const dictionary&) = delete;
protected:
lstring index_input;
lstring index_output;
};
}
#endif
-129
View File
@@ -1,129 +0,0 @@
#ifndef NALL_DIRECTORY_HPP
#define NALL_DIRECTORY_HPP
#include <nall/foreach.hpp>
#include <nall/sort.hpp>
#include <nall/string.hpp>
#if defined(_WIN32)
#include <nall/utf8.hpp>
#else
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#endif
namespace nall {
struct directory {
static lstring folders(const char *pathname);
static lstring files(const char *pathname);
static lstring contents(const char *pathname);
};
#if defined(_WIN32)
inline lstring directory::folders(const char *pathname) {
lstring list;
string path = pathname;
path.transform("/", "\\");
if(!strend(path, "\\")) path.append("\\");
path.append("*");
HANDLE handle;
WIN32_FIND_DATA data;
handle = FindFirstFile(utf16_t(path), &data);
if(handle != INVALID_HANDLE_VALUE) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
list.append(string(utf8_t(data.cFileName), "/"));
}
}
while(FindNextFile(handle, &data) != false) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
list.append(string(utf8_t(data.cFileName), "/"));
}
}
}
FindClose(handle);
}
sort(&list[0], list.size());
return list;
}
inline lstring directory::files(const char *pathname) {
lstring list;
string path = pathname;
path.transform("/", "\\");
if(!strend(path, "\\")) path.append("\\");
path.append("*");
HANDLE handle;
WIN32_FIND_DATA data;
handle = FindFirstFile(utf16_t(path), &data);
if(handle != INVALID_HANDLE_VALUE) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
list.append(utf8_t(data.cFileName));
}
while(FindNextFile(handle, &data) != false) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
list.append(utf8_t(data.cFileName));
}
}
FindClose(handle);
}
sort(&list[0], list.size());
return list;
}
inline lstring directory::contents(const char *pathname) {
lstring folders = directory::folders(pathname);
lstring files = directory::files(pathname);
foreach(file, files) folders.append(file);
return folders;
}
#else
inline lstring directory::folders(const char *pathname) {
lstring list;
DIR *dp;
struct dirent *ep;
dp = opendir(pathname);
if(dp) {
while(ep = readdir(dp)) {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if(ep->d_type & DT_DIR) list.append(string(ep->d_name, "/"));
}
closedir(dp);
}
sort(&list[0], list.size());
return list;
}
inline lstring directory::files(const char *pathname) {
lstring list;
DIR *dp;
struct dirent *ep;
dp = opendir(pathname);
if(dp) {
while(ep = readdir(dp)) {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if((ep->d_type & DT_DIR) == 0) list.append(ep->d_name);
}
closedir(dp);
}
sort(&list[0], list.size());
return list;
}
inline lstring directory::contents(const char *pathname) {
lstring folders = directory::folders(pathname);
lstring files = directory::files(pathname);
foreach(file, files) folders.append(file);
return folders;
}
#endif
}
#endif
-96
View File
@@ -1,96 +0,0 @@
#ifndef NALL_DL_HPP
#define NALL_DL_HPP
//dynamic linking support
#include <nall/detect.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
#include <dlfcn.h>
#elif defined(PLATFORM_WIN)
#include <windows.h>
#include <nall/utf8.hpp>
#endif
namespace nall {
struct library {
bool opened() const { return handle; }
bool open(const char*, const char* = "");
void* sym(const char*);
void close();
library() : handle(0) {}
~library() { close(); }
library& operator=(const library&) = delete;
library(const library&) = delete;
private:
uintptr_t handle;
};
#if defined(PLATFORM_X)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY);
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_OSX)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY);
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_WIN)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return (void*)GetProcAddress((HMODULE)handle, name);
}
inline void library::close() {
if(!handle) return;
FreeLibrary((HMODULE)handle);
handle = 0;
}
#else
inline bool library::open(const char*, const char*) { return false; }
inline void* library::sym(const char*) { return 0; }
inline void library::close() {}
#endif
};
#endif
-38
View File
@@ -1,38 +0,0 @@
#ifndef NALL_ENDIAN_HPP
#define NALL_ENDIAN_HPP
#if !defined(ARCH_MSB)
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
#define order_lsb2(a,b) a,b
#define order_lsb3(a,b,c) a,b,c
#define order_lsb4(a,b,c,d) a,b,c,d
#define order_lsb5(a,b,c,d,e) a,b,c,d,e
#define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f
#define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
#define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
#define order_msb2(a,b) b,a
#define order_msb3(a,b,c) c,b,a
#define order_msb4(a,b,c,d) d,c,b,a
#define order_msb5(a,b,c,d,e) e,d,c,b,a
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
#else
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
#define order_lsb2(a,b) b,a
#define order_lsb3(a,b,c) c,b,a
#define order_lsb4(a,b,c,d) d,c,b,a
#define order_lsb5(a,b,c,d,e) e,d,c,b,a
#define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a
#define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
#define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
#define order_msb2(a,b) a,b
#define order_msb3(a,b,c) a,b,c
#define order_msb4(a,b,c,d) a,b,c,d
#define order_msb5(a,b,c,d,e) a,b,c,d,e
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
#endif
#endif
-261
View File
@@ -1,261 +0,0 @@
#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/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utf8.hpp>
#include <nall/utility.hpp>
namespace nall {
inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
#if !defined(_WIN32)
return fopen(utf8_filename, mode);
#else
return _wfopen(utf16_t(utf8_filename), utf16_t(mode));
#endif
}
class file {
public:
enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread };
enum SeekMode { seek_absolute, seek_relative };
uint8_t read() {
if(!fp) return 0xff; //file not open
if(file_mode == mode_write) return 0xff; //reads not permitted
if(file_offset >= file_size) return 0xff; //cannot read past end of file
buffer_sync();
return buffer[(file_offset++) & buffer_mask];
}
uintmax_t readl(unsigned length = 1) {
uintmax_t data = 0;
for(int i = 0; i < length; i++) {
data |= (uintmax_t)read() << (i << 3);
}
return data;
}
uintmax_t readm(unsigned length = 1) {
uintmax_t data = 0;
while(length--) {
data <<= 8;
data |= read();
}
return data;
}
void read(uint8_t *buffer, unsigned length) {
while(length--) *buffer++ = read();
}
void write(uint8_t data) {
if(!fp) return; //file not open
if(file_mode == mode_read) return; //writes not permitted
buffer_sync();
buffer[(file_offset++) & buffer_mask] = data;
buffer_dirty = true;
if(file_offset > file_size) file_size = file_offset;
}
void writel(uintmax_t data, unsigned length = 1) {
while(length--) {
write(data);
data >>= 8;
}
}
void writem(uintmax_t data, unsigned length = 1) {
for(int i = length - 1; i >= 0; i--) {
write(data >> (i << 3));
}
}
void write(const uint8_t *buffer, unsigned length) {
while(length--) write(*buffer++);
}
template<typename... Args> void print(Args... args) {
string data(args...);
const char *p = data;
while(*p) write(*p++);
}
void flush() {
buffer_flush();
fflush(fp);
}
void seek(int offset, SeekMode mode = seek_absolute) {
if(!fp) return; //file not open
buffer_flush();
uintmax_t req_offset = file_offset;
switch(mode) {
case seek_absolute: req_offset = offset; break;
case seek_relative: req_offset += offset; break;
}
if(req_offset < 0) req_offset = 0; //cannot seek before start of file
if(req_offset > file_size) {
if(file_mode == mode_read) { //cannot seek past end of file
req_offset = file_size;
} else { //pad file to requested location
file_offset = file_size;
while(file_size < req_offset) write(0x00);
}
}
file_offset = req_offset;
}
int offset() {
if(!fp) return -1; //file not open
return file_offset;
}
int size() {
if(!fp) return -1; //file not open
return file_size;
}
bool truncate(unsigned size) {
if(!fp) return false; //file not open
#if !defined(_WIN32)
return ftruncate(fileno(fp), size) == 0;
#else
return _chsize(fileno(fp), size) == 0;
#endif
}
bool end() {
if(!fp) return true; //file not open
return file_offset >= file_size;
}
static bool exists(const char *fn) {
#if !defined(_WIN32)
FILE *fp = fopen(fn, "rb");
#else
FILE *fp = _wfopen(utf16_t(fn), L"rb");
#endif
if(fp) {
fclose(fp);
return true;
}
return false;
}
static unsigned size(const char *fn) {
#if !defined(_WIN32)
FILE *fp = fopen(fn, "rb");
#else
FILE *fp = _wfopen(utf16_t(fn), L"rb");
#endif
unsigned filesize = 0;
if(fp) {
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
fclose(fp);
}
return filesize;
}
bool open() {
return fp;
}
bool open(const char *fn, FileMode mode) {
if(fp) return false;
switch(file_mode = mode) {
#if !defined(_WIN32)
case mode_read: fp = fopen(fn, "rb"); break;
case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering
case mode_readwrite: fp = fopen(fn, "rb+"); break;
case mode_writeread: fp = fopen(fn, "wb+"); break;
#else
case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break;
case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break;
case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
#endif
}
if(!fp) return false;
buffer_offset = -1; //invalidate buffer
file_offset = 0;
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
return true;
}
void close() {
if(!fp) return;
buffer_flush();
fclose(fp);
fp = 0;
}
file() {
memset(buffer, 0, sizeof buffer);
buffer_offset = -1;
buffer_dirty = false;
fp = 0;
file_offset = 0;
file_size = 0;
file_mode = mode_read;
}
~file() {
close();
}
file& operator=(const file&) = delete;
file(const file&) = delete;
private:
enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 };
char buffer[buffer_size];
int buffer_offset;
bool buffer_dirty;
FILE *fp;
unsigned file_offset;
unsigned file_size;
FileMode file_mode;
void buffer_sync() {
if(!fp) return; //file not open
if(buffer_offset != (file_offset & ~buffer_mask)) {
buffer_flush();
buffer_offset = file_offset & ~buffer_mask;
fseek(fp, buffer_offset, SEEK_SET);
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
if(length) unsigned unused = fread(buffer, 1, length, fp);
}
}
void buffer_flush() {
if(!fp) return; //file not open
if(file_mode == mode_read) return; //buffer cannot be written to
if(buffer_offset < 0) return; //buffer unused
if(buffer_dirty == false) return; //buffer unmodified since read
fseek(fp, buffer_offset, SEEK_SET);
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
if(length) unsigned unused = fwrite(buffer, 1, length, fp);
buffer_offset = -1; //invalidate buffer
buffer_dirty = false;
}
};
}
#endif
-190
View File
@@ -1,190 +0,0 @@
#ifndef NALL_FILEMAP_HPP
#define NALL_FILEMAP_HPP
#include <nall/stdint.hpp>
#include <nall/utf8.hpp>
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
namespace nall {
class filemap {
public:
enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread };
bool open(const char *filename, filemode mode) { return p_open(filename, mode); }
void close() { return p_close(); }
unsigned size() const { return p_size; }
uint8_t* handle() { return p_handle; }
const uint8_t* handle() const { return p_handle; }
filemap() : p_size(0), p_handle(0) { p_ctor(); }
~filemap() { p_dtor(); }
private:
unsigned p_size;
uint8_t *p_handle;
#if defined(_WIN32)
//=============
//MapViewOfFile
//=============
HANDLE p_filehandle, p_maphandle;
bool p_open(const char *filename, filemode mode) {
int desired_access, creation_disposition, flprotect, map_access;
switch(mode) {
default: return false;
case mode_read:
desired_access = GENERIC_READ;
creation_disposition = OPEN_EXISTING;
flprotect = PAGE_READONLY;
map_access = FILE_MAP_READ;
break;
case mode_write:
//write access requires read access
desired_access = GENERIC_WRITE;
creation_disposition = CREATE_ALWAYS;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
case mode_readwrite:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = OPEN_EXISTING;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
case mode_writeread:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = CREATE_NEW;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
}
p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL,
creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
if(p_filehandle == INVALID_HANDLE_VALUE) return false;
p_size = GetFileSize(p_filehandle, NULL);
p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL);
if(p_maphandle == INVALID_HANDLE_VALUE) {
CloseHandle(p_filehandle);
p_filehandle = INVALID_HANDLE_VALUE;
return false;
}
p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size);
return p_handle;
}
void p_close() {
if(p_handle) {
UnmapViewOfFile(p_handle);
p_handle = 0;
}
if(p_maphandle != INVALID_HANDLE_VALUE) {
CloseHandle(p_maphandle);
p_maphandle = INVALID_HANDLE_VALUE;
}
if(p_filehandle != INVALID_HANDLE_VALUE) {
CloseHandle(p_filehandle);
p_filehandle = INVALID_HANDLE_VALUE;
}
}
void p_ctor() {
p_filehandle = INVALID_HANDLE_VALUE;
p_maphandle = INVALID_HANDLE_VALUE;
}
void p_dtor() {
close();
}
#else
//====
//mmap
//====
int p_fd;
bool p_open(const char *filename, filemode mode) {
int open_flags, mmap_flags;
switch(mode) {
default: return false;
case mode_read:
open_flags = O_RDONLY;
mmap_flags = PROT_READ;
break;
case mode_write:
open_flags = O_RDWR | O_CREAT; //mmap() requires read access
mmap_flags = PROT_WRITE;
break;
case mode_readwrite:
open_flags = O_RDWR;
mmap_flags = PROT_READ | PROT_WRITE;
break;
case mode_writeread:
open_flags = O_RDWR | O_CREAT;
mmap_flags = PROT_READ | PROT_WRITE;
break;
}
p_fd = ::open(filename, open_flags);
if(p_fd < 0) return false;
struct stat p_stat;
fstat(p_fd, &p_stat);
p_size = p_stat.st_size;
p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0);
if(p_handle == MAP_FAILED) {
p_handle = 0;
::close(p_fd);
p_fd = -1;
return false;
}
return p_handle;
}
void p_close() {
if(p_handle) {
munmap(p_handle, p_size);
p_handle = 0;
}
if(p_fd >= 0) {
::close(p_fd);
p_fd = -1;
}
}
void p_ctor() {
p_fd = -1;
}
void p_dtor() {
p_close();
}
#endif
};
}
#endif
-12
View File
@@ -1,12 +0,0 @@
#ifndef NALL_FOREACH_HPP
#define NALL_FOREACH_HPP
#include <type_traits>
#include <nall/concept.hpp>
#undef foreach
#define foreach(iter, object) \
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
#endif
-91
View File
@@ -1,91 +0,0 @@
#ifndef NALL_FUNCTION_HPP
#define NALL_FUNCTION_HPP
#include <stdlib.h>
#include <functional>
#include <type_traits>
namespace nall {
template<typename T> class function;
template<typename R, typename... P>
class function<R (P...)> {
private:
struct base1 { virtual void func1(P...) {} };
struct base2 { virtual void func2(P...) {} };
struct derived : base1, virtual base2 {};
struct data_t {
R (*callback)(const data_t&, P...);
union {
R (*callback_global)(P...);
struct {
R (derived::*callback_member)(P...);
void *object;
};
};
} data;
static R callback_global(const data_t &data, P... p) {
return data.callback_global(p...);
}
template<typename C>
static R callback_member(const data_t &data, P... p) {
return (((C*)data.object)->*((R (C::*&)(P...))data.callback_member))(p...);
}
public:
R operator()(P... p) const { return data.callback(data, p...); }
operator bool() const { return data.callback; }
void reset() { data.callback = 0; }
function& operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; }
function(const function &source) { operator=(source); }
//no pointer
function() {
data.callback = 0;
}
//symbolic link pointer (nall/dl.hpp::sym, etc)
function(void *callback) {
data.callback = callback ? &callback_global : 0;
data.callback_global = (R (*)(P...))callback;
}
//global function pointer
function(R (*callback)(P...)) {
data.callback = &callback_global;
data.callback_global = callback;
}
//member function pointer
template<typename C>
function(R (C::*callback)(P...), C *object) {
static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small");
data.callback = &callback_member<C>;
(R (C::*&)(P...))data.callback_member = callback;
data.object = object;
}
//const member function pointer
template<typename C>
function(R (C::*callback)(P...) const, C *object) {
static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small");
data.callback = &callback_member<C>;
(R (C::*&)(P...))data.callback_member = (R (C::*&)(P...))callback;
data.object = object;
}
//lambda function pointer
template<typename T>
function(T callback) {
static_assert(std::is_same<R, typename std::result_of<T(P...)>::type>::value, "lambda mismatch");
data.callback = &callback_global;
data.callback_global = (R (*)(P...))callback;
}
};
}
#endif
-386
View File
@@ -1,386 +0,0 @@
#ifndef NALL_INPUT_HPP
#define NALL_INPUT_HPP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct Keyboard;
Keyboard& keyboard(unsigned = 0);
static const char KeyboardScancodeName[][64] = {
"Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
"PrintScreen", "ScrollLock", "Pause", "Tilde",
"Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0",
"Dash", "Equal", "Backspace",
"Insert", "Delete", "Home", "End", "PageUp", "PageDown",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash",
"Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0",
"Point", "Enter", "Add", "Subtract", "Multiply", "Divide",
"NumLock", "CapsLock",
"Up", "Down", "Left", "Right",
"Tab", "Return", "Spacebar", "Menu",
"Shift", "Control", "Alt", "Super",
};
struct Keyboard {
const unsigned ID;
enum { Base = 1 };
enum { Count = 8, Size = 128 };
enum Scancode {
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
PrintScreen, ScrollLock, Pause, Tilde,
Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0,
Dash, Equal, Backspace,
Insert, Delete, Home, End, PageUp, PageDown,
A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash,
Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0,
Point, Enter, Add, Subtract, Multiply, Divide,
NumLock, CapsLock,
Up, Down, Left, Right,
Tab, Return, Spacebar, Menu,
Shift, Control, Alt, Super,
Limit,
};
static signed numberDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).belongsTo(scancode)) return i;
}
return -1;
}
static signed keyDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape);
}
return -1;
}
static signed modifierDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift);
}
return -1;
}
static bool isAnyKey(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isKey(scancode)) return true;
}
return false;
}
static bool isAnyModifier(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isModifier(scancode)) return true;
}
return false;
}
static uint16_t decode(const char *name) {
string s(name);
if(!strbegin(name, "KB")) return 0;
s.ltrim("KB");
unsigned id = strunsigned(s);
auto pos = strpos(s, "::");
if(!pos) return 0;
s = substr(s, pos() + 2);
for(unsigned i = 0; i < Limit; i++) {
if(s == KeyboardScancodeName[i]) return Base + Size * id + i;
}
return 0;
}
string encode(uint16_t code) const {
unsigned index = 0;
for(unsigned i = 0; i < Count; i++) {
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
index = code - (Base + Size * i);
break;
}
}
return string() << "KB" << ID << "::" << KeyboardScancodeName[index];
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
uint16_t key(unsigned id) const { return Base + Size * ID + id; }
bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); }
bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); }
bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); }
Keyboard(unsigned ID_) : ID(ID_) {}
};
inline Keyboard& keyboard(unsigned id) {
static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7);
switch(id) { default:
case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3;
case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7;
}
}
static const char MouseScancodeName[][64] = {
"Xaxis", "Yaxis", "Zaxis",
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
};
struct Mouse;
Mouse& mouse(unsigned = 0);
struct Mouse {
const unsigned ID;
enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count };
enum { Count = 8, Size = 16 };
enum { Axes = 3, Buttons = 8 };
enum Scancode {
Xaxis, Yaxis, Zaxis,
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
Limit,
};
static signed numberDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).belongsTo(scancode)) return i;
}
return -1;
}
static signed axisDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0);
}
return -1;
}
static signed buttonDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0);
}
return -1;
}
static bool isAnyAxis(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isAxis(scancode)) return true;
}
return false;
}
static bool isAnyButton(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isButton(scancode)) return true;
}
return false;
}
static uint16_t decode(const char *name) {
string s(name);
if(!strbegin(name, "MS")) return 0;
s.ltrim("MS");
unsigned id = strunsigned(s);
auto pos = strpos(s, "::");
if(!pos) return 0;
s = substr(s, pos() + 2);
for(unsigned i = 0; i < Limit; i++) {
if(s == MouseScancodeName[i]) return Base + Size * id + i;
}
return 0;
}
string encode(uint16_t code) const {
unsigned index = 0;
for(unsigned i = 0; i < Count; i++) {
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
index = code - (Base + Size * i);
break;
}
}
return string() << "MS" << ID << "::" << MouseScancodeName[index];
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; }
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); }
bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); }
bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); }
Mouse(unsigned ID_) : ID(ID_) {}
};
inline Mouse& mouse(unsigned id) {
static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7);
switch(id) { default:
case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3;
case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7;
}
}
static const char JoypadScancodeName[][64] = {
"Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7",
"Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7",
"Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15",
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
"Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15",
"Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23",
"Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31",
};
struct Joypad;
Joypad& joypad(unsigned = 0);
struct Joypad {
const unsigned ID;
enum { Base = Mouse::Base + Mouse::Size * Mouse::Count };
enum { Count = 8, Size = 64 };
enum { Hats = 8, Axes = 16, Buttons = 32 };
enum Scancode {
Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7,
Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7,
Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15,
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15,
Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23,
Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31,
Limit,
};
enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 };
static signed numberDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).belongsTo(scancode)) return i;
}
return -1;
}
static signed hatDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0);
}
return -1;
}
static signed axisDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0);
}
return -1;
}
static signed buttonDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0);
}
return -1;
}
static bool isAnyHat(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isHat(scancode)) return true;
}
return false;
}
static bool isAnyAxis(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isAxis(scancode)) return true;
}
return false;
}
static bool isAnyButton(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isButton(scancode)) return true;
}
return false;
}
static uint16_t decode(const char *name) {
string s(name);
if(!strbegin(name, "JP")) return 0;
s.ltrim("JP");
unsigned id = strunsigned(s);
auto pos = strpos(s, "::");
if(!pos) return 0;
s = substr(s, pos() + 2);
for(unsigned i = 0; i < Limit; i++) {
if(s == JoypadScancodeName[i]) return Base + Size * id + i;
}
return 0;
}
string encode(uint16_t code) const {
unsigned index = 0;
for(unsigned i = 0; i < Count; i++) {
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
index = code - (Base + Size * i);
}
}
return string() << "JP" << ID << "::" << JoypadScancodeName[index];
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; }
uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; }
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); }
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); }
bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); }
bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); }
Joypad(unsigned ID_) : ID(ID_) {}
};
inline Joypad& joypad(unsigned id) {
static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7);
switch(id) { default:
case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3;
case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7;
}
}
struct Scancode {
enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count };
static uint16_t decode(const char *name) {
uint16_t code;
code = Keyboard::decode(name);
if(code) return code;
code = Mouse::decode(name);
if(code) return code;
code = Joypad::decode(name);
if(code) return code;
return None;
}
static string encode(uint16_t code) {
for(unsigned i = 0; i < Keyboard::Count; i++) {
if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code);
}
for(unsigned i = 0; i < Mouse::Count; i++) {
if(mouse(i).belongsTo(code)) return mouse(i).encode(code);
}
for(unsigned i = 0; i < Joypad::Count; i++) {
if(joypad(i).belongsTo(code)) return joypad(i).encode(code);
}
return "None";
}
};
}
#endif
-81
View File
@@ -1,81 +0,0 @@
#ifndef NALL_LZSS_HPP
#define NALL_LZSS_HPP
#include <nall/array.hpp>
#include <nall/new.hpp>
#include <nall/stdint.hpp>
namespace nall {
class lzss {
public:
static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) {
output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9];
unsigned i = 0, o = 0;
while(i < inlength) {
unsigned flagoffset = o++;
uint8_t flag = 0x00;
for(unsigned b = 0; b < 8 && i < inlength; b++) {
unsigned longest = 0, pointer;
for(unsigned index = 1; index < 4096; index++) {
unsigned count = 0;
while(true) {
if(count >= 15 + 3) break; //verify pattern match is not longer than max length
if(i + count >= inlength) break; //verify pattern match does not read past end of input
if(i + count < index) break; //verify read is not before start of input
if(input[i + count] != input[i + count - index]) break; //verify pattern still matches
count++;
}
if(count > longest) {
longest = count;
pointer = index;
}
}
if(longest < 3) output[o++] = input[i++];
else {
flag |= 1 << b;
uint16_t x = ((longest - 3) << 12) + pointer;
output[o++] = x;
output[o++] = x >> 8;
i += longest;
}
}
output[flagoffset] = flag;
}
outlength = o;
return true;
}
static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) {
output = new(zeromemory) uint8_t[length];
unsigned i = 0, o = 0;
while(o < length) {
uint8_t flag = input[i++];
for(unsigned b = 0; b < 8 && o < length; b++) {
if(!(flag & (1 << b))) output[o++] = input[i++];
else {
uint16_t offset = input[i++];
offset += input[i++] << 8;
uint16_t lookuplength = (offset >> 12) + 3;
offset &= 4095;
for(unsigned index = 0; index < lookuplength && o + index < length; index++) {
output[o + index] = output[o + index - offset];
}
o += lookuplength;
}
}
}
return true;
}
};
}
#endif
-40
View File
@@ -1,40 +0,0 @@
#ifndef NALL_MODULO_HPP
#define NALL_MODULO_HPP
#include <nall/serializer.hpp>
namespace nall {
template<typename T, int size> class modulo_array {
public:
inline T operator[](int index) const {
return buffer[size + index];
}
inline T read(int index) const {
return buffer[size + index];
}
inline void write(unsigned index, const T value) {
buffer[index] =
buffer[index + size] =
buffer[index + size + size] = value;
}
void serialize(serializer &s) {
s.array(buffer, size * 3);
}
modulo_array() {
buffer = new T[size * 3]();
}
~modulo_array() {
delete[] buffer;
}
private:
T *buffer;
};
}
#endif
-122
View File
@@ -1,122 +0,0 @@
#ifndef NALL_PLATFORM_HPP
#define NALL_PLATFORM_HPP
#include <nall/utf8.hpp>
//=========================
//standard platform headers
//=========================
#include <limits>
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(_WIN32)
#include <io.h>
#include <direct.h>
#include <shlobj.h>
#undef interface
#define dllexport __declspec(dllexport)
#else
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#define dllexport
#endif
//==================
//warning supression
//==================
//Visual C++
#if defined(_MSC_VER)
//disable libc "deprecation" warnings
#pragma warning(disable:4996)
#endif
//================
//POSIX compliance
//================
#if defined(_MSC_VER)
#define PATH_MAX _MAX_PATH
#define va_copy(dest, src) ((dest) = (src))
#endif
#if defined(_WIN32)
#define getcwd _getcwd
#define ftruncate _chsize
#define putenv _putenv
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
#define rmdir _rmdir
#define vsnprintf _vsnprintf
#define usleep(n) Sleep(n / 1000)
#endif
//================
//inline expansion
//================
#if defined(__GNUC__)
#define noinline __attribute__((noinline))
#define inline inline
#define alwaysinline inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define noinline __declspec(noinline)
#define inline inline
#define alwaysinline inline __forceinline
#else
#define noinline
#define inline inline
#define alwaysinline inline
#endif
//=========================
//file system functionality
//=========================
#if defined(_WIN32)
inline char* realpath(const char *filename, char *resolvedname) {
wchar_t fn[_MAX_PATH] = L"";
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
strcpy(resolvedname, nall::utf8_t(fn));
return resolvedname;
}
inline char* userpath(char *path) {
wchar_t fp[_MAX_PATH] = L"";
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
strcpy(path, nall::utf8_t(fp));
return path;
}
inline char* getcwd(char *path) {
wchar_t fp[_MAX_PATH] = L"";
_wgetcwd(fp, _MAX_PATH);
strcpy(path, nall::utf8_t(fp));
return path;
}
#else
//realpath() already exists
inline char* userpath(char *path) {
*path = 0;
struct passwd *userinfo = getpwuid(getuid());
if(userinfo) strcpy(path, userinfo->pw_dir);
return path;
}
inline char *getcwd(char *path) {
return getcwd(path, PATH_MAX);
}
#endif
#endif
-109
View File
@@ -1,109 +0,0 @@
#ifndef NALL_PRIORITYQUEUE_HPP
#define NALL_PRIORITYQUEUE_HPP
#include <limits>
#include <nall/function.hpp>
#include <nall/serializer.hpp>
#include <nall/utility.hpp>
namespace nall {
template<typename type_t> void priority_queue_nocallback(type_t) {}
//priority queue implementation using binary min-heap array;
//does not require normalize() function.
//O(1) find (tick)
//O(log n) insert (enqueue)
//O(log n) remove (dequeue)
template<typename type_t> class priority_queue {
public:
inline void tick(unsigned ticks) {
basecounter += ticks;
while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue());
}
//counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks);
//counter cannot exceed std::numeric_limits<unsigned>::max() >> 1.
void enqueue(unsigned counter, type_t event) {
unsigned child = heapsize++;
counter += basecounter;
while(child) {
unsigned parent = (child - 1) >> 1;
if(gte(counter, heap[parent].counter)) break;
heap[child].counter = heap[parent].counter;
heap[child].event = heap[parent].event;
child = parent;
}
heap[child].counter = counter;
heap[child].event = event;
}
type_t dequeue() {
type_t event(heap[0].event);
unsigned parent = 0;
unsigned counter = heap[--heapsize].counter;
while(true) {
unsigned child = (parent << 1) + 1;
if(child >= heapsize) break;
if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++;
if(gte(heap[child].counter, counter)) break;
heap[parent].counter = heap[child].counter;
heap[parent].event = heap[child].event;
parent = child;
}
heap[parent].counter = counter;
heap[parent].event = heap[heapsize].event;
return event;
}
void reset() {
basecounter = 0;
heapsize = 0;
}
void serialize(serializer &s) {
s.integer(basecounter);
s.integer(heapsize);
for(unsigned n = 0; n < heapcapacity; n++) {
s.integer(heap[n].counter);
s.integer(heap[n].event);
}
}
priority_queue(unsigned size, function<void (type_t)> callback_ = &priority_queue_nocallback<type_t>)
: callback(callback_) {
heap = new heap_t[size];
heapcapacity = size;
reset();
}
~priority_queue() {
delete[] heap;
}
priority_queue& operator=(const priority_queue&) = delete;
priority_queue(const priority_queue&) = delete;
private:
function<void (type_t)> callback;
unsigned basecounter;
unsigned heapsize;
unsigned heapcapacity;
struct heap_t {
unsigned counter;
type_t event;
} *heap;
//return true if x is greater than or equal to y
inline bool gte(unsigned x, unsigned y) {
return x - y < (std::numeric_limits<unsigned>::max() >> 1);
}
};
}
#endif
-91
View File
@@ -1,91 +0,0 @@
#ifndef NALL_PROPERTY_HPP
#define NALL_PROPERTY_HPP
//nall::property implements ownership semantics into container classes
//example: property<owner>::readonly<type> implies that only owner has full
//access to type; and all other code has readonly access.
//
//this code relies on extended friend semantics from C++0x to work, as it
//declares a friend class via a template paramter. it also exploits a bug in
//G++ 4.x to work even in C++98 mode.
//
//if compiling elsewhere, simply remove the friend class and private semantics
//property can be used either of two ways:
//struct foo {
// property<foo>::readonly<bool> x;
// property<foo>::readwrite<int> y;
//};
//-or-
//struct foo : property<foo> {
// readonly<bool> x;
// readwrite<int> y;
//};
//return types are const T& (byref) instead fo T (byval) to avoid major speed
//penalties for objects with expensive copy constructors
//operator-> provides access to underlying object type:
//readonly<Object> foo;
//foo->bar();
//... will call Object::bar();
//operator='s reference is constant so as to avoid leaking a reference handle
//that could bypass access restrictions
//both constant and non-constant operators are provided, though it may be
//necessary to cast first, for instance:
//struct foo : property<foo> { readonly<int> bar; } object;
//int main() { int value = const_cast<const foo&>(object); }
//writeonly is useful for objects that have non-const reads, but const writes.
//however, to avoid leaking handles, the interface is very restricted. the only
//way to write is via operator=, which requires conversion via eg copy
//constructor. example:
//struct foo {
// foo(bool value) { ... }
//};
//writeonly<foo> bar;
//bar = true;
namespace nall {
template<typename C> struct property {
template<typename T> struct traits { typedef T type; };
template<typename T> struct readonly {
const T* operator->() const { return &value; }
const T& operator()() const { return value; }
operator const T&() const { return value; }
private:
T* operator->() { return &value; }
operator T&() { return value; }
const T& operator=(const T& value_) { return value = value_; }
T value;
friend class traits<C>::type;
};
template<typename T> struct writeonly {
void operator=(const T& value_) { value = value_; }
private:
const T* operator->() const { return &value; }
const T& operator()() const { return value; }
operator const T&() const { return value; }
T* operator->() { return &value; }
operator T&() { return value; }
T value;
friend class traits<C>::type;
};
template<typename T> struct readwrite {
const T* operator->() const { return &value; }
const T& operator()() const { return value; }
operator const T&() const { return value; }
T* operator->() { return &value; }
operator T&() { return value; }
const T& operator=(const T& value_) { return value = value_; }
T value;
};
};
}
#endif
-55
View File
@@ -1,55 +0,0 @@
# requires nall/Makefile
# imports:
# $(qtlibs) -- list of Qt components to link against
# exports the following symbols:
# $(moc) -- meta-object compiler
# $(rcc) -- resource compiler
# $(qtinc) -- includes for compiling
# $(qtlib) -- libraries for linking
ifeq ($(moc),)
moc := moc
endif
ifeq ($(rcc),)
rcc := rcc
endif
ifeq ($(platform),x)
qtinc := `pkg-config --cflags $(qtlibs)`
qtlib := `pkg-config --libs $(qtlibs)`
else ifeq ($(platform),osx)
qtinc := $(foreach lib,$(qtlibs),-I/Library/Frameworks/$(lib).framework/Versions/4/Headers)
qtlib := -L/Library/Frameworks
qtlib += $(foreach lib,$(qtlibs),-framework $(lib))
qtlib += -framework Carbon
qtlib += -framework Cocoa
qtlib += -framework OpenGL
qtlib += -framework AppKit
qtlib += -framework ApplicationServices
else ifeq ($(platform),win)
ifeq ($(qtpath),)
# find Qt install directory from PATH environment variable
qtpath := $(foreach path,$(subst ;, ,$(PATH)),$(if $(wildcard $(path)/$(moc).exe),$(path)))
qtpath := $(strip $(qtpath))
qtpath := $(subst \,/,$(qtpath))
qtpath := $(patsubst %/bin,%,$(qtpath))
endif
qtinc := -I$(qtpath)/include
qtinc += $(foreach lib,$(qtlibs),-I$(qtpath)/include/$(lib))
qtlib := -L$(qtpath)/lib
qtlib += -L$(qtpath)/plugins/imageformats
qtlib += $(foreach lib,$(qtlibs),-l$(lib)4)
qtlib += -lmingw32 -lqtmain -lcomdlg32 -loleaut32 -limm32 -lwinmm
qtlib += -lwinspool -lmsimg32 -lole32 -ladvapi32 -lws2_32 -luuid -lgdi32
qtlib += $(foreach lib,$(qtlibs),-l$(lib)4)
# optional image-file support:
# qtlib += -lqjpeg -lqmng
endif
-41
View File
@@ -1,41 +0,0 @@
#ifndef NALL_QT_CHECKACTION_HPP
#define NALL_QT_CHECKACTION_HPP
namespace nall {
class CheckAction : public QAction {
Q_OBJECT
public:
bool isChecked() const;
void setChecked(bool);
void toggleChecked();
CheckAction(const QString&, QObject*);
protected slots:
protected:
bool checked;
};
inline bool CheckAction::isChecked() const {
return checked;
}
inline void CheckAction::setChecked(bool checked_) {
checked = checked_;
if(checked) setIcon(QIcon(":/16x16/item-check-on.png"));
else setIcon(QIcon(":/16x16/item-check-off.png"));
}
inline void CheckAction::toggleChecked() {
setChecked(!isChecked());
}
inline CheckAction::CheckAction(const QString &text, QObject *parent) : QAction(text, parent) {
setChecked(false);
}
}
#endif
-10
View File
@@ -1,10 +0,0 @@
#ifndef NALL_QT_CONCEPT_HPP
#define NALL_QT_CONCEPT_HPP
#include <nall/concept.hpp>
namespace nall {
template<typename T> struct has_count<QList<T>> { enum { value = true }; };
}
#endif
-392
View File
@@ -1,392 +0,0 @@
#ifndef NALL_QT_FILEDIALOG_HPP
#define NALL_QT_FILEDIALOG_HPP
#include <nall/platform.hpp>
#include <nall/string.hpp>
#include <nall/qt/window.moc.hpp>
namespace nall {
class FileDialog;
class NewFolderDialog : public Window {
Q_OBJECT
public:
void show();
NewFolderDialog(FileDialog*);
protected slots:
void createFolderAction();
protected:
FileDialog *parent;
QVBoxLayout *layout;
QLineEdit *folderNameEdit;
QHBoxLayout *controlLayout;
QPushButton *okButton;
QPushButton *cancelButton;
};
class FileView : public QListView {
Q_OBJECT
protected:
void keyPressEvent(QKeyEvent*);
signals:
void changed(const QModelIndex&);
void browseUp();
protected slots:
void currentChanged(const QModelIndex&, const QModelIndex&);
};
class FileDialog : public Window {
Q_OBJECT
public:
void showLoad();
void showSave();
void showFolder();
void setPath(string path);
void setNameFilters(const string &filters);
FileDialog();
signals:
void changed(const string&);
void activated(const string&);
void accepted(const string&);
void rejected();
protected slots:
void fileViewChange(const QModelIndex&);
void fileViewActivate(const QModelIndex&);
void pathBoxChanged();
void filterBoxChanged();
void createNewFolder();
void browseUp();
void acceptAction();
void rejectAction();
protected:
NewFolderDialog *newFolderDialog;
QVBoxLayout *layout;
QHBoxLayout *navigationLayout;
QComboBox *pathBox;
QPushButton *newFolderButton;
QPushButton *upFolderButton;
QHBoxLayout *browseLayout;
QFileSystemModel *fileSystemModel;
FileView *fileView;
QGroupBox *previewFrame;
QLineEdit *fileNameEdit;
QHBoxLayout *controlLayout;
QComboBox *filterBox;
QPushButton *optionsButton;
QPushButton *acceptButton;
QPushButton *rejectButton;
bool lock;
void createFolderAction(const string &name);
void closeEvent(QCloseEvent*);
friend class NewFolderDialog;
};
inline void NewFolderDialog::show() {
folderNameEdit->setText("");
Window::show();
folderNameEdit->setFocus();
}
inline void NewFolderDialog::createFolderAction() {
string name = folderNameEdit->text().toUtf8().constData();
if(name == "") {
folderNameEdit->setFocus();
} else {
parent->createFolderAction(name);
close();
}
}
inline NewFolderDialog::NewFolderDialog(FileDialog *fileDialog) : parent(fileDialog) {
setMinimumWidth(240);
setWindowTitle("Create New Folder");
layout = new QVBoxLayout;
layout->setAlignment(Qt::AlignTop);
layout->setMargin(5);
layout->setSpacing(5);
setLayout(layout);
folderNameEdit = new QLineEdit;
layout->addWidget(folderNameEdit);
controlLayout = new QHBoxLayout;
controlLayout->setAlignment(Qt::AlignRight);
layout->addLayout(controlLayout);
okButton = new QPushButton("Ok");
controlLayout->addWidget(okButton);
cancelButton = new QPushButton("Cancel");
controlLayout->addWidget(cancelButton);
connect(folderNameEdit, SIGNAL(returnPressed()), this, SLOT(createFolderAction()));
connect(okButton, SIGNAL(released()), this, SLOT(createFolderAction()));
connect(cancelButton, SIGNAL(released()), this, SLOT(close()));
}
inline void FileView::currentChanged(const QModelIndex &current, const QModelIndex &previous) {
QAbstractItemView::currentChanged(current, previous);
emit changed(current);
}
inline void FileView::keyPressEvent(QKeyEvent *event) {
//enhance consistency: force OS X to act like Windows and Linux; enter = activate item
if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
emit activated(currentIndex());
return;
}
//simulate popular file manager behavior; backspace = go up one directory
if(event->key() == Qt::Key_Backspace) {
emit browseUp();
return;
}
//fallback: unrecognized keypresses get handled by the widget itself
QListView::keyPressEvent(event);
}
inline void FileDialog::showLoad() {
acceptButton->setText("Load");
fileNameEdit->hide();
filterBox->show();
show();
}
inline void FileDialog::showSave() {
acceptButton->setText("Save");
fileNameEdit->show();
filterBox->show();
show();
}
inline void FileDialog::showFolder() {
acceptButton->setText("Choose");
fileNameEdit->hide();
filterBox->hide();
setNameFilters("Folders ()");
show();
}
inline void FileDialog::fileViewChange(const QModelIndex &index) {
string path = fileSystemModel->filePath(index).toUtf8().constData();
if(path == fileSystemModel->rootPath().toUtf8().constData()) path = "";
fileNameEdit->setText(notdir(path));
emit changed(path);
}
inline void FileDialog::fileViewActivate(const QModelIndex &index) {
string path = fileSystemModel->filePath(index).toUtf8().constData();
if(fileSystemModel->isDir(index)) {
emit activated(path);
setPath(path);
} else {
emit activated(path);
close();
}
}
inline void FileDialog::pathBoxChanged() {
if(lock) return;
setPath(pathBox->currentText().toUtf8().constData());
}
inline void FileDialog::filterBoxChanged() {
if(lock) return;
string filters = filterBox->currentText().toUtf8().constData();
if(filters.length() == 0) {
fileSystemModel->setNameFilters(QStringList() << "*");
} else {
filters = substr(filters, strpos(filters, "(")());
filters.ltrim("(");
filters.rtrim(")");
lstring part;
part.split(" ", filters);
QStringList list;
for(unsigned i = 0; i < part.size(); i++) list << part[i];
fileSystemModel->setNameFilters(list);
}
}
inline void FileDialog::createNewFolder() {
newFolderDialog->show();
}
inline void FileDialog::browseUp() {
if(pathBox->count() > 1) pathBox->setCurrentIndex(1);
}
inline void FileDialog::setPath(string path) {
lock = true;
newFolderDialog->close();
if(QDir(path).exists()) {
newFolderButton->setEnabled(true);
} else {
newFolderButton->setEnabled(false);
path = "";
}
fileSystemModel->setRootPath(path);
fileView->setRootIndex(fileSystemModel->index(path));
fileView->setCurrentIndex(fileView->rootIndex());
fileView->setFocus();
pathBox->clear();
if(path.length() > 0) {
QDir directory(path);
while(true) {
pathBox->addItem(directory.absolutePath());
if(directory.isRoot()) break;
directory.cdUp();
}
}
pathBox->addItem("<root>");
fileNameEdit->setText("");
lock = false;
}
inline void FileDialog::setNameFilters(const string &filters) {
lock = true;
lstring list;
list.split("\n", filters);
filterBox->clear();
for(unsigned i = 0; i < list.size(); i++) {
filterBox->addItem(list[i]);
}
lock = false;
filterBoxChanged();
}
inline void FileDialog::acceptAction() {
string path = fileSystemModel->rootPath().toUtf8().constData();
path << "/" << notdir(fileNameEdit->text().toUtf8().constData());
path.rtrim("/");
if(QDir(path).exists()) {
emit accepted(path);
setPath(path);
} else {
emit accepted(path);
close();
}
}
inline void FileDialog::rejectAction() {
emit rejected();
close();
}
inline void FileDialog::createFolderAction(const string &name) {
string path = fileSystemModel->rootPath().toUtf8().constData();
path << "/" << notdir(name);
mkdir(path, 0755);
}
inline void FileDialog::closeEvent(QCloseEvent *event) {
newFolderDialog->close();
Window::closeEvent(event);
}
inline FileDialog::FileDialog() {
newFolderDialog = new NewFolderDialog(this);
resize(640, 360);
layout = new QVBoxLayout;
layout->setMargin(5);
layout->setSpacing(5);
setLayout(layout);
navigationLayout = new QHBoxLayout;
layout->addLayout(navigationLayout);
pathBox = new QComboBox;
pathBox->setEditable(true);
pathBox->setMinimumContentsLength(16);
pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
pathBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
navigationLayout->addWidget(pathBox);
newFolderButton = new QPushButton;
newFolderButton->setIconSize(QSize(16, 16));
newFolderButton->setIcon(QIcon(":/16x16/folder-new.png"));
navigationLayout->addWidget(newFolderButton);
upFolderButton = new QPushButton;
upFolderButton->setIconSize(QSize(16, 16));
upFolderButton->setIcon(QIcon(":/16x16/go-up.png"));
navigationLayout->addWidget(upFolderButton);
browseLayout = new QHBoxLayout;
layout->addLayout(browseLayout);
fileSystemModel = new QFileSystemModel;
fileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
fileSystemModel->setNameFilterDisables(false);
fileView = new FileView;
fileView->setMinimumWidth(320);
fileView->setModel(fileSystemModel);
fileView->setIconSize(QSize(16, 16));
browseLayout->addWidget(fileView);
previewFrame = new QGroupBox;
previewFrame->hide();
browseLayout->addWidget(previewFrame);
fileNameEdit = new QLineEdit;
layout->addWidget(fileNameEdit);
controlLayout = new QHBoxLayout;
controlLayout->setAlignment(Qt::AlignRight);
layout->addLayout(controlLayout);
filterBox = new QComboBox;
filterBox->setMinimumContentsLength(16);
filterBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
filterBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
controlLayout->addWidget(filterBox);
optionsButton = new QPushButton("Options");
optionsButton->hide();
controlLayout->addWidget(optionsButton);
acceptButton = new QPushButton("Ok");
controlLayout->addWidget(acceptButton);
rejectButton = new QPushButton("Cancel");
controlLayout->addWidget(rejectButton);
lock = false;
connect(pathBox, SIGNAL(currentIndexChanged(int)), this, SLOT(pathBoxChanged()));
connect(newFolderButton, SIGNAL(released()), this, SLOT(createNewFolder()));
connect(upFolderButton, SIGNAL(released()), this, SLOT(browseUp()));
connect(fileView, SIGNAL(changed(const QModelIndex&)), this, SLOT(fileViewChange(const QModelIndex&)));
connect(fileView, SIGNAL(activated(const QModelIndex&)), this, SLOT(fileViewActivate(const QModelIndex&)));
connect(fileView, SIGNAL(browseUp()), this, SLOT(browseUp()));
connect(fileNameEdit, SIGNAL(returnPressed()), this, SLOT(acceptAction()));
connect(filterBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterBoxChanged()));
connect(acceptButton, SIGNAL(released()), this, SLOT(acceptAction()));
connect(rejectButton, SIGNAL(released()), this, SLOT(rejectAction()));
}
}
#endif
-173
View File
@@ -1,173 +0,0 @@
#ifndef NALL_QT_HEXEDITOR_HPP
#define NALL_QT_HEXEDITOR_HPP
#include <nall/function.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
class HexEditor : public QTextEdit {
Q_OBJECT
public:
function<uint8_t (unsigned)> reader;
function<void (unsigned, uint8_t)> writer;
void setColumns(unsigned columns);
void setRows(unsigned rows);
void setOffset(unsigned offset);
void setSize(unsigned size);
unsigned lineWidth() const;
void refresh();
HexEditor();
protected slots:
void scrolled();
protected:
QHBoxLayout *layout;
QScrollBar *scrollBar;
unsigned editorColumns;
unsigned editorRows;
unsigned editorOffset;
unsigned editorSize;
bool lock;
void keyPressEvent(QKeyEvent*);
};
inline void HexEditor::keyPressEvent(QKeyEvent *event) {
QTextCursor cursor = textCursor();
unsigned x = cursor.position() % lineWidth();
unsigned y = cursor.position() / lineWidth();
int hexCode = -1;
switch(event->key()) {
case Qt::Key_0: hexCode = 0; break;
case Qt::Key_1: hexCode = 1; break;
case Qt::Key_2: hexCode = 2; break;
case Qt::Key_3: hexCode = 3; break;
case Qt::Key_4: hexCode = 4; break;
case Qt::Key_5: hexCode = 5; break;
case Qt::Key_6: hexCode = 6; break;
case Qt::Key_7: hexCode = 7; break;
case Qt::Key_8: hexCode = 8; break;
case Qt::Key_9: hexCode = 9; break;
case Qt::Key_A: hexCode = 10; break;
case Qt::Key_B: hexCode = 11; break;
case Qt::Key_C: hexCode = 12; break;
case Qt::Key_D: hexCode = 13; break;
case Qt::Key_E: hexCode = 14; break;
case Qt::Key_F: hexCode = 15; break;
}
if(cursor.hasSelection() == false && hexCode != -1) {
bool cursorOffsetValid = (x >= 11 && ((x - 11) % 3) != 2);
if(cursorOffsetValid) {
bool nibble = (x - 11) % 3; //0 = top nibble, 1 = bottom nibble
unsigned cursorOffset = y * editorColumns + ((x - 11) / 3);
unsigned effectiveOffset = editorOffset + cursorOffset;
if(effectiveOffset >= editorSize) effectiveOffset %= editorSize;
uint8_t data = reader ? reader(effectiveOffset) : 0x00;
data &= (nibble == 0 ? 0x0f : 0xf0);
data |= (nibble == 0 ? (hexCode << 4) : (hexCode << 0));
if(writer) writer(effectiveOffset, data);
refresh();
cursor.setPosition(y * lineWidth() + x + 1); //advance cursor
setTextCursor(cursor);
}
} else {
//allow navigation keys to move cursor, but block text input
setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
QTextEdit::keyPressEvent(event);
setTextInteractionFlags(Qt::TextEditorInteraction);
}
}
inline void HexEditor::setColumns(unsigned columns) {
editorColumns = columns;
}
inline void HexEditor::setRows(unsigned rows) {
editorRows = rows;
scrollBar->setPageStep(editorRows);
}
inline void HexEditor::setOffset(unsigned offset) {
lock = true;
editorOffset = offset;
scrollBar->setSliderPosition(editorOffset / editorColumns);
lock = false;
}
inline void HexEditor::setSize(unsigned size) {
editorSize = size;
bool indivisible = (editorSize % editorColumns) != 0; //add one for incomplete row
scrollBar->setRange(0, editorSize / editorColumns + indivisible - editorRows);
}
inline unsigned HexEditor::lineWidth() const {
return 11 + 3 * editorColumns;
}
inline void HexEditor::refresh() {
string output;
char temp[256];
unsigned offset = editorOffset;
for(unsigned y = 0; y < editorRows; y++) {
if(offset >= editorSize) break;
sprintf(temp, "%.4x:%.4x", (offset >> 16) & 0xffff, (offset >> 0) & 0xffff);
output << "<font color='#808080'>" << temp << "</font>&nbsp;&nbsp;";
for(unsigned x = 0; x < editorColumns; x++) {
if(offset >= editorSize) break;
sprintf(temp, "%.2x", reader ? reader(offset) : 0x00);
offset++;
output << "<font color='" << ((x & 1) ? "#000080" : "#0000ff") << "'>" << temp << "</font>";
if(x != (editorColumns - 1)) output << "&nbsp;";
}
if(y != (editorRows - 1)) output << "<br>";
}
setHtml(output);
}
inline void HexEditor::scrolled() {
if(lock) return;
unsigned offset = scrollBar->sliderPosition();
editorOffset = offset * editorColumns;
refresh();
}
inline HexEditor::HexEditor() {
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
layout = new QHBoxLayout;
layout->setAlignment(Qt::AlignRight);
layout->setMargin(0);
layout->setSpacing(0);
setLayout(layout);
scrollBar = new QScrollBar(Qt::Vertical);
scrollBar->setSingleStep(1);
layout->addWidget(scrollBar);
lock = false;
connect(scrollBar, SIGNAL(actionTriggered(int)), this, SLOT(scrolled()));
setColumns(16);
setRows(16);
setSize(0);
setOffset(0);
}
}
#endif
-41
View File
@@ -1,41 +0,0 @@
#ifndef NALL_QT_RADIOACTION_HPP
#define NALL_QT_RADIOACTION_HPP
namespace nall {
class RadioAction : public QAction {
Q_OBJECT
public:
bool isChecked() const;
void setChecked(bool);
void toggleChecked();
RadioAction(const QString&, QObject*);
protected slots:
protected:
bool checked;
};
inline bool RadioAction::isChecked() const {
return checked;
}
inline void RadioAction::setChecked(bool checked_) {
checked = checked_;
if(checked) setIcon(QIcon(":/16x16/item-radio-on.png"));
else setIcon(QIcon(":/16x16/item-radio-off.png"));
}
inline void RadioAction::toggleChecked() {
setChecked(!isChecked());
}
inline RadioAction::RadioAction(const QString &text, QObject *parent) : QAction(text, parent) {
setChecked(false);
}
}
#endif
-105
View File
@@ -1,105 +0,0 @@
#ifndef NALL_QT_WINDOW_HPP
#define NALL_QT_WINDOW_HPP
#include <nall/base64.hpp>
#include <nall/string.hpp>
namespace nall {
class Window : public QWidget {
Q_OBJECT
public:
void setGeometryString(string *geometryString);
void setCloseOnEscape(bool);
void show();
void hide();
void shrink();
Window();
protected slots:
protected:
string *geometryString;
bool closeOnEscape;
void keyReleaseEvent(QKeyEvent *event);
void closeEvent(QCloseEvent *event);
};
inline void Window::setGeometryString(string *geometryString_) {
geometryString = geometryString_;
if(geometryString && isVisible() == false) {
uint8_t *data;
unsigned length;
base64::decode(data, length, *geometryString);
QByteArray array((const char*)data, length);
delete[] data;
restoreGeometry(array);
}
}
inline void Window::setCloseOnEscape(bool value) {
closeOnEscape = value;
}
inline void Window::show() {
if(geometryString && isVisible() == false) {
uint8_t *data;
unsigned length;
base64::decode(data, length, *geometryString);
QByteArray array((const char*)data, length);
delete[] data;
restoreGeometry(array);
}
QWidget::show();
QApplication::processEvents();
activateWindow();
raise();
}
inline void Window::hide() {
if(geometryString && isVisible() == true) {
char *data;
QByteArray geometry = saveGeometry();
base64::encode(data, (const uint8_t*)geometry.data(), geometry.length());
*geometryString = data;
delete[] data;
}
QWidget::hide();
}
inline void Window::shrink() {
if(isFullScreen()) return;
for(unsigned i = 0; i < 2; i++) {
resize(0, 0);
usleep(2000);
QApplication::processEvents();
}
}
inline void Window::keyReleaseEvent(QKeyEvent *event) {
if(closeOnEscape && (event->key() == Qt::Key_Escape)) close();
QWidget::keyReleaseEvent(event);
}
inline void Window::closeEvent(QCloseEvent *event) {
if(geometryString) {
char *data;
QByteArray geometry = saveGeometry();
base64::encode(data, (const uint8_t*)geometry.data(), geometry.length());
*geometryString = data;
delete[] data;
}
QWidget::closeEvent(event);
}
inline Window::Window() {
geometryString = 0;
closeOnEscape = true;
}
}
#endif
-20
View File
@@ -1,20 +0,0 @@
#ifndef NALL_RANDOM_HPP
#define NALL_RANDOM_HPP
namespace nall {
//pseudo-random number generator
inline unsigned prng() {
static unsigned n = 0;
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
}
struct random_cyclic {
unsigned seed;
inline unsigned operator()() {
return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320);
}
random_cyclic() : seed(0) {}
};
}
#endif
-85
View File
@@ -1,85 +0,0 @@
#ifndef NALL_SERIAL_HPP
#define NALL_SERIAL_HPP
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <nall/stdint.hpp>
namespace nall {
class serial {
public:
//-1 on error, otherwise return bytes read
int read(uint8_t *data, unsigned length) {
if(port_open == false) return -1;
return ::read(port, (void*)data, length);
}
//-1 on error, otherwise return bytes written
int write(const uint8_t *data, unsigned length) {
if(port_open == false) return -1;
return ::write(port, (void*)data, length);
}
bool open(const char *portname, unsigned rate, bool flowcontrol) {
close();
port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if(port == -1) return false;
if(ioctl(port, TIOCEXCL) == -1) { close(); return false; }
if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; }
if(tcgetattr(port, &original_attr) == -1) { close(); return false; }
termios attr = original_attr;
cfmakeraw(&attr);
cfsetspeed(&attr, rate);
attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN);
attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
attr.c_iflag |= (IGNBRK | IGNPAR);
attr.c_oflag &=~ (OPOST);
attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL);
attr.c_cflag |= (CS8 | CREAD);
if(flowcontrol == false) {
attr.c_cflag &= ~CRTSCTS;
} else {
attr.c_cflag |= CRTSCTS;
}
attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0;
if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; }
return port_open = true;
}
void close() {
if(port != -1) {
tcdrain(port);
if(port_open == true) {
tcsetattr(port, TCSANOW, &original_attr);
port_open = false;
}
::close(port);
port = -1;
}
}
serial() {
port = -1;
port_open = false;
}
~serial() {
close();
}
private:
int port;
bool port_open;
termios original_attr;
};
}
#endif
-145
View File
@@ -1,145 +0,0 @@
#ifndef NALL_SERIALIZER_HPP
#define NALL_SERIALIZER_HPP
#include <type_traits>
#include <utility>
#include <nall/stdint.hpp>
#include <nall/utility.hpp>
namespace nall {
//serializer: a class designed to save and restore the state of classes.
//
//benefits:
//- data() will be portable in size (it is not necessary to specify type sizes.)
//- data() will be portable in endianness (always stored internally as little-endian.)
//- one serialize function can both save and restore class states.
//
//caveats:
//- only plain-old-data can be stored. complex classes must provide serialize(serializer&);
//- floating-point usage is not portable across platforms
class serializer {
public:
enum mode_t { Load, Save, Size };
mode_t mode() const {
return imode;
}
const uint8_t* data() const {
return idata;
}
unsigned size() const {
return isize;
}
unsigned capacity() const {
return icapacity;
}
template<typename T> void floatingpoint(T &value) {
enum { size = sizeof(T) };
//this is rather dangerous, and not cross-platform safe;
//but there is no standardized way to export FP-values
uint8_t *p = (uint8_t*)&value;
if(imode == Save) {
for(unsigned n = 0; n < size; n++) idata[isize++] = p[n];
} else if(imode == Load) {
for(unsigned n = 0; n < size; n++) p[n] = idata[isize++];
} else {
isize += size;
}
}
template<typename T> void integer(T &value) {
enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
if(imode == Save) {
for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3);
} else if(imode == Load) {
value = 0;
for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3);
} else if(imode == Size) {
isize += size;
}
}
template<typename T> void array(T &array) {
enum { size = sizeof(T) / sizeof(typename std::remove_extent<T>::type) };
for(unsigned n = 0; n < size; n++) integer(array[n]);
}
template<typename T> void array(T array, unsigned size) {
for(unsigned n = 0; n < size; n++) integer(array[n]);
}
//copy
serializer& operator=(const serializer &s) {
if(idata) delete[] idata;
imode = s.imode;
idata = new uint8_t[s.icapacity];
isize = s.isize;
icapacity = s.icapacity;
memcpy(idata, s.idata, s.icapacity);
return *this;
}
serializer(const serializer &s) : idata(0) {
operator=(s);
}
//move
serializer& operator=(serializer &&s) {
if(idata) delete[] idata;
imode = s.imode;
idata = s.idata;
isize = s.isize;
icapacity = s.icapacity;
s.idata = 0;
return *this;
}
serializer(serializer &&s) {
operator=(std::move(s));
}
//construction
serializer() {
imode = Size;
idata = 0;
isize = 0;
}
serializer(unsigned capacity) {
imode = Save;
idata = new uint8_t[capacity]();
isize = 0;
icapacity = capacity;
}
serializer(const uint8_t *data, unsigned capacity) {
imode = Load;
idata = new uint8_t[capacity];
isize = 0;
icapacity = capacity;
memcpy(idata, data, capacity);
}
~serializer() {
if(idata) delete[] idata;
}
private:
mode_t imode;
uint8_t *idata;
unsigned isize;
unsigned icapacity;
};
};
#endif
-143
View File
@@ -1,143 +0,0 @@
#ifndef NALL_SHA256_HPP
#define NALL_SHA256_HPP
//author: vladitx
namespace nall {
#define PTR(t, a) ((t*)(a))
#define SWAP32(x) ((uint32_t)( \
(((uint32_t)(x) & 0x000000ff) << 24) | \
(((uint32_t)(x) & 0x0000ff00) << 8) | \
(((uint32_t)(x) & 0x00ff0000) >> 8) | \
(((uint32_t)(x) & 0xff000000) >> 24) \
))
#define ST32(a, d) *PTR(uint32_t, a) = (d)
#define ST32BE(a, d) ST32(a, SWAP32(d))
#define LD32(a) *PTR(uint32_t, a)
#define LD32BE(a) SWAP32(LD32(a))
#define LSL32(x, n) ((uint32_t)(x) << (n))
#define LSR32(x, n) ((uint32_t)(x) >> (n))
#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n)))
//first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
static const uint32_t T_H[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
};
//first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311
static const uint32_t T_K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
};
struct sha256_ctx {
uint8_t in[64];
unsigned inlen;
uint32_t w[64];
uint32_t h[8];
uint64_t len;
};
void sha256_init(sha256_ctx *p) {
memset(p, 0, sizeof(sha256_ctx));
memcpy(p->h, T_H, sizeof(T_H));
}
static void sha256_block(sha256_ctx *p) {
unsigned i;
uint32_t s0, s1;
uint32_t a, b, c, d, e, f, g, h;
uint32_t t1, t2, maj, ch;
for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4);
for(i = 16; i < 64; i++) {
s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3);
s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10);
p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1;
}
a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3];
e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7];
for(i = 0; i < 64; i++) {
s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22);
maj = (a & b) ^ (a & c) ^ (b & c);
t2 = s0 + maj;
s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25);
ch = (e & f) ^ (~e & g);
t1 = h + s1 + ch + T_K[i] + p->w[i];
h = g; g = f; f = e; e = d + t1;
d = c; c = b; b = a; a = t1 + t2;
}
p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d;
p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h;
//next block
p->inlen = 0;
}
void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
unsigned l;
p->len += len;
while(len) {
l = 64 - p->inlen;
l = (len < l) ? len : l;
memcpy(p->in + p->inlen, s, l);
s += l;
p->inlen += l;
len -= l;
if(p->inlen == 64) sha256_block(p);
}
}
void sha256_final(sha256_ctx *p) {
uint64_t len;
p->in[p->inlen++] = 0x80;
if(p->inlen > 56) {
memset(p->in + p->inlen, 0, 64 - p->inlen);
sha256_block(p);
}
memset(p->in + p->inlen, 0, 56 - p->inlen);
len = p->len << 3;
ST32BE(p->in + 56, len >> 32);
ST32BE(p->in + 60, len);
sha256_block(p);
}
void sha256_hash(sha256_ctx *p, uint8_t *s) {
uint32_t *t = (uint32_t*)s;
for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
}
#undef PTR
#undef SWAP32
#undef ST32
#undef ST32BE
#undef LD32
#undef LD32BE
#undef LSL32
#undef LSR32
#undef ROR32
}
#endif
-864
View File
@@ -1,864 +0,0 @@
#ifndef NALL_SNES_INFO_HPP
#define NALL_SNES_INFO_HPP
namespace nall {
class snes_information {
public:
string xml_memory_map;
inline snes_information(const uint8_t *data, unsigned size);
private:
inline void read_header(const uint8_t *data, unsigned size);
inline unsigned find_header(const uint8_t *data, unsigned size);
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size);
inline bool gameboy_has_rtc(const uint8_t *data, unsigned size);
enum HeaderField {
CartName = 0x00,
Mapper = 0x15,
RomType = 0x16,
RomSize = 0x17,
RamSize = 0x18,
CartRegion = 0x19,
Company = 0x1a,
Version = 0x1b,
Complement = 0x1c, //inverse checksum
Checksum = 0x1e,
ResetVector = 0x3c,
};
enum Mode {
ModeNormal,
ModeBsxSlotted,
ModeBsx,
ModeSufamiTurbo,
ModeSuperGameBoy,
};
enum Type {
TypeNormal,
TypeBsxSlotted,
TypeBsxBios,
TypeBsx,
TypeSufamiTurboBios,
TypeSufamiTurbo,
TypeSuperGameBoy1Bios,
TypeSuperGameBoy2Bios,
TypeGameBoy,
TypeUnknown,
};
enum Region {
NTSC,
PAL,
};
enum MemoryMapper {
LoROM,
HiROM,
ExLoROM,
ExHiROM,
SuperFXROM,
SA1ROM,
SPC7110ROM,
BSCLoROM,
BSCHiROM,
BSXROM,
STROM,
};
enum DSP1MemoryMapper {
DSP1Unmapped,
DSP1LoROM1MB,
DSP1LoROM2MB,
DSP1HiROM,
};
bool loaded; //is a base cartridge inserted?
unsigned crc32; //crc32 of all cartridges (base+slot(s))
unsigned rom_size;
unsigned ram_size;
Mode mode;
Type type;
Region region;
MemoryMapper mapper;
DSP1MemoryMapper dsp1_mapper;
bool has_bsx_slot;
bool has_superfx;
bool has_sa1;
bool has_srtc;
bool has_sdd1;
bool has_spc7110;
bool has_spc7110rtc;
bool has_cx4;
bool has_dsp1;
bool has_dsp2;
bool has_dsp3;
bool has_dsp4;
bool has_obc1;
bool has_st010;
bool has_st011;
bool has_st018;
};
snes_information::snes_information(const uint8_t *data, unsigned size) {
read_header(data, size);
string xml = "<?xml version='1.0' encoding='UTF-8'?>\n";
if(type == TypeBsx) {
xml << "<cartridge/>";
xml_memory_map = xml;
return;
}
if(type == TypeSufamiTurbo) {
xml << "<cartridge/>";
xml_memory_map = xml;
return;
}
if(type == TypeGameBoy) {
xml << "<cartridge rtc='" << gameboy_has_rtc(data, size) << "'>\n";
if(gameboy_ram_size(data, size) > 0) {
xml << " <ram size='" << strhex(gameboy_ram_size(data, size)) << "'/>\n";
}
xml << "</cartridge>\n";
xml_memory_map = xml;
return;
}
xml << "<cartridge";
if(region == NTSC) {
xml << " region='NTSC'";
} else {
xml << " region='PAL'";
}
xml << ">\n";
if(type == TypeSuperGameBoy1Bios) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <supergameboy revision='1'>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " </supergameboy>\n";
} else if(type == TypeSuperGameBoy2Bios) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <supergameboy revision='2'>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " </supergameboy>\n";
} else if(has_spc7110) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-0f:8000-ffff'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-cf:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <spc7110>\n";
xml << " <mcu>\n";
xml << " <map address='d0-ff:0000-ffff' offset='100000' size='" << strhex(size - 0x100000) << "'/>\n";
xml << " </mcu>\n";
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='00:6000-7fff'/>\n";
xml << " <map mode='linear' address='30:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:4800-483f'/>\n";
xml << " <map address='80-bf:4800-483f'/>\n";
xml << " </mmio>\n";
if(has_spc7110rtc) {
xml << " <rtc>\n";
xml << " <map address='00-3f:4840-4842'/>\n";
xml << " <map address='80-bf:4840-4842'/>\n";
xml << " </rtc>\n";
}
xml << " <dcu>\n";
xml << " <map address='50:0000-ffff'/>\n";
xml << " </dcu>\n";
xml << " </spc7110>\n";
} else if(mapper == LoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
if(ram_size > 0) {
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-ffff'/>\n";
}
xml << " </ram>\n";
}
} else if(mapper == HiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\n";
if(ram_size > 0) {
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
}
xml << " </ram>\n";
}
} else if(mapper == ExLoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " </rom>\n";
if(ram_size > 0) {
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " </ram>\n";
}
} else if(mapper == ExHiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-3f:8000-ffff' offset='400000'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff' offset='400000'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff' offset='000000'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff' offset='000000'/>\n";
xml << " </rom>\n";
if(ram_size > 0) {
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
}
xml << " </ram>\n";
}
} else if(mapper == SuperFXROM) {
xml << " <superfx revision='2'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='00-3f:6000-7fff' size='2000'/>\n";
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:6000-7fff' size='2000'/>\n";
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
xml << " </ram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:3000-32ff'/>\n";
xml << " <map address='80-bf:3000-32ff'/>\n";
xml << " </mmio>\n";
xml << " </superfx>\n";
} 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 << " <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='" << strhex(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";
xml << " <map address='80-bf:2200-23ff'/>\n";
xml << " </mmio>\n";
xml << " </sa1>\n";
} else if(mapper == BSCLoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-1f:8000-ffff' offset='000000'/>\n";
xml << " <map mode='linear' address='20-3f:8000-ffff' offset='100000'/>\n";
xml << " <map mode='linear' address='80-9f:8000-ffff' offset='200000'/>\n";
xml << " <map mode='linear' address='a0-bf:8000-ffff' offset='100000'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
xml << " </ram>\n";
xml << " <bsx>\n";
xml << " <slot>\n";
xml << " <map mode='linear' address='c0-ef:0000-ffff'/>\n";
xml << " </slot>\n";
xml << " </bsx>\n";
} else if(mapper == BSCHiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-1f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='80-9f:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << strhex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " <bsx>\n";
xml << " <slot>\n";
xml << " <map mode='shadow' address='20-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='a0-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
xml << " </slot>\n";
xml << " </bsx>\n";
} 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 << " <mmio>\n";
xml << " <map address='00-3f:5000-5fff'/>\n";
xml << " <map address='80-bf:5000-5fff'/>\n";
xml << " </mmio>\n";
xml << " </bsx>\n";
} else if(mapper == STROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-1f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-9f:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <sufamiturbo>\n";
xml << " <slot id='A'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='20-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='a0-bf:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram>\n";
xml << " <map mode='linear' address='60-63:8000-ffff'/>\n";
xml << " <map mode='linear' address='e0-e3:8000-ffff'/>\n";
xml << " </ram>\n";
xml << " </slot>\n";
xml << " <slot id='B'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='40-5f:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram>\n";
xml << " <map mode='linear' address='70-73:8000-ffff'/>\n";
xml << " <map mode='linear' address='f0-f3:8000-ffff'/>\n";
xml << " </ram>\n";
xml << " </slot>\n";
xml << " </sufamiturbo>\n";
}
if(has_srtc) {
xml << " <srtc>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:2800-2801'/>\n";
xml << " <map address='80-bf:2800-2801'/>\n";
xml << " </mmio>\n";
xml << " </srtc>\n";
}
if(has_sdd1) {
xml << " <sdd1>\n";
xml << " <mcu>\n";
xml << " <map address='c0-ff:0000-ffff'/>\n";
xml << " </mcu>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:4800-4807'/>\n";
xml << " <map address='80-bf:4800-4807'/>\n";
xml << " </mmio>\n";
xml << " </sdd1>\n";
}
if(has_cx4) {
xml << " <cx4>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " </cx4>\n";
}
if(has_dsp1) {
xml << " <necdsp program='DSP-1B'>\n";
if(dsp1_mapper == DSP1LoROM1MB) {
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
} else if(dsp1_mapper == DSP1LoROM2MB) {
xml << " <dr>\n";
xml << " <map address='60-6f:0000-3fff'/>\n";
xml << " <map address='e0-ef:0000-3fff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60-6f:4000-7fff'/>\n";
xml << " <map address='e0-ef:4000-7fff'/>\n";
xml << " </sr>\n";
} else if(dsp1_mapper == DSP1HiROM) {
xml << " <dr>\n";
xml << " <map address='00-1f:6000-6fff'/>\n";
xml << " <map address='80-9f:6000-6fff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='00-1f:7000-7fff'/>\n";
xml << " <map address='80-9f:7000-7fff'/>\n";
xml << " </sr>\n";
}
xml << " </necdsp>\n";
}
if(has_dsp2) {
xml << " <necdsp program='DSP-2'>\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 program='DSP-3'>\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 program='DSP-4'>\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";
}
if(has_obc1) {
xml << " <obc1>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </mmio>\n";
xml << " </obc1>\n";
}
if(has_st010) {
xml << " <setadsp program='ST-0010'>\n";
xml << " <mmio>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </mmio>\n";
xml << " </setadsp>\n";
}
if(has_st011) {
//ST-0011 addresses not verified; chip is unsupported
xml << " <setadsp program='ST-0011'>\n";
xml << " <mmio>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </mmio>\n";
xml << " </setadsp>\n";
}
if(has_st018) {
xml << " <setarisc program='ST-0018'>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:3800-38ff'/>\n";
xml << " <map address='80-bf:3800-38ff'/>\n";
xml << " </mmio>\n";
xml << " </setarisc>\n";
}
xml << "</cartridge>\n";
xml_memory_map = xml;
}
void snes_information::read_header(const uint8_t *data, unsigned size) {
type = TypeUnknown;
mapper = LoROM;
dsp1_mapper = DSP1Unmapped;
region = NTSC;
rom_size = size;
ram_size = 0;
has_bsx_slot = false;
has_superfx = false;
has_sa1 = false;
has_srtc = false;
has_sdd1 = false;
has_spc7110 = false;
has_spc7110rtc = false;
has_cx4 = false;
has_dsp1 = false;
has_dsp2 = false;
has_dsp3 = false;
has_dsp4 = false;
has_obc1 = false;
has_st010 = false;
has_st011 = false;
has_st018 = false;
//=====================
//detect Game Boy carts
//=====================
if(size >= 0x0140) {
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
type = TypeGameBoy;
return;
}
}
const unsigned index = find_header(data, size);
const uint8_t mapperid = data[index + Mapper];
const uint8_t rom_type = data[index + RomType];
const uint8_t rom_size = data[index + RomSize];
const uint8_t company = data[index + Company];
const uint8_t regionid = data[index + CartRegion] & 0x7f;
ram_size = 1024 << (data[index + RamSize] & 7);
if(ram_size == 1024) ram_size = 0; //no RAM present
//0, 1, 13 = NTSC; 2 - 12 = PAL
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
//=======================
//detect BS-X flash carts
//=======================
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
if(data[index + 0x14] == 0x00) {
const uint8_t n15 = data[index + 0x15];
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
type = TypeBsx;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return;
}
}
}
}
//=========================
//detect Sufami Turbo carts
//=========================
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
type = TypeSufamiTurboBios;
} else {
type = TypeSufamiTurbo;
}
mapper = STROM;
region = NTSC; //Sufami Turbo only released in Japan
return; //RAM size handled outside this routine
}
//==========================
//detect Super Game Boy BIOS
//==========================
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
type = TypeSuperGameBoy2Bios;
return;
}
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
type = TypeSuperGameBoy1Bios;
return;
}
//=====================
//detect standard carts
//=====================
//detect presence of BS-X flash cartridge connector (reads extended header information)
if(data[index - 14] == 'Z') {
if(data[index - 11] == 'J') {
uint8_t n13 = data[index - 13];
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
has_bsx_slot = true;
}
}
}
}
if(has_bsx_slot) {
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
//BS-X base cart
type = TypeBsxBios;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
} else {
type = TypeBsxSlotted;
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
region = NTSC; //BS-X slotted cartridges only released in Japan
}
} else {
//standard cart
type = TypeNormal;
if(index == 0x7fc0 && size >= 0x401000) {
mapper = ExLoROM;
} else if(index == 0x7fc0 && mapperid == 0x32) {
mapper = ExLoROM;
} else if(index == 0x7fc0) {
mapper = LoROM;
} else if(index == 0xffc0) {
mapper = HiROM;
} else { //index == 0x40ffc0
mapper = ExHiROM;
}
}
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
has_superfx = true;
mapper = SuperFXROM;
ram_size = 1024 << (data[index - 3] & 7);
if(ram_size == 1024) ram_size = 0;
}
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
has_sa1 = true;
mapper = SA1ROM;
}
if(mapperid == 0x35 && rom_type == 0x55) {
has_srtc = true;
}
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
has_sdd1 = true;
}
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
has_spc7110 = true;
has_spc7110rtc = (rom_type == 0xf9);
mapper = SPC7110ROM;
}
if(mapperid == 0x20 && rom_type == 0xf3) {
has_cx4 = true;
}
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
has_dsp1 = true;
}
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
has_dsp1 = true;
}
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
has_dsp1 = true;
}
if(has_dsp1 == true) {
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
dsp1_mapper = DSP1LoROM1MB;
} else if((mapperid & 0x2f) == 0x20) {
dsp1_mapper = DSP1LoROM2MB;
} else if((mapperid & 0x2f) == 0x21) {
dsp1_mapper = DSP1HiROM;
}
}
if(mapperid == 0x20 && rom_type == 0x05) {
has_dsp2 = true;
}
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
has_dsp3 = true;
}
if(mapperid == 0x30 && rom_type == 0x03) {
has_dsp4 = true;
}
if(mapperid == 0x30 && rom_type == 0x25) {
has_obc1 = true;
}
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
has_st010 = true;
}
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
has_st011 = true;
}
if(mapperid == 0x30 && rom_type == 0xf5) {
has_st018 = true;
}
}
unsigned snes_information::find_header(const uint8_t *data, unsigned size) {
unsigned score_lo = score_header(data, size, 0x007fc0);
unsigned score_hi = score_header(data, size, 0x00ffc0);
unsigned score_ex = score_header(data, size, 0x40ffc0);
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
if(score_lo >= score_hi && score_lo >= score_ex) {
return 0x007fc0;
} else if(score_hi >= score_ex) {
return 0x00ffc0;
} else {
return 0x40ffc0;
}
}
unsigned snes_information::score_header(const uint8_t *data, unsigned size, unsigned addr) {
if(size < addr + 64) return 0; //image too small to contain header at this location?
int score = 0;
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
//$00:[000-7fff] contains uninitialized RAM and MMIO.
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
if(resetvector < 0x8000) return 0;
//some images duplicate the header in multiple locations, and others have completely
//invalid header information that cannot be relied upon.
//below code will analyze the first opcode executed at the specified reset vector to
//determine the probability that this is the correct header.
//most likely opcodes
if(resetop == 0x78 //sei
|| resetop == 0x18 //clc (clc; xce)
|| resetop == 0x38 //sec (sec; xce)
|| resetop == 0x9c //stz $nnnn (stz $4200)
|| resetop == 0x4c //jmp $nnnn
|| resetop == 0x5c //jml $nnnnnn
) score += 8;
//plausible opcodes
if(resetop == 0xc2 //rep #$nn
|| resetop == 0xe2 //sep #$nn
|| resetop == 0xad //lda $nnnn
|| resetop == 0xae //ldx $nnnn
|| resetop == 0xac //ldy $nnnn
|| resetop == 0xaf //lda $nnnnnn
|| resetop == 0xa9 //lda #$nn
|| resetop == 0xa2 //ldx #$nn
|| resetop == 0xa0 //ldy #$nn
|| resetop == 0x20 //jsr $nnnn
|| resetop == 0x22 //jsl $nnnnnn
) score += 4;
//implausible opcodes
if(resetop == 0x40 //rti
|| resetop == 0x60 //rts
|| resetop == 0x6b //rtl
|| resetop == 0xcd //cmp $nnnn
|| resetop == 0xec //cpx $nnnn
|| resetop == 0xcc //cpy $nnnn
) score -= 4;
//least likely opcodes
if(resetop == 0x00 //brk #$nn
|| resetop == 0x02 //cop #$nn
|| resetop == 0xdb //stp
|| resetop == 0x42 //wdm
|| resetop == 0xff //sbc $nnnnnn,x
) score -= 8;
//at times, both the header and reset vector's first opcode will match ...
//fallback and rely on info validity in these cases to determine more likely header.
//a valid checksum is the biggest indicator of a valid header.
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
if(data[addr + RomType] < 0x08) score++;
if(data[addr + RomSize] < 0x10) score++;
if(data[addr + RamSize] < 0x08) score++;
if(data[addr + CartRegion] < 14) score++;
if(score < 0) score = 0;
return score;
}
unsigned snes_information::gameboy_ram_size(const uint8_t *data, unsigned size) {
if(size < 512) return 0;
switch(data[0x0149]) {
case 0x00: return 0 * 1024;
case 0x01: return 8 * 1024;
case 0x02: return 8 * 1024;
case 0x03: return 32 * 1024;
case 0x04: return 128 * 1024;
case 0x05: return 128 * 1024;
default: return 128 * 1024;
}
}
bool snes_information::gameboy_has_rtc(const uint8_t *data, unsigned size) {
if(size < 512) return false;
if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true;
return false;
}
}
#endif
-62
View File
@@ -1,62 +0,0 @@
#ifndef NALL_SORT_HPP
#define NALL_SORT_HPP
#include <nall/utility.hpp>
//class: merge sort
//average: O(n log n)
//worst: O(n log n)
//memory: O(n)
//stack: O(log n)
//stable?: yes
//notes:
//there are two primary reasons for choosing merge sort
//over the (usually) faster quick sort*:
//1: it is a stable sort.
//2: it lacks O(n^2) worst-case overhead.
//(* which is also O(n log n) in the average case.)
namespace nall {
template<typename T>
void sort(T list[], unsigned length) {
if(length <= 1) return; //nothing to sort
//use insertion sort to quickly sort smaller blocks
if(length < 64) {
for(unsigned i = 0; i < length; i++) {
unsigned min = i;
for(unsigned j = i + 1; j < length; j++) {
if(list[j] < list[min]) min = j;
}
if(min != i) swap(list[i], list[min]);
}
return;
}
//split list in half and recursively sort both
unsigned middle = length / 2;
sort(list, middle);
sort(list + middle, length - middle);
//left and right are sorted here; perform merge sort
T *buffer = new T[length];
unsigned offset = 0;
unsigned left = 0;
unsigned right = middle;
while(left < middle && right < length) {
if(list[left] < list[right]) {
buffer[offset++] = list[left++];
} else {
buffer[offset++] = list[right++];
}
}
while(left < middle) buffer[offset++] = list[left++];
while(right < length) buffer[offset++] = list[right++];
for(unsigned i = 0; i < length; i++) list[i] = buffer[i];
delete[] buffer;
}
}
#endif
-20
View File
@@ -1,20 +0,0 @@
#ifndef NALL_STATIC_HPP
#define NALL_STATIC_HPP
namespace nall {
template<bool C, typename T, typename F> struct static_if { typedef T type; };
template<typename T, typename F> struct static_if<false, T, F> { typedef F type; };
template<typename C, typename T, typename F> struct mp_static_if { typedef typename static_if<C::type, T, F>::type type; };
template<bool A, bool B> struct static_and { enum { value = false }; };
template<> struct static_and<true, true> { enum { value = true }; };
template<typename A, typename B> struct mp_static_and { enum { value = static_and<A::value, B::value>::value }; };
template<bool A, bool B> struct static_or { enum { value = false }; };
template<> struct static_or<false, true> { enum { value = true }; };
template<> struct static_or<true, false> { enum { value = true }; };
template<> struct static_or<true, true> { enum { value = true }; };
template<typename A, typename B> struct mp_static_or { enum { value = static_or<A::value, B::value>::value }; };
}
#endif
-44
View File
@@ -1,44 +0,0 @@
#ifndef NALL_STDINT_HPP
#define NALL_STDINT_HPP
#include <nall/static.hpp>
#if defined(_MSC_VER)
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef int64_t intmax_t;
#if defined(_WIN64)
typedef int64_t intptr_t;
#else
typedef int32_t intptr_t;
#endif
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef uint64_t uintmax_t;
#if defined(_WIN64)
typedef uint64_t uintptr_t;
#else
typedef uint32_t uintptr_t;
#endif
#else
#include <stdint.h>
#endif
namespace nall {
static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" );
static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size");
static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size");
static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size");
static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" );
static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size");
static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size");
static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size");
}
#endif
-30
View File
@@ -1,30 +0,0 @@
#ifndef NALL_STRING_HPP
#define NALL_STRING_HPP
#include <initializer_list>
#include <nall/utility.hpp>
#include <nall/string/base.hpp>
#include <nall/string/bsv.hpp>
#include <nall/string/core.hpp>
#include <nall/string/cast.hpp>
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/filename.hpp>
#include <nall/string/match.hpp>
#include <nall/string/math.hpp>
#include <nall/string/strl.hpp>
#include <nall/string/strpos.hpp>
#include <nall/string/trim.hpp>
#include <nall/string/replace.hpp>
#include <nall/string/split.hpp>
#include <nall/string/utility.hpp>
#include <nall/string/variadic.hpp>
#include <nall/string/xml.hpp>
namespace nall {
template<> struct has_length<string> { enum { value = true }; };
template<> struct has_size<lstring> { enum { value = true }; };
}
#endif
-141
View File
@@ -1,141 +0,0 @@
#ifndef NALL_STRING_BASE_HPP
#define NALL_STRING_BASE_HPP
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nall/concept.hpp>
#include <nall/stdint.hpp>
#include <nall/utf8.hpp>
#include <nall/vector.hpp>
namespace nall {
class string;
template<typename T> inline string to_string(T);
class string {
public:
inline void reserve(unsigned);
inline unsigned length() const;
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 T> inline string& operator= (T value);
template<typename T> inline string& operator<<(T value);
inline operator const char*() const;
inline char* operator()();
inline char& operator[](int);
inline bool operator==(const char*) const;
inline bool operator!=(const char*) const;
inline bool operator< (const char*) const;
inline bool operator<=(const char*) const;
inline bool operator> (const char*) const;
inline bool operator>=(const char*) const;
inline string& operator=(const string&);
inline string& operator=(string&&);
template<typename... Args> inline string(Args&&... args);
inline string(const string&);
inline string(string&&);
inline ~string();
inline bool readfile(const char*);
inline string& replace (const char*, const char*);
inline string& qreplace(const char*, const char*);
inline string& lower();
inline string& upper();
inline string& transform(const char *before, const char *after);
inline string& ltrim(const char *key = " ");
inline string& rtrim(const char *key = " ");
inline string& trim (const char *key = " ");
inline string& ltrim_once(const char *key = " ");
inline string& rtrim_once(const char *key = " ");
inline string& trim_once (const char *key = " ");
protected:
char *data;
unsigned size;
#if defined(QSTRING_H)
public:
inline operator QString() const;
#endif
};
class lstring : public linear_vector<string> {
public:
template<typename T> inline lstring& operator<<(T value);
inline optional<unsigned> find(const char*);
inline void split (const char*, const char*, unsigned = 0);
inline void qsplit(const char*, const char*, unsigned = 0);
lstring();
lstring(std::initializer_list<string>);
};
//compare.hpp
inline char chrlower(char c);
inline char chrupper(char c);
inline int stricmp(const char *dest, const char *src);
inline bool strbegin (const char *str, const char *key);
inline bool stribegin(const char *str, const char *key);
inline bool strend (const char *str, const char *key);
inline bool striend(const char *str, const char *key);
//convert.hpp
inline char* strlower(char *str);
inline char* strupper(char *str);
inline char* strtr(char *dest, const char *before, const char *after);
inline uintmax_t strhex (const char *str);
inline intmax_t strsigned (const char *str);
inline uintmax_t strunsigned(const char *str);
inline uintmax_t strbin (const char *str);
inline double strdouble (const char *str);
//match.hpp
inline bool match(const char *pattern, const char *str);
//math.hpp
inline bool strint (const char *str, int &result);
inline bool strmath(const char *str, int &result);
//strl.hpp
inline unsigned strlcpy(char *dest, const char *src, unsigned length);
inline unsigned strlcat(char *dest, const char *src, unsigned length);
//trim.hpp
inline char* ltrim(char *str, const char *key = " ");
inline char* rtrim(char *str, const char *key = " ");
inline char* trim (char *str, const char *key = " ");
inline char* ltrim_once(char *str, const char *key = " ");
inline char* rtrim_once(char *str, const char *key = " ");
inline char* trim_once (char *str, const char *key = " ");
//utility.hpp
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
inline unsigned strlcat(string &dest, const char *src, unsigned length);
inline string substr(const char *src, unsigned start = 0, unsigned length = 0);
inline string& strtr(string &dest, const char *before, const char *after);
template<unsigned length = 0, char padding = '0'> inline string strhex(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string strsigned(intmax_t value);
template<unsigned length = 0, char padding = '0'> inline string strunsigned(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string strbin(uintmax_t value);
inline unsigned strdouble(char *str, double value);
inline string strdouble(double value);
//variadic.hpp
template<typename... Args> inline void print(Args&&... args);
};
#endif
-75
View File
@@ -1,75 +0,0 @@
#ifndef NALL_STRING_BSV_HPP
#define NALL_STRING_BSV_HPP
//BSV parser
//version 0.01
namespace nall {
inline string bsv_decode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '}' ) return "";
if(*input == '\r') return "";
if(*input == '\n') return "";
//normal characters
if(*input != '{') { output[offset++] = *input++; continue; }
//entities
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
//illegal entities
return "";
}
output[offset] = 0;
return output;
}
inline string bsv_encode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '\r') return "";
if(*input == '\n') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'f';
output[offset++] = '}';
input++;
continue;
}
if(*input == '{') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
if(*input == '}') {
output[offset++] = '{';
output[offset++] = 'r';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
output[offset++] = *input++;
}
output[offset] = 0;
return output;
}
}
#endif
-32
View File
@@ -1,32 +0,0 @@
#ifndef NALL_STRING_CAST_HPP
#define NALL_STRING_CAST_HPP
namespace nall {
//this is needed, as C++0x does not support explicit template specialization inside classes
template<> inline string to_string<bool> (bool v) { return v ? "true" : "false"; }
template<> inline string to_string<signed int> (signed int v) { return strsigned(v); }
template<> inline string to_string<unsigned int> (unsigned int v) { return strunsigned(v); }
template<> inline string to_string<double> (double v) { return strdouble(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<typename T> string& string::operator= (T value) { return assign(to_string<T>(value)); }
template<typename T> string& string::operator<<(T value) { return append(to_string<T>(value)); }
template<typename T> lstring& lstring::operator<<(T value) {
operator[](size()).assign(to_string<T>(value));
return *this;
}
#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(); }
string::operator QString() const { return QString::fromUtf8(*this); }
#endif
}
#endif
-72
View File
@@ -1,72 +0,0 @@
#ifndef NALL_STRING_COMPARE_HPP
#define NALL_STRING_COMPARE_HPP
namespace nall {
char chrlower(char c) {
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
}
char chrupper(char c) {
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
}
int stricmp(const char *dest, const char *src) {
while(*dest) {
if(chrlower(*dest) != chrlower(*src)) break;
dest++;
src++;
}
return (int)chrlower(*dest) - (int)chrlower(*src);
}
bool strbegin(const char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
return (!memcmp(str, key, ksl));
}
bool stribegin(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
for(int i = 0; i < ksl; i++) {
if(str[i] >= 'A' && str[i] <= 'Z') {
if(str[i] != key[i] && str[i]+0x20 != key[i])return false;
} else if(str[i] >= 'a' && str[i] <= 'z') {
if(str[i] != key[i] && str[i]-0x20 != key[i])return false;
} else {
if(str[i] != key[i])return false;
}
}
return true;
}
bool strend(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
return (!memcmp(str + ssl - ksl, key, ksl));
}
bool striend(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) {
if(str[i] >= 'A' && str[i] <= 'Z') {
if(str[i] != key[z] && str[i]+0x20 != key[z])return false;
} else if(str[i] >= 'a' && str[i] <= 'z') {
if(str[i] != key[z] && str[i]-0x20 != key[z])return false;
} else {
if(str[i] != key[z])return false;
}
}
return true;
}
}
#endif
-157
View File
@@ -1,157 +0,0 @@
#ifndef NALL_STRING_CONVERT_HPP
#define NALL_STRING_CONVERT_HPP
namespace nall {
char* strlower(char *str) {
if(!str) return 0;
int i = 0;
while(str[i]) {
str[i] = chrlower(str[i]);
i++;
}
return str;
}
char* strupper(char *str) {
if(!str) return 0;
int i = 0;
while(str[i]) {
str[i] = chrupper(str[i]);
i++;
}
return str;
}
char* strtr(char *dest, const char *before, const char *after) {
if(!dest || !before || !after) return dest;
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace
for(unsigned i = 0; i < sl; i++) {
for(unsigned l = 0; l < bsl; l++) {
if(dest[i] == before[l]) {
dest[i] = after[l];
break;
}
}
}
return dest;
}
string& string::lower() { nall::strlower(data); return *this; }
string& string::upper() { nall::strupper(data); return *this; }
string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; }
uintmax_t strhex(const char *str) {
if(!str) return 0;
uintmax_t result = 0;
//skip hex identifiers 0x and $, if present
if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2;
else if(*str == '$') str++;
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else if(x >= 'A' && x <= 'F') x -= 'A' - 10;
else if(x >= 'a' && x <= 'f') x -= 'a' - 10;
else break; //stop at first invalid character
result = result * 16 + x;
}
return result;
}
intmax_t strsigned(const char *str) {
if(!str) return 0;
intmax_t result = 0;
bool negate = false;
//check for negation
if(*str == '-') {
negate = true;
str++;
}
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else break; //stop at first invalid character
result = result * 10 + x;
}
return !negate ? result : -result;
}
uintmax_t strunsigned(const char *str) {
if(!str) return 0;
uintmax_t result = 0;
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else break; //stop at first invalid character
result = result * 10 + x;
}
return result;
}
uintmax_t strbin(const char *str) {
if(!str) return 0;
uintmax_t result = 0;
//skip bin identifiers 0b and %, if present
if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2;
else if(*str == '%') str++;
while(*str) {
uint8_t x = *str++;
if(x == '0' || x == '1') x -= '0';
else break; //stop at first invalid character
result = result * 2 + x;
}
return result;
}
double strdouble(const char *str) {
if(!str) return 0.0;
bool negate = false;
//check for negation
if(*str == '-') {
negate = true;
str++;
}
intmax_t result_integral = 0;
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else if(x == '.' || x == ',') break; //break loop and read fractional part
else return (double)result_integral; //invalid value, assume no fractional part
result_integral = result_integral * 10 + x;
}
intmax_t result_fractional = 0;
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else break; //stop at first invalid character
result_fractional = result_fractional * 10 + x;
}
//calculate fractional portion
double result = (double)result_fractional;
while((uintmax_t)result > 0) result /= 10.0;
result += (double)result_integral;
return !negate ? result : -result;
}
}
#endif
-143
View File
@@ -1,143 +0,0 @@
#ifndef NALL_STRING_CORE_HPP
#define NALL_STRING_CORE_HPP
namespace nall {
void string::reserve(unsigned size_) {
if(size_ > size) {
size = size_;
data = (char*)realloc(data, size + 1);
data[size] = 0;
}
}
unsigned string::length() const {
return strlen(data);
}
string& string::assign(const char *s) {
unsigned length = strlen(s);
reserve(length);
strcpy(data, s);
return *this;
}
string& string::append(const char *s) {
unsigned length = strlen(data) + strlen(s);
reserve(length);
strcat(data, s);
return *this;
}
string& string::append(bool value) { append(value ? "true" : "false"); return *this; }
string& string::append(signed int value) { append(strsigned(value)); return *this; }
string& string::append(unsigned int value) { append(strunsigned(value)); return *this; }
string& string::append(double value) { append(strdouble(value)); return *this; }
string::operator const char*() const {
return data;
}
char* string::operator()() {
return data;
}
char& string::operator[](int index) {
reserve(index);
return data[index];
}
bool string::operator==(const char *str) const { return strcmp(data, str) == 0; }
bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; }
bool string::operator< (const char *str) const { return strcmp(data, str) < 0; }
bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; }
bool string::operator> (const char *str) const { return strcmp(data, str) > 0; }
bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; }
string& string::operator=(const string &value) {
assign(value);
return *this;
}
string& string::operator=(string &&source) {
if(data) free(data);
size = source.size;
data = source.data;
source.data = 0;
source.size = 0;
return *this;
}
static void istring(string &output) {
}
template<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);
*data = 0;
istring(*this, std::forward<Args>(args)...);
}
string::string(const string &value) {
size = strlen(value);
data = strdup(value);
}
string::string(string &&source) {
size = source.size;
data = source.data;
source.data = 0;
}
string::~string() {
if(data) free(data);
}
bool string::readfile(const char *filename) {
assign("");
#if !defined(_WIN32)
FILE *fp = fopen(filename, "rb");
#else
FILE *fp = _wfopen(utf16_t(filename), L"rb");
#endif
if(!fp) return false;
fseek(fp, 0, SEEK_END);
unsigned size = ftell(fp);
rewind(fp);
char *fdata = new char[size + 1];
unsigned unused = fread(fdata, 1, size, fp);
fclose(fp);
fdata[size] = 0;
assign(fdata);
delete[] fdata;
return true;
}
optional<unsigned> lstring::find(const char *key) {
for(unsigned i = 0; i < size(); i++) {
if(operator[](i) == key) return { true, i };
}
return { false, 0 };
}
inline lstring::lstring() {
}
inline lstring::lstring(std::initializer_list<string> list) {
for(const string *s = list.begin(); s != list.end(); ++s) {
operator<<(*s);
}
}
}
#endif
-61
View File
@@ -1,61 +0,0 @@
#ifndef NALL_FILENAME_HPP
#define NALL_FILENAME_HPP
namespace nall {
// "foo/bar.c" -> "foo/", "bar.c" -> "./"
inline string dir(char const *name) {
string result = name;
for(signed i = strlen(result); i >= 0; i--) {
if(result[i] == '/' || result[i] == '\\') {
result[i + 1] = 0;
break;
}
if(i == 0) result = "./";
}
return result;
}
// "foo/bar.c" -> "bar.c"
inline string notdir(char const *name) {
for(signed i = strlen(name); i >= 0; i--) {
if(name[i] == '/' || name[i] == '\\') {
name += i + 1;
break;
}
}
string result = name;
return result;
}
// "foo/bar.c" -> "foo/bar"
inline string basename(char const *name) {
string result = name;
for(signed i = strlen(result); i >= 0; i--) {
if(result[i] == '/' || result[i] == '\\') {
//file has no extension
break;
}
if(result[i] == '.') {
result[i] = 0;
break;
}
}
return result;
}
// "foo/bar.c" -> "c"
inline string extension(char const *name) {
for(signed i = strlen(name); i >= 0; i--) {
if(name[i] == '.') {
name += i + 1;
break;
}
}
string result = name;
return result;
}
}
#endif
-76
View File
@@ -1,76 +0,0 @@
#ifndef NALL_STRING_MATCH_HPP
#define NALL_STRING_MATCH_HPP
namespace nall {
bool match(const char *p, const char *s) {
const char *p_ = 0, *s_ = 0;
for(;;) {
if(!*s) {
while(*p == '*') p++;
return !*p;
}
//wildcard match
if(*p == '*') {
p_ = p++, s_ = s;
continue;
}
//any match
if(*p == '?') {
p++, s++;
continue;
}
//ranged match
if(*p == '{') {
#define pattern(name_, rule_) \
if(strbegin(p, name_)) { \
if(rule_) { \
p += sizeof(name_) - 1, s++; \
continue; \
} \
goto failure; \
}
pattern("{alpha}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'))
pattern("{alphanumeric}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || (*s >= '0' && *s <= '9'))
pattern("{binary}", (*s == '0' || *s == '1'))
pattern("{hex}", (*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f'))
pattern("{lowercase}", (*s >= 'a' && *s <= 'z'))
pattern("{numeric}", (*s >= '0' && *s <= '9'))
pattern("{uppercase}", (*s >= 'A' && *s <= 'Z'))
pattern("{whitespace}", (*s == ' ' || *s == '\t'))
#undef pattern
goto failure;
}
//reserved character match
if(*p == '\\') {
p++;
//fallthrough
}
//literal match
if(*p == *s) {
p++, *s++;
continue;
}
//attempt wildcard rematch
failure:
if(p_) {
p = p_, s = s_ + 1;
continue;
}
return false;
}
}
}
#endif
-164
View File
@@ -1,164 +0,0 @@
#ifndef NALL_STRING_MATH_HPP
#define NALL_STRING_MATH_HPP
namespace nall {
static int eval_integer(const char *&s) {
if(!*s) throw "unrecognized_integer";
int value = 0, x = *s, y = *(s + 1);
//hexadecimal
if(x == '0' && (y == 'X' || y == 'x')) {
s += 2;
while(true) {
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
return value;
}
}
//binary
if(x == '0' && (y == 'B' || y == 'b')) {
s += 2;
while(true) {
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
return value;
}
}
//octal (or decimal '0')
if(x == '0') {
s += 1;
while(true) {
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
return value;
}
}
//decimal
if(x >= '0' && x <= '9') {
while(true) {
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
return value;
}
}
//char
if(x == '\'' && y != '\'') {
s += 1;
while(true) {
value = value * 256 + *s++;
if(*s == '\'') { s += 1; return value; }
if(!*s) throw "mismatched_char";
}
}
throw "unrecognized_integer";
}
static int eval(const char *&s, int depth = 0) {
while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) throw "unrecognized_token";
int value = 0, x = *s, y = *(s + 1);
if(*s == '(') {
value = eval(++s, 1);
if(*s++ != ')') throw "mismatched_group";
}
else if(x == '!') value = !eval(++s, 13);
else if(x == '~') value = ~eval(++s, 13);
else if(x == '+') value = +eval(++s, 13);
else if(x == '-') value = -eval(++s, 13);
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
else throw "unrecognized_token";
while(true) {
while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) break;
x = *s, y = *(s + 1);
if(depth >= 13) break;
if(x == '*') { value *= eval(++s, 13); continue; }
if(x == '/') { value /= eval(++s, 13); continue; }
if(x == '%') { value %= eval(++s, 13); continue; }
if(depth >= 12) break;
if(x == '+') { value += eval(++s, 12); continue; }
if(x == '-') { value -= eval(++s, 12); continue; }
if(depth >= 11) break;
if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; }
if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; }
if(depth >= 10) break;
if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; }
if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; }
if(x == '<') { value = value < eval(++s, 10); continue; }
if(x == '>') { value = value > eval(++s, 10); continue; }
if(depth >= 9) break;
if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; }
if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; }
if(depth >= 8) break;
if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; }
if(depth >= 7) break;
if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; }
if(depth >= 6) break;
if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; }
if(depth >= 5) break;
if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; }
if(depth >= 4) break;
if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; }
if(depth >= 3) break;
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
if(x == '?') {
int lhs = eval(++s, 2);
if(*s != ':') throw "mismatched_ternary";
int rhs = eval(++s, 2);
value = value ? lhs : rhs;
continue;
}
if(depth >= 2) break;
if(depth > 0 && x == ')') break;
throw "unrecognized_token";
}
return value;
}
bool strint(const char *s, int &result) {
try {
result = eval_integer(s);
return true;
} catch(const char*) {
result = 0;
return false;
}
}
bool strmath(const char *s, int &result) {
try {
result = eval(s);
return true;
} catch(const char*) {
result = 0;
return false;
}
}
}
#endif
-103
View File
@@ -1,103 +0,0 @@
#ifndef NALL_STRING_REPLACE_HPP
#define NALL_STRING_REPLACE_HPP
namespace nall {
string& string::replace(const char *key, const char *token) {
int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
unsigned int replace_count = 0, size = ssl;
char *buffer;
if(ksl <= ssl) {
if(tsl > ksl) { //the new string may be longer than the old string...
for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need...
if(!memcmp(data + i, key, ksl)) {
replace_count++;
i += ksl;
} else i++;
}
size = ssl + ((tsl - ksl) * replace_count);
reserve(size);
}
buffer = new char[size + 1];
for(i = z = 0; i < ssl;) {
if(i <= ssl - ksl) {
if(!memcmp(data + i, key, ksl)) {
memcpy(buffer + z, token, tsl);
z += tsl;
i += ksl;
} else buffer[z++] = data[i++];
} else buffer[z++] = data[i++];
}
buffer[z] = 0;
assign(buffer);
delete[] buffer;
}
return *this;
}
string& string::qreplace(const char *key, const char *token) {
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
unsigned int replace_count = 0, size = ssl;
uint8_t x;
char *buffer;
if(ksl <= ssl) {
if(tsl > ksl) {
for(i = 0; i <= ssl - ksl;) {
x = data[i];
if(x == '\"' || x == '\'') {
l = i;
i++;
while(data[i++] != x) {
if(i == ssl) {
i = l;
break;
}
}
}
if(!memcmp(data + i, key, ksl)) {
replace_count++;
i += ksl;
} else i++;
}
size = ssl + ((tsl - ksl) * replace_count);
reserve(size);
}
buffer = new char[size + 1];
for(i = z = 0; i < ssl;) {
x = data[i];
if(x == '\"' || x == '\'') {
l = i++;
while(data[i] != x && i < ssl)i++;
if(i >= ssl)i = l;
else {
memcpy(buffer + z, data + l, i - l);
z += i - l;
}
}
if(i <= ssl - ksl) {
if(!memcmp(data + i, key, ksl)) {
memcpy(buffer + z, token, tsl);
z += tsl;
i += ksl;
replace_count++;
} else buffer[z++] = data[i++];
} else buffer[z++] = data[i++];
}
buffer[z] = 0;
assign(buffer);
delete[] buffer;
}
return *this;
}
};
#endif
-56
View File
@@ -1,56 +0,0 @@
#ifndef NALL_STRING_SPLIT_HPP
#define NALL_STRING_SPLIT_HPP
namespace nall {
void lstring::split(const char *key, const char *src, unsigned limit) {
reset();
int ssl = strlen(src), ksl = strlen(key);
int lp = 0, split_count = 0;
for(int i = 0; i <= ssl - ksl;) {
if(!memcmp(src + i, key, ksl)) {
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
i += ksl;
lp = i;
if(!--limit) break;
} else i++;
}
operator[](split_count++) = src + lp;
}
void lstring::qsplit(const char *key, const char *src, unsigned limit) {
reset();
int ssl = strlen(src), ksl = strlen(key);
int lp = 0, split_count = 0;
for(int i = 0; i <= ssl - ksl;) {
uint8_t x = src[i];
if(x == '\"' || x == '\'') {
int z = i++; //skip opening quote
while(i < ssl && src[i] != x) i++;
if(i >= ssl) i = z; //failed match, rewind i
else {
i++; //skip closing quote
continue; //restart in case next char is also a quote
}
}
if(!memcmp(src + i, key, ksl)) {
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
i += ksl;
lp = i;
if(!--limit) break;
} else i++;
}
operator[](split_count++) = src + lp;
}
};
#endif
-52
View File
@@ -1,52 +0,0 @@
#ifndef NALL_STRING_STRL_HPP
#define NALL_STRING_STRL_HPP
namespace nall {
//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller
//return = strlen(src)
unsigned strlcpy(char *dest, const char *src, unsigned length) {
char *d = dest;
const char *s = src;
unsigned n = length;
if(n) {
while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached
}
if(!n) {
if(length) *d = 0;
while(*s++); //traverse rest of s, so that s - src == strlen(src)
}
return (s - src - 1); //return length of copied string, sans null terminator
}
//return = strlen(src) + min(length, strlen(dest))
unsigned strlcat(char *dest, const char *src, unsigned length) {
char *d = dest;
const char *s = src;
unsigned n = length;
while(n-- && *d) d++; //find end of dest
unsigned dlength = d - dest;
n = length - dlength; //subtract length of dest from maximum string length
if(!n) return dlength + strlen(s);
while(*s) {
if(n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = 0;
return dlength + (s - src); //return length of resulting string, sans null terminator
}
}
#endif
-41
View File
@@ -1,41 +0,0 @@
#ifndef NALL_STRING_STRPOS_HPP
#define NALL_STRING_STRPOS_HPP
//usage example:
//if(auto pos = strpos(str, key)) print(pos(), "\n");
//prints position of key within str, only if it is found
namespace nall {
inline optional<unsigned> strpos(const char *str, const char *key) {
unsigned ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return { false, 0 };
for(unsigned i = 0; i <= ssl - ksl; i++) {
if(!memcmp(str + i, key, ksl)) return { true, i };
}
return { false, 0 };
}
inline optional<unsigned> qstrpos(const char *str, const char *key) {
unsigned ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return { false, 0 };
for(unsigned i = 0; i <= ssl - ksl;) {
uint8_t x = str[i];
if(x == '\"' || x == '\'') {
uint8_t z = i++;
while(str[i] != x && i < ssl) i++;
if(i >= ssl) i = z;
}
if(!memcmp(str + i, key, ksl)) return { true, i };
i++;
}
return { false, 0 };
}
}
#endif
-61
View File
@@ -1,61 +0,0 @@
#ifndef NALL_STRING_TRIM_HPP
#define NALL_STRING_TRIM_HPP
namespace nall {
char* ltrim(char *str, const char *key) {
if(!key || !*key) return str;
while(strbegin(str, key)) {
char *dest = str, *src = str + strlen(key);
while(true) {
*dest = *src++;
if(!*dest) break;
dest++;
}
}
return str;
}
char* rtrim(char *str, const char *key) {
if(!key || !*key) return str;
while(strend(str, key)) str[strlen(str) - strlen(key)] = 0;
return str;
}
char* trim(char *str, const char *key) {
return ltrim(rtrim(str, key), key);
}
char* ltrim_once(char *str, const char *key) {
if(!key || !*key) return str;
if(strbegin(str, key)) {
char *dest = str, *src = str + strlen(key);
while(true) {
*dest = *src++;
if(!*dest) break;
dest++;
}
}
return str;
}
char* rtrim_once(char *str, const char *key) {
if(!key || !*key) return str;
if(strend(str, key)) str[strlen(str) - strlen(key)] = 0;
return str;
}
char* trim_once(char *str, const char *key) {
return ltrim_once(rtrim_once(str, key), key);
}
string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; }
string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; }
string& string::trim (const char *key) { nall::trim (data, key); return *this; }
string& string::ltrim_once(const char *key) { nall::ltrim_once(data, key); return *this; }
string& string::rtrim_once(const char *key) { nall::rtrim_once(data, key); return *this; }
string& string::trim_once (const char *key) { nall::trim_once (data, key); return *this; }
}
#endif
-157
View File
@@ -1,157 +0,0 @@
#ifndef NALL_STRING_UTILITY_HPP
#define NALL_STRING_UTILITY_HPP
namespace nall {
unsigned strlcpy(string &dest, const char *src, unsigned length) {
dest.reserve(length);
return strlcpy(dest(), src, length);
}
unsigned strlcat(string &dest, const char *src, unsigned length) {
dest.reserve(length);
return strlcat(dest(), src, length);
}
string substr(const char *src, unsigned start, unsigned length) {
string dest;
if(length == 0) {
//copy entire string
dest = src + start;
} else {
//copy partial string
strlcpy(dest, src + start, length + 1);
}
return dest;
}
/* arithmetic <> string */
template<unsigned length, char padding> string strhex(uintmax_t value) {
string output;
unsigned offset = 0;
//render string backwards, as we do not know its length yet
do {
unsigned n = value & 15;
output[offset++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
//reverse the string in-place
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
template<unsigned length, char padding> string strsigned(intmax_t value) {
string output;
unsigned offset = 0;
bool negative = value < 0;
if(negative) value = abs(value);
do {
unsigned n = value % 10;
output[offset++] = '0' + n;
value /= 10;
} while(value);
while(offset < length) output[offset++] = padding;
if(negative) output[offset++] = '-';
output[offset--] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
template<unsigned length, char padding> string strunsigned(uintmax_t value) {
string output;
unsigned offset = 0;
do {
unsigned n = value % 10;
output[offset++] = '0' + n;
value /= 10;
} while(value);
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
template<unsigned length, char padding> string strbin(uintmax_t value) {
string output;
unsigned offset = 0;
do {
unsigned n = value & 1;
output[offset++] = '0' + n;
value >>= 1;
} while(value);
while(offset < length) output[offset++] = padding;
output[offset--] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
}
return output;
}
//using sprintf is certainly not the most ideal method to convert
//a double to a string ... but attempting to parse a double by
//hand, digit-by-digit, results in subtle rounding errors.
unsigned strdouble(char *str, double value) {
char buffer[256];
sprintf(buffer, "%f", value);
//remove excess 0's in fraction (2.500000 -> 2.5)
for(char *p = buffer; *p; p++) {
if(*p == '.') {
char *p = buffer + strlen(buffer) - 1;
while(*p == '0') {
if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1.
p--;
}
break;
}
}
unsigned length = strlen(buffer);
if(str) strcpy(str, buffer);
return length + 1;
}
string strdouble(double value) {
string temp;
temp.reserve(strdouble(0, value));
strdouble(temp(), value);
return temp;
}
}
#endif
-12
View File
@@ -1,12 +0,0 @@
#ifndef NALL_STRING_VARIADIC_HPP
#define NALL_STRING_VARIADIC_HPP
namespace nall {
template<typename... Args> inline void print(Args&&... args) {
printf("%s", (const char*)string(std::forward<Args>(args)...));
}
}
#endif
-265
View File
@@ -1,265 +0,0 @@
#ifndef NALL_STRING_XML_HPP
#define NALL_STRING_XML_HPP
//XML subset parser
//version 0.05
namespace nall {
struct xml_attribute {
string name;
string content;
virtual string parse() const;
};
struct xml_element : xml_attribute {
string parse() const;
linear_vector<xml_attribute> attribute;
linear_vector<xml_element> element;
protected:
void parse_doctype(const char *&data);
bool parse_head(string data);
bool parse_body(const char *&data);
friend xml_element xml_parse(const char *data);
};
inline string xml_attribute::parse() const {
string data;
unsigned offset = 0;
const char *source = content;
while(*source) {
if(*source == '&') {
if(strbegin(source, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { data[offset++] = '"'; source += 6; continue; }
}
//reject illegal characters
if(*source == '&') return "";
if(*source == '<') return "";
if(*source == '>') return "";
data[offset++] = *source++;
}
data[offset] = 0;
return data;
}
inline string xml_element::parse() const {
string data;
unsigned offset = 0;
const char *source = content;
while(*source) {
if(*source == '&') {
if(strbegin(source, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { data[offset++] = '"'; source += 6; continue; }
}
if(strbegin(source, "<!--")) {
if(auto pos = strpos(source, "-->")) {
source += pos() + 3;
continue;
} else {
return "";
}
}
if(strbegin(source, "<![CDATA[")) {
if(auto pos = strpos(source, "]]>")) {
string cdata = substr(source, 9, pos() - 9);
data << cdata;
offset += strlen(cdata);
source += offset + 3;
continue;
} else {
return "";
}
}
//reject illegal characters
if(*source == '&') return "";
if(*source == '<') return "";
if(*source == '>') return "";
data[offset++] = *source++;
}
data[offset] = 0;
return data;
}
inline void xml_element::parse_doctype(const char *&data) {
name = "!DOCTYPE";
const char *content_begin = data;
signed counter = 0;
while(*data) {
char value = *data++;
if(value == '<') counter++;
if(value == '>') counter--;
if(counter < 0) {
content = substr(content_begin, 0, data - content_begin - 1);
return;
}
}
throw "...";
}
inline bool xml_element::parse_head(string data) {
data.qreplace("\t", " ");
data.qreplace("\r", " ");
data.qreplace("\n", " ");
while(qstrpos(data, " ")) data.qreplace(" ", " ");
data.qreplace(" =", "=");
data.qreplace("= ", "=");
data.rtrim();
lstring part;
part.qsplit(" ", data);
name = part[0];
if(name == "") throw "...";
for(unsigned i = 1; i < part.size(); i++) {
lstring side;
side.qsplit("=", part[i]);
if(side.size() != 2) throw "...";
xml_attribute attr;
attr.name = side[0];
attr.content = side[1];
if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim_once("\"");
else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim_once("'");
else throw "...";
attribute.append(attr);
}
}
inline bool xml_element::parse_body(const char *&data) {
while(true) {
if(!*data) return false;
if(*data++ != '<') continue;
if(*data == '/') return false;
if(strbegin(data, "!DOCTYPE") == true) {
parse_doctype(data);
return true;
}
if(strbegin(data, "!--")) {
if(auto offset = strpos(data, "-->")) {
data += offset() + 3;
continue;
} else {
throw "...";
}
}
if(strbegin(data, "![CDATA[")) {
if(auto offset = strpos(data, "]]>")) {
data += offset() + 3;
continue;
} else {
throw "...";
}
}
auto offset = strpos(data, ">");
if(!offset) throw "...";
string tag = substr(data, 0, offset());
data += offset() + 1;
const char *content_begin = data;
bool self_terminating = false;
if(strend(tag, "?") == true) {
self_terminating = true;
tag.rtrim_once("?");
} else if(strend(tag, "/") == true) {
self_terminating = true;
tag.rtrim_once("/");
}
parse_head(tag);
if(self_terminating) return true;
while(*data) {
unsigned index = element.size();
xml_element node;
if(node.parse_body(data) == false) {
if(*data == '/') {
signed length = data - content_begin - 1;
if(length > 0) content = substr(content_begin, 0, length);
data++;
auto offset = strpos(data, ">");
if(!offset) throw "...";
tag = substr(data, 0, offset());
data += offset() + 1;
tag.replace("\t", " ");
tag.replace("\r", " ");
tag.replace("\n", " ");
while(strpos(tag, " ")) tag.replace(" ", " ");
tag.rtrim();
if(name != tag) throw "...";
return true;
}
} else {
element.append(node);
}
}
}
}
//ensure there is only one root element
inline bool xml_validate(xml_element &document) {
unsigned root_counter = 0;
for(unsigned i = 0; i < document.element.size(); i++) {
string &name = document.element[i].name;
if(strbegin(name, "?")) continue;
if(strbegin(name, "!")) continue;
if(++root_counter > 1) return false;
}
return true;
}
inline xml_element xml_parse(const char *data) {
xml_element self;
try {
while(*data) {
xml_element node;
if(node.parse_body(data) == false) {
break;
} else {
self.element.append(node);
}
}
if(xml_validate(self) == false) throw "...";
return self;
} catch(const char*) {
xml_element empty;
return empty;
}
}
}
#endif
-190
View File
@@ -1,190 +0,0 @@
#ifndef NALL_UPS_HPP
#define NALL_UPS_HPP
#include <stdio.h>
#include <nall/algorithm.hpp>
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/stdint.hpp>
namespace nall {
class ups {
public:
enum result {
ok,
patch_unreadable,
patch_unwritable,
patch_invalid,
input_invalid,
output_invalid,
patch_crc32_invalid,
input_crc32_invalid,
output_crc32_invalid,
};
ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) {
if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable;
crc32 = ~0;
uint32_t x_crc32 = crc32_calculate(x_data, x_size);
uint32_t y_crc32 = crc32_calculate(y_data, y_size);
//header
write('U');
write('P');
write('S');
write('1');
encptr(x_size);
encptr(y_size);
//body
unsigned max_size = max(x_size, y_size);
unsigned relative = 0;
for(unsigned i = 0; i < max_size;) {
uint8_t x = i < x_size ? x_data[i] : 0x00;
uint8_t y = i < y_size ? y_data[i] : 0x00;
if(x == y) {
i++;
continue;
}
encptr(i++ - relative);
write(x ^ y);
while(true) {
if(i >= max_size) {
write(0x00);
break;
}
x = i < x_size ? x_data[i] : 0x00;
y = i < y_size ? y_data[i] : 0x00;
i++;
write(x ^ y);
if(x == y) break;
}
relative = i;
}
//footer
for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3));
for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3));
uint32_t p_crc32 = ~crc32;
for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3));
fp.close();
return ok;
}
ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) {
if(p_size < 18) return patch_invalid;
p_buffer = p_data;
crc32 = ~0;
//header
if(read() != 'U') return patch_invalid;
if(read() != 'P') return patch_invalid;
if(read() != 'S') return patch_invalid;
if(read() != '1') return patch_invalid;
unsigned px_size = decptr();
unsigned py_size = decptr();
//mirror
if(x_size != px_size && x_size != py_size) return input_invalid;
y_size = (x_size == px_size) ? py_size : px_size;
y_data = new uint8_t[y_size]();
for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i];
for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00;
//body
unsigned relative = 0;
while(p_buffer < p_data + p_size - 12) {
relative += decptr();
while(true) {
uint8_t x = read();
if(x && relative < y_size) {
uint8_t y = relative < x_size ? x_data[relative] : 0x00;
y_data[relative] = x ^ y;
}
relative++;
if(!x) break;
}
}
//footer
unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0;
for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3);
for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3);
uint32_t p_crc32 = ~crc32;
for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3);
uint32_t x_crc32 = crc32_calculate(x_data, x_size);
uint32_t y_crc32 = crc32_calculate(y_data, y_size);
if(px_size != py_size) {
if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid;
if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid;
if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid;
if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid;
} else {
if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid;
if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid;
if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid;
if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid;
}
if(p_crc32 != pp_crc32) return patch_crc32_invalid;
return ok;
}
private:
file fp;
uint32_t crc32;
const uint8_t *p_buffer;
uint8_t read() {
uint8_t n = *p_buffer++;
crc32 = crc32_adjust(crc32, n);
return n;
}
void write(uint8_t n) {
fp.write(n);
crc32 = crc32_adjust(crc32, n);
}
void encptr(uint64_t offset) {
while(true) {
uint64_t x = offset & 0x7f;
offset >>= 7;
if(offset == 0) {
write(0x80 | x);
break;
}
write(x);
offset--;
}
}
uint64_t decptr() {
uint64_t offset = 0, shift = 1;
while(true) {
uint8_t x = read();
offset += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
offset += shift;
}
return offset;
}
};
}
#endif
-86
View File
@@ -1,86 +0,0 @@
#ifndef NALL_UTF8_HPP
#define NALL_UTF8_HPP
//UTF-8 <> UTF-16 conversion
//used only for Win32; Linux, etc use UTF-8 internally
#if defined(_WIN32)
#undef UNICODE
#undef _WIN32_WINNT
#undef NOMINMAX
#define UNICODE
#define _WIN32_WINNT 0x0501
#define NOMINMAX
#include <windows.h>
#undef interface
namespace nall {
//UTF-8 to UTF-16
class utf16_t {
public:
operator wchar_t*() {
return buffer;
}
operator const wchar_t*() const {
return buffer;
}
utf16_t(const char *s = "") {
if(!s) s = "";
unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0);
buffer = new wchar_t[length + 1]();
MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length);
}
~utf16_t() {
delete[] buffer;
}
private:
wchar_t *buffer;
};
//UTF-16 to UTF-8
class utf8_t {
public:
operator char*() {
return buffer;
}
operator const char*() const {
return buffer;
}
utf8_t(const wchar_t *s = L"") {
if(!s) s = L"";
unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0);
buffer = new char[length + 1]();
WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0);
}
~utf8_t() {
delete[] buffer;
}
utf8_t(const utf8_t&) = delete;
utf8_t& operator=(const utf8_t&) = delete;
private:
char *buffer;
};
inline void utf8_args(int &argc, char **&argv) {
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
argv = new char*[argc];
for(unsigned i = 0; i < argc; i++) {
argv[i] = new char[_MAX_PATH];
strcpy(argv[i], nall::utf8_t(wargv[i]));
}
}
}
#endif //if defined(_WIN32)
#endif
-39
View File
@@ -1,39 +0,0 @@
#ifndef NALL_UTILITY_HPP
#define NALL_UTILITY_HPP
#include <type_traits>
#include <utility>
namespace nall {
template<bool C, typename T = bool> struct enable_if { typedef T type; };
template<typename T> struct enable_if<false, T> {};
template<typename C, typename T = bool> struct mp_enable_if : enable_if<C::value, T> {};
template<typename T> inline void swap(T &x, T &y) {
T temp(std::move(x));
x = std::move(y);
y = std::move(temp);
}
template<typename T> struct base_from_member {
T value;
base_from_member(T value_) : value(value_) {}
};
template<typename T> class optional {
bool valid;
T value;
public:
inline operator bool() const { return valid; }
inline const T& operator()() const { if(!valid) throw; return value; }
inline optional(bool valid, const T &value) : valid(valid), value(value) {}
};
template<typename T> inline T* allocate(unsigned size, const T &value) {
T *array = new T[size];
for(unsigned i = 0; i < size; i++) array[i] = value;
return array;
}
}
#endif
-92
View File
@@ -1,92 +0,0 @@
#ifndef NALL_VARINT_HPP
#define NALL_VARINT_HPP
#include <type_traits>
#include <nall/bit.hpp>
#include <nall/static.hpp>
namespace nall {
template<unsigned bits> class uint_t {
private:
enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value
typedef typename static_if<
sizeof(int) >= bytes,
unsigned int,
typename static_if<
sizeof(long) >= bytes,
unsigned long,
typename static_if<
sizeof(long long) >= bytes,
unsigned long long,
void
>::type
>::type
>::type T;
static_assert(!std::is_same<T, void>::value, "");
T data;
public:
inline operator T() const { return data; }
inline T operator ++(int) { T r = data; data = uclip<bits>(data + 1); return r; }
inline T operator --(int) { T r = data; data = uclip<bits>(data - 1); return r; }
inline T operator ++() { return data = uclip<bits>(data + 1); }
inline T operator --() { return data = uclip<bits>(data - 1); }
inline T operator =(const T i) { return data = uclip<bits>(i); }
inline T operator |=(const T i) { return data = uclip<bits>(data | i); }
inline T operator ^=(const T i) { return data = uclip<bits>(data ^ i); }
inline T operator &=(const T i) { return data = uclip<bits>(data & i); }
inline T operator<<=(const T i) { return data = uclip<bits>(data << i); }
inline T operator>>=(const T i) { return data = uclip<bits>(data >> i); }
inline T operator +=(const T i) { return data = uclip<bits>(data + i); }
inline T operator -=(const T i) { return data = uclip<bits>(data - i); }
inline T operator *=(const T i) { return data = uclip<bits>(data * i); }
inline T operator /=(const T i) { return data = uclip<bits>(data / i); }
inline T operator %=(const T i) { return data = uclip<bits>(data % i); }
inline uint_t() : data(0) {}
inline uint_t(const T i) : data(uclip<bits>(i)) {}
};
template<unsigned bits> class int_t {
private:
enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value
typedef typename static_if<
sizeof(int) >= bytes,
signed int,
typename static_if<
sizeof(long) >= bytes,
signed long,
typename static_if<
sizeof(long long) >= bytes,
signed long long,
void
>::type
>::type
>::type T;
static_assert(!std::is_same<T, void>::value, "");
T data;
public:
inline operator T() const { return data; }
inline T operator ++(int) { T r = data; data = sclip<bits>(data + 1); return r; }
inline T operator --(int) { T r = data; data = sclip<bits>(data - 1); return r; }
inline T operator ++() { return data = sclip<bits>(data + 1); }
inline T operator --() { return data = sclip<bits>(data - 1); }
inline T operator =(const T i) { return data = sclip<bits>(i); }
inline T operator |=(const T i) { return data = sclip<bits>(data | i); }
inline T operator ^=(const T i) { return data = sclip<bits>(data ^ i); }
inline T operator &=(const T i) { return data = sclip<bits>(data & i); }
inline T operator<<=(const T i) { return data = sclip<bits>(data << i); }
inline T operator>>=(const T i) { return data = sclip<bits>(data >> i); }
inline T operator +=(const T i) { return data = sclip<bits>(data + i); }
inline T operator -=(const T i) { return data = sclip<bits>(data - i); }
inline T operator *=(const T i) { return data = sclip<bits>(data * i); }
inline T operator /=(const T i) { return data = sclip<bits>(data / i); }
inline T operator %=(const T i) { return data = sclip<bits>(data % i); }
inline int_t() : data(0) {}
inline int_t(const T i) : data(sclip<bits>(i)) {}
};
}
#endif
-281
View File
@@ -1,281 +0,0 @@
#ifndef NALL_VECTOR_HPP
#define NALL_VECTOR_HPP
#include <initializer_list>
#include <new>
#include <type_traits>
#include <utility>
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/concept.hpp>
#include <nall/foreach.hpp>
#include <nall/utility.hpp>
namespace nall {
//linear_vector
//memory: O(capacity * 2)
//
//linear_vector uses placement new + manual destructor calls to create a
//contiguous block of memory for all objects. accessing individual elements
//is fast, though resizing the array incurs significant overhead.
//reserve() overhead is reduced from quadratic time to amortized constant time
//by resizing twice as much as requested.
//
//if objects hold memory address references to themselves (introspection), a
//valid copy constructor will be needed to keep pointers valid.
template<typename T> class linear_vector {
protected:
T *pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) {
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
free(pool);
}
pool = 0;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned newsize) {
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
T *poolcopy = (T*)malloc(newsize * sizeof(T));
for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]);
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
free(pool);
pool = poolcopy;
poolsize = newsize;
objectsize = min(objectsize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(newsize);
if(newsize < objectsize) {
//vector is shrinking; destroy excess objects
for(unsigned i = newsize; i < objectsize; i++) pool[i].~T();
} else if(newsize > objectsize) {
//vector is expanding; allocate new objects
for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T;
}
objectsize = newsize;
}
void append(const T data) {
if(objectsize + 1 > poolsize) reserve(objectsize + 1);
new(pool + objectsize++) T(data);
}
template<typename U> void insert(unsigned index, const U list) {
linear_vector<T> merged;
for(unsigned i = 0; i < index; i++) merged.append(pool[i]);
foreach(item, list) merged.append(item);
for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]);
operator=(merged);
}
void insert(unsigned index, const T item) {
insert(index, linear_vector<T>{ item });
}
void remove(unsigned index, unsigned count = 1) {
for(unsigned i = index; count + i < objectsize; i++) {
pool[i] = pool[count + i];
}
if(count + index >= objectsize) resize(index); //every element >= index was removed
else resize(objectsize - count);
}
inline T& operator[](unsigned index) {
if(index >= objectsize) resize(index + 1);
return pool[index];
}
inline const T& operator[](unsigned index) const {
if(index >= objectsize) throw "vector[] out of bounds";
return pool[index];
}
//copy
inline linear_vector<T>& operator=(const linear_vector<T> &source) {
reset();
reserve(source.capacity());
resize(source.size());
for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i);
return *this;
}
linear_vector(const linear_vector<T> &source) : pool(0), poolsize(0), objectsize(0) {
operator=(source);
}
//move
inline linear_vector<T>& operator=(linear_vector<T> &&source) {
reset();
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = 0;
source.reset();
return *this;
}
linear_vector(linear_vector<T> &&source) : pool(0), poolsize(0), objectsize(0) {
operator=(std::move(source));
}
//construction
linear_vector() : pool(0), poolsize(0), objectsize(0) {
}
linear_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
}
~linear_vector() {
reset();
}
};
//pointer_vector
//memory: O(1)
//
//pointer_vector keeps an array of pointers to each vector object. this adds
//significant overhead to individual accesses, but allows for optimal memory
//utilization.
//
//by guaranteeing that the base memory address of each objects never changes,
//this avoids the need for an object to have a valid copy constructor.
template<typename T> class pointer_vector {
protected:
T **pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) {
for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; }
free(pool);
}
pool = 0;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned newsize) {
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
for(unsigned i = newsize; i < objectsize; i++) {
if(pool[i]) { delete pool[i]; pool[i] = 0; }
}
pool = (T**)realloc(pool, newsize * sizeof(T*));
for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0;
poolsize = newsize;
objectsize = min(objectsize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(newsize);
for(unsigned i = newsize; i < objectsize; i++) {
if(pool[i]) { delete pool[i]; pool[i] = 0; }
}
objectsize = newsize;
}
void append(const T data) {
if(objectsize + 1 > poolsize) reserve(objectsize + 1);
pool[objectsize++] = new T(data);
}
template<typename U> void insert(unsigned index, const U list) {
pointer_vector<T> merged;
for(unsigned i = 0; i < index; i++) merged.append(*pool[i]);
foreach(item, list) merged.append(item);
for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]);
operator=(merged);
}
void insert(unsigned index, const T item) {
insert(index, pointer_vector<T>{ item });
}
void remove(unsigned index, unsigned count = 1) {
for(unsigned i = index; count + i < objectsize; i++) {
*pool[i] = *pool[count + i];
}
if(count + index >= objectsize) resize(index); //every element >= index was removed
else resize(objectsize - count);
}
inline T& operator[](unsigned index) {
if(index >= objectsize) resize(index + 1);
if(!pool[index]) pool[index] = new T;
return *pool[index];
}
inline const T& operator[](unsigned index) const {
if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
return *pool[index];
}
//copy
inline pointer_vector<T>& operator=(const pointer_vector<T> &source) {
reset();
reserve(source.capacity());
resize(source.size());
for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i);
return *this;
}
pointer_vector(const pointer_vector<T> &source) : pool(0), poolsize(0), objectsize(0) {
operator=(source);
}
//move
inline pointer_vector<T>& operator=(pointer_vector<T> &&source) {
reset();
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = 0;
source.reset();
return *this;
}
pointer_vector(pointer_vector<T> &&source) : pool(0), poolsize(0), objectsize(0) {
operator=(std::move(source));
}
//construction
pointer_vector() : pool(0), poolsize(0), objectsize(0) {
}
pointer_vector(std::initializer_list<T> list) : pool(0), poolsize(0), objectsize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
}
~pointer_vector() {
reset();
}
};
template<typename T> struct has_size<linear_vector<T>> { enum { value = true }; };
template<typename T> struct has_size<pointer_vector<T>> { enum { value = true }; };
}
#endif
-1
View File
@@ -1 +0,0 @@
*
-1
View File
@@ -1 +0,0 @@
*
-12
View File
@@ -1,12 +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 char *text) {
object->widget = gtk_button_new_with_label(text);
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);
}
-58
View File
@@ -1,58 +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();
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;
}
-22
View File
@@ -1,22 +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 char *text) {
object->widget = gtk_check_button_new_with_label(text);
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;
}
-47
View File
@@ -1,47 +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 char *text) {
object->widget = gtk_combo_box_new_text();
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 char *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;
}
-48
View File
@@ -1,48 +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 char *text) {
object->widget = gtk_scrolled_window_new(0, 0);
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 char *text) {
object->locked = true;
gtk_text_buffer_set_text(object->textBuffer, text, -1);
object->locked = false;
}
-18
View File
@@ -1,18 +0,0 @@
bool Font::create(const char *name, unsigned size, Font::Style style) {
font->font = pango_font_description_new();
pango_font_description_set_family(font->font, name);
pango_font_description_set_size(font->font, size * PANGO_SCALE);
pango_font_description_set_style(font->font, (style & Style::Italic) == Style::Italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL);
pango_font_description_set_weight(font->font, (style & Style::Bold) == Style::Bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
return true;
}
Font::Font() {
font = new Font::Data;
font->font = 0;
}
Font::~Font() {
if(font->font) pango_font_description_free(font->font);
delete font;
}
-191
View File
@@ -1,191 +0,0 @@
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#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 "font.cpp"
#include "menu.cpp"
#include "widget.cpp"
#include "window.cpp"
#include "button.cpp"
#include "canvas.cpp"
#include "checkbox.cpp"
#include "combobox.cpp"
#include "editbox.cpp"
#include "horizontalslider.cpp"
#include "label.cpp"
#include "listbox.cpp"
#include "progressbar.cpp"
#include "radiobox.cpp"
#include "textbox.cpp"
#include "verticalslider.cpp"
#include "viewport.cpp"
#include "messagewindow.cpp"
OS &os = OS::handle();
Window Window::None;
OS& OS::handle() {
static OS os;
return os;
}
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 char *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);
return name;
}
string OS::fileOpen(Window &parent, const char *filter, const char *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 char *filter, const char *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;
}
OS::OS() {
os = new OS::Data;
int argc = 1;
char *argv[2];
argv[0] = new char[8];
argv[1] = 0;
strcpy(argv[0], "phoenix");
char **argvp = argv;
gtk_init(&argc, &argvp);
gtk_rc_parse_string(
"style \"phoenix-gtk\"\n"
"{\n"
" GtkComboBox::appears-as-list = 1\n"
" GtkTreeView::vertical-separator = 0\n"
"}\n"
"class \"GtkComboBox\" style \"phoenix-gtk\"\n"
"class \"GtkTreeView\" style \"phoenix-gtk\"\n"
);
}
}
-250
View File
@@ -1,250 +0,0 @@
namespace phoenix {
struct Window;
struct Object {
Object();
Object& operator=(const Object&) = delete;
Object(const Object&) = delete;
//private:
virtual void unused();
struct Data;
Data *object;
};
struct Font : Object {
enum class Style : unsigned {
None = 0,
Bold = 1,
Italic = 2,
};
bool create(const char *name, unsigned size, Font::Style style = Style::None);
Font();
~Font();
//private:
struct Data;
Data *font;
};
inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); }
inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); }
struct Action : Object {
void setFont(Font &font);
bool visible();
void setVisible(bool visible = true);
bool enabled();
void setEnabled(bool enabled = true);
};
struct Menu : Action {
void create(Window &parent, const char *text);
void create(Menu &parent, const char *text);
};
struct MenuSeparator : Action {
void create(Menu &parent);
};
struct MenuItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const char *text);
};
struct MenuCheckItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const char *text);
bool checked();
void setChecked(bool checked = true);
};
struct MenuRadioItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const char *text);
void create(MenuRadioItem &parent, const char *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();
};
struct Window : Widget {
static Window None;
nall::function<bool ()> onClose;
void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
bool focused();
void setFocused();
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 char *text);
void setStatusText(const char *text);
void setMenuVisible(bool visible = true);
void setStatusVisible(bool visible = true);
Window();
//private:
struct Data;
Data *window;
};
struct Button : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *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 char *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 char *text = "");
void reset();
void addItem(const char *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 char *text = "");
void setFocused();
void setEditable(bool editable = true);
void setWordWrap(bool wordWrap = true);
nall::string text();
void setText(const char *text);
};
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 char *text = "");
void setText(const char *text);
};
struct ListBox : Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
void setFocused();
void setHeaderVisible(bool headerVisible = true);
void setFont(Font &font);
void reset();
void resizeColumnsToContent();
void addItem(const char *text);
void setItem(unsigned row, const char *text);
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 setProgress(unsigned progress);
};
struct RadioBox : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
bool checked();
void setChecked();
private:
RadioBox *first;
};
struct TextBox : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
void setEditable(bool editable = true);
nall::string text();
void setText(const char *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 char *text, Buttons = Buttons::Ok);
static Response question(Window &parent, const char *text, Buttons = Buttons::YesNo);
static Response warning(Window &parent, const char *text, Buttons = Buttons::Ok);
static Response critical(Window &parent, const char *text, Buttons = Buttons::Ok);
};
struct OS : Object {
bool pending();
void run();
void main();
void quit();
unsigned desktopWidth();
unsigned desktopHeight();
nall::string folderSelect(Window &parent, const char *path = "");
nall::string fileOpen(Window &parent, const char *filter, const char *path = "");
nall::string fileSave(Window &parent, const char *filter, const char *path = "");
//private:
static OS& handle();
struct Data;
Data *os;
private:
OS();
};
extern OS &os;
}
-24
View File
@@ -1,24 +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);
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);
}
-12
View File
@@ -1,12 +0,0 @@
void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
object->widget = gtk_label_new(text);
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 char *text) {
gtk_label_set_text(GTK_LABEL(object->widget), text);
}
-154
View File
@@ -1,154 +0,0 @@
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_activate(ListBox *self) {
signed selection = -1;
if(auto position = self->selection()) selection = position();
self->listBox->selection = selection;
if(self->onActivate) self->onActivate();
}
void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
listBox->selection = -1;
object->widget = gtk_scrolled_window_new(0, 0);
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", text);
GType *v = (GType*)malloc(list.size() * sizeof(GType));
for(unsigned i = 0; i < list.size(); i++) v[i] = 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));
//alternate color of each row if there is more than one column
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 2);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false);
for(unsigned i = 0; i < list.size(); i++) {
listBox->column[i].renderer = gtk_cell_renderer_text_new();
listBox->column[i].column = gtk_tree_view_column_new_with_attributes(
list[i], listBox->column[i].renderer, "text", i, (void*)0
);
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);
}
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::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 char *text) {
lstring list;
list.split("\t", text);
GtkTreeIter iter;
gtk_list_store_append(listBox->store, &iter);
for(unsigned i = 0; i < list.size(); i++) {
gtk_list_store_set(listBox->store, &iter, i, (const char*)list[i], -1);
}
}
void ListBox::setItem(unsigned row, const char *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);
for(unsigned i = 0; i < list.size(); i++) {
gtk_list_store_set(listBox->store, &iter, i, (const char*)list[i], -1);
}
}
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;
}
-113
View File
@@ -1,113 +0,0 @@
static void Action_setFont(GtkWidget *widget, gpointer font) {
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, font);
}
}
void Action::setFont(Font &font) {
Action_setFont(object->widget, font.font->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);
}
void Menu::create(Window &parent, const char *text) {
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(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_menu_bar_append(parent.object->menu, object->widget);
gtk_widget_show(object->widget);
}
void Menu::create(Menu &parent, const char *text) {
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);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
void MenuSeparator::create(Menu &parent) {
object->widget = gtk_separator_menu_item_new();
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 char *text) {
object->widget = gtk_menu_item_new_with_label(text);
g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(MenuItem_tick), (gpointer)this);
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 char *text) {
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);
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 char *text) {
first = this;
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);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
void MenuRadioItem::create(MenuRadioItem &parent, const char *text) {
first = parent.first;
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);
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;
}
-65
View File
@@ -1,65 +0,0 @@
static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, gint response) {
if(response == GTK_RESPONSE_OK) return MessageWindow::Response::Ok;
if(response == GTK_RESPONSE_CANCEL) return MessageWindow::Response::Cancel;
if(response == GTK_RESPONSE_YES) return MessageWindow::Response::Yes;
if(response == GTK_RESPONSE_NO) return MessageWindow::Response::No;
if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel;
if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No;
return MessageWindow::Response::Ok;
}
MessageWindow::Response MessageWindow::information(Window &parent, const char *text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::question(Window &parent, const char *text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::warning(Window &parent, const char *text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::critical(Window &parent, const char *text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return MessageWindow_response(buttons, response);
}
-50
View File
@@ -1,50 +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 Window::Data {
Font *defaultFont;
};
struct Canvas::Data {
uint32_t *bufferRGB;
uint32_t *bufferBGR;
unsigned pitch;
};
struct ListBox::Data {
GtkListStore *store;
struct GtkColumn {
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *label;
};
linear_vector<GtkColumn> column;
signed selection;
};
struct OS::Data {
};
void Object::unused() {
}
Object::Object() {
object = new Object::Data;
object->locked = false;
}
-11
View File
@@ -1,11 +0,0 @@
void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
object->widget = gtk_progress_bar_new();
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::setProgress(unsigned progress) {
progress = progress <= 100 ? progress : 0;
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)progress / 100.0);
}
-35
View File
@@ -1,35 +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 char *text) {
first = this;
object->parentWindow = &parent;
object->widget = gtk_radio_button_new_with_label(0, 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(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 char *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;
}
-27
View File
@@ -1,27 +0,0 @@
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 char *text) {
object->widget = gtk_entry_new();
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), "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 char *text) {
object->locked = true;
gtk_entry_set_text(GTK_ENTRY(object->widget), text);
object->locked = false;
}
Loaded 100 of 1412 files, more files were not shown because too many files have changed in this diff. Show more