Compare commits

...

18 Commits
v039 ... v040

Author SHA1 Message Date
byuu
def86470f4 Update to bsnes v040 release.
Too much to really name. The biggest news is that the entire user interface has been re-written from scratch. It is now far more polished and professional. To name one example; the cheat code editor now has checkboxes in the list to quickly toggle codes on an off, there is now a global hotkey to toggle all cheat codes, and each cheat code can contain multiple individual Game Genie or Pro Action Replay codes, allowing easy grouping of multi-part codes.
You'll also notice new artwork: a logo created by Derrick Sobodash (note that the logo contest from below is still active — if someone can design a better logo, it can appear in v041), and a new photo-realistic SNES controller graphic by FirebrandX.
I was finally able to utilize MinGW's profile-guided optimizations, which means this release is approximately ~12% faster than v039.
And emulation itself was even improved(!), such as with Jonas Quinn's fix for a sprite overflow bug.
There were many other changes as well: Linux users will be happy to see RGB overlay support for the X-Video driver, many will benefit from greatly enhanced warning messages and tooltips throughout the GUI, Windows users will now be able to access the menu without freezing emulation, etc etc.
2009-03-09 15:17:32 +00:00
byuu
3e7578761a Update to bsnes v039r22? release.
New WIP.

Softened the panel titles a lot, they take less space but still stand
out well enough.

Should I add a checkbox+global hotkey to toggle the cheat code system
itself on and off? Ala the flip switch that's on the real Game Genie.
Not sure if it's as important anymore now that it's easy to group
multiple cheat codes together and toggle them with just one checkbox.
If so, I need a caption for the checkbox widget, eg "Enable cheat code
system", but something more descriptive.

Rewrote a couple chunks of the nall::string library. I had a lot of
problems with casting due to things like this:
    int strdec(const char*);
    string strdec(int);
    string::operator int();
    string::operator const char*();
    string::operator=(int);
    string::operator=(const char*);
    string::operator<<(int);
    string::operator<<(const char*);
    string::string(int);
    string::string(const char*);


It couldn't implicitly determine if string() << 0 should refer 0 as
const char* or int. So I started by dropping the string->integer
implicit conversions, no need for those, use the strTransform(string)
functions instead. More verbose of the format you want anyway (eg
signed or unsigned integer).

Next, rather than try and implement signed+unsigned+signed
short+unsigned short+signed char etc etc for string::operator= +
string::operator<<, I instead wrote them to use templates. Worked
around the limitation that classes can't use explicit template
specialization by using global thunk functions. operator<<, operator=
and lstring::operator<< all share one set of template specialization
functions to perform conversion of any supported type to a string for
assignment or appending. Pass an unsupported type and it will throw a
"template function undefined" error and fail to compile. No run-time
surprises.

I was careful to implement the copy constructor and copy operator= to
stop the compiler from generating its own functions that copy around
the raw pointer (which would lead to memory leaks + double memory
frees.) So it should be 100% leak proof.

I also split strdec(int) into strsigned(signed) and
strunsigned(unsigned); and updated all the other stuff that used the
lib for that (eg nall::config et al), so you can now perform
unsigned->string conversions on UINT_MAX without getting back -1.

Only thing I'm debating now is if I want to trade C compatibility for
speed and store the string lengths inside the string class for O(1)
length + append functions, compared to their O(n) now. Multiple
chained appends raise that to O(n^2), but with ~20 appends at most per
string, it's hardly a bottleneck right now.

I'm hesitant to do this, because if I do, I'll have to remove char*
operator()() to give a raw handle to the string pointer. I use that
for quick libc const char*->string& wrapper functions, and it's also
nice for other functions to use. And char& operator[](size_t) would
take a hell of a speed hit for having to check for '\0' writes to
adjust the length internally.

_Not_ going to allow storing '\0' directly ala std::string, and make
string::c_str() require memory allocation. Fuck that. Use an
appropriate binary container if you want '\0' inside a block of
memory. The whole idea of nall::string is to maintain 100%
compatibility with C89 strings and their functions.

[No archive available]
2009-03-05 11:48:00 +00:00
byuu
45feae8f75 Update to bsnes v039r21? release.
New WIP.

I added FirebrandX's controller image (let me know if you don't have
the WIP link and want to check it out, FB.)

I also added Derrick's SVG logo for now. The contest thing isn't over
yet, and I like DMM's the best so far (sans the aliasing issues.) But
the auto-resizing to exactly what I want is too nice to pass up. I
think I'm going to require SVG for the final submission at this point.
Side note: Qt supports SVG for auto-scaling, but I use a PNG anyway as
not all Qt libs are going to have SVG support built-in. I still want
SVG for website / print purposes.

For belegdol, I added SDL hat support. It only works with the first
hat, as I figured those four-controller-as-one-device cheapass drivers
might not work well if every single players' hat manipulates the same
controller. You'll have to let me know how it works, since SDL doesn't
detect my joypad's hats.

I'd also like it if someone could test the X-Video RGB support. Anyone
with an RGB overlay surface can do so just by selecting the Xv driver.

[No archive available]
2009-03-01 11:09:00 +00:00
byuu
4d10c36870 Update to bsnes v039r20? release.
New WIP. This one's available here:
http://byuu.cinnamonpirate.com/temp/bsn ... ip.tar.bz2
http://byuu.cinnamonpirate.com/temp/qtdlls.zip

Please don't distribute to other news sites.

You will need to extract the Qt run-time DLLs into the same folder as
the bsnes executable. And you'll likely need WinRAR or 7-zip to
extract the WIP archive.

Please report any issues you can find that weren't present in v039.
I'd like for v040's release to be as bug-free as possible.

Changes from wip19:

The new 'current directory' caching mechanism was caching _after_ save
RAM load, so it wasn't loading save files correctly on first run.
Fixed.

I wasn't setting the internal renderer to match the requested video
mode, so PAL mode wasn't showing the extra scanlines. Fixed.

Had to add a 50ms (very conservative) delay when toggling fullscreen
mode to give Xorg enough time to complete the request. Before it was
trying to query the window size too soon and not fully expanding
fullscreen video to fit the screen. Because of this added delay, I
made it clear the video output when toggling modes. Can't help the
slight line redrawing issue in Qt. Not a bug in my code there.

After reading FitzRoy's comments and thinking more about it myself,
I've decided against the 'intelligent' fullscreen auto-menu hide.
Sorry. It'll still remember whether you were in fullscreen mode on the
last run, but the menubar is always visible by default now. It doesn't
change your menu visibility when you toggle fullscreen anymore.

I also added back the aspect adjust settings to the config file. This
time I combined them to floating point values. So instead of the old:
video.ntsc_aspect_x = 54
video.ntsc_aspect_y = 47
You now have:
video.ntscAspectRatio = 1.14893617

It's an advanced feature not in the GUI, so I expect you to know how
to compensate for the 256x224 vs your native monitor's resolution if
you screw with that setting. Maybe someone can make a web script to
calculate it ala those Xorg modeline generators or something.

Lastly, I removed the group boxes. Took advantage of every row having
three options but one, and added a spacer to get everything aligned.
Advanced panel looks a lot better now.

[No archive available]
2009-02-25 12:32:00 +00:00
byuu
cca8164005 Update to bsnes v039r19? release.
New WIP.

- added hardware settings group to advanced panel. Lets you control
hardware region and base unit.
- added descriptive tooltips to video and audio settings.
- revised documentation to list filetypes, mention BS-X issues and
generalize unsupported special chip notes
- improved handling of paths: core now keeps track of cartridge path
rather than relying on the current working directory; export data path
now works the same as SRAM / cheats / etc when not selected
- fixed XvRGB15/16 blue color channel glitch; testing would be much
appreciated
- I now set the drivers to "None" when they fail to initialize and
give a warning. Before the app would just crash on cart load if this
failed
- added more options to the config file: allow invalid input, analog
axis resistance, and for the first time ever -- CPU, PPU1 and PPU2
version configuration

Really not happy with the overall look and feel of the advanced panel.
I don't think the group boxes are working there. Also, the filetype
descriptions are very terse, but I like them that way. Don't really
care if someone doesn't know what 'non-volatile' means, that's why god
made Google. Complain and I'll make the complex terms hyperlinks to
Wikipedia :P

I'll look into the fullscreen menubar thing again in a few days or
something.

> The Cpu and DMA approach is the same like in bsnes. The exception
> are the stp and wai opcodes.


Heheh, I bet someone looking at STP without being aware of how the
cothreads work would gasp in horror :D

> You are right it's really hard to jump back from doing a nested hdma
> transfer within a dma. But with my approach such an action is not
> needed.


Yeah, I know it's possible with enslavement to only make the simpler
processor a state machine. In our case, the S-SMP. That's how SNEeSe
does it. I just really hate the idea of enslavement.

I can certainly see why others get so upset with me on this, but
having each module cleanly separated is, to me, more important than
savestates. That it's somewhat faster here is just an added bonus. I'm
sure you can appreciate my S-SMP op_*.b files over those state
machines for maintenance, too ;)

As for your work on rewriting all the S-SMP opcodes, I wish you
would've mentioned this to me earlier ... the cycle labels in those .b
files are used to create the exact same switch(cycle) {} code you
wrote automatically, you just have to use a different generator. Given
I dropped that generator back at ~v017, it should be easy to update /
rewrite. The downside is that they don't directly support the bus hold
delays.

Still overall, really really impressive stuff. Kudos on making
something so cool :D

[No archive available]
2009-02-23 13:45:00 +00:00
byuu
0f83e39d5c Update to bsnes v039r18? release.
New WIP.

Added fix for OAM Yflip overflow bug pointed out by Jonas Quinn.

Re-added QGroupBox controls as per discussion with jensbw, the frame
issue should be fixed with Qt 4.5.

Config file now omits " #" marker when there is no item description.

Main window resizes itself a bit better before showing itself on Linux
for the first time. Not a problem at all on Windows.

Using _wgetcwd instead of getcwd for Windows UTF-8 support.

Finished Cartridge class revisions: load_foo returns boolean success,
unload() doesn't need one so that was removed, dropped redundant
bsx_cart_loaded() as you can tell via mode() == ModeBsx. Still need
bsx_flash_loaded() for register mapping purposes.

Fixed hiro port to compile again.

I also rewrote much of the Xv driver. It now properly finds modes via
XvListImageFormats(), and I added support for more modes. It used to
be YUY2 only, now it supports RGB32, RGB24, RGB16, RGB15, YUY2 and
UYVY (chooses the driver mode in that order.)

Unfortunately I was only able to test YUY2 and UYVY with my driver, so
no idea if the RGB modes even work or not. I know RGB16/RGB15 will
have problems, forgot to mask the blue channel before uploading: for
line 344 and 359, (p >> 3) needs to be ((p >> 3) & 0x1f).

To test each mode, the optimal ones would have to be manually disabled
since there's no external way to select the preferred driver. And the
RGB32 copy is sub-optimal, I'll probably allow direct rendering to its
surface in a future revision.

[No archive available]
2009-02-22 11:22:00 +00:00
byuu
ebbcc998d0 Update to bsnes v039r17? release.
New WIP. Posted only for the sake of testing for regressions.

The only real change was adding nall::property as discussed earlier,
and completely revamping the Cartridge class with it. Results:
http://byuu.cinnamonpirate.com/temp/cartridge.hpp

Compared to the old version, it's night and day. All stuff that can be
hidden has been, end-user can't screw with important internal-settings
while emulation is active, as many functions as possible were made
const, ditched char* stuff to replace with string, removed a few
useless structs, simplified the public interface, replaced a memory
duplication in the cart loader that removes the header with a
memmove() instead, blah blah blah.

If I screwed any of this up, it may break the following:
- special chip detection
- RAM / PSRAM / RTC data saving
- UPS patching
- cheat code loading
- relative path stuff
- etc

[No archive available]
2009-02-21 10:39:00 +00:00
byuu
78da6946c6 Update to bsnes v039r16? release.
New WIP. Sorry about the delay in adding your .desktop file, belegdol.
I appreciate you sending it to me.

- worked around a cursor bug in Qt/Xlib: if you started the app and
your mouse cursor was on top of where the window appeared, it gave you
the "resize window" grip cursor, and it would stay like that. The
resize function now ensures you always get the normal cursor.
- worked around a bug in Qt/QGtkStyle: if you pass a default path of
"", it throws all kinds of errors at you on the terminal window, I
implemented a current working directory system for both folder path
selection and file selection (when no default game path is selected.)
It starts in your program startup directory (via getcwd()), and will
update whenever you choose a valid file or folder without canceling
the window.
- icon is now stored in $(prefix)/share/pixmaps instead of
$(prefix)/share/icons
- added belegdol's bsnes.desktop. If I can figure out how to get the
one from Derrick, his has stuff that makes it auto-suggest bsnes for
.SFC files and such. I'll probably add his extensions to it later.
This file installs to $(prefix)/share/applications, and bsnes shows up
under 'games' now.
- updated src/cart a bit, merged some 5x ~800 byte files to a general
cart_loader.cpp file, renamed the functions to be clearer:
cartridge.load_cart_bsc() -> cartridge.load_bsx_slotted();
cartridge.unload_cart_st() -> cartridge.unload_sufami_turbo();

- resized HTML viewer, was too small before, but I think it's too wide
now, meh.
- readme was renamed to documentation. I don't care that it's not
verbose enough to warrant the name right now. I intend to expand upon
it in the future and have it be the general sort of "help"
functionality, hence the name change.
- both the documentation and license are now stored inside src/data as
HTML files. These are embedded with Qt's resource compiler into the
final binary. Easier to edit, and the HTML files can stand on their
own.
- I've partially revamped the documentation. It's somewhat of a
compromise between my ideas and FitzRoy's. I may expand on it a bit,
but I like how it is now, so don't expect many more changes there
please.
- Revamped the license stuff a good deal, removed a lot of cruft.
Grant of Rights section remains the same, so no legal changes.

> bsnes could detect the computer's time zone, and switch to purple if
> necessary.


The US SNES is an eyesore. Both the console and especially the
controller. Fuck it, if they want to see that they can look it up on
Google Images :P

[No archive available]
2009-02-17 12:56:00 +00:00
byuu
4d31452bca Update to bsnes v039r15? release.
Okay, new WIP. To my knowledge, the Qt port now matches or exceeds the
feature-set / quality of the hiro port in every regard, sans things
I've intentionally removed.

- added back all the UI shortcut keys
- started using Qt's resource compiler, rcc, to embed files into the
binary on all platforms; not as efficient as my base56+LZSS method,
but much more standardized and avoids string length limits in Visual
C++
- Linux port now sets the program icon from bsnes.png @ 48x48 (any
larger and the filtering makes it look bad)
- Windows port uses embedded 16x16, 32x32, 48x48 or 256x256 icon as
before
- all windows now rise to the top when they are shown
- replaced about screen -- it's just a placeholder for now so that
it's not modal. Want to put the logo on there, with the rest of the
info and a webpage link below
- removed 'Ok' button from document viewer window
- killed icon48.h and controller.h, ~100kb worth of source. Right now,
hiro port shows black boxes in their place. I'll do something nice
with it later; but I don't want to grow the source that big for the
non-standard target
- added .zip, .gz and .jma to filter, based on compilation flags

Thinking about killing src/data, putting the necessary stuff in each
platform folder. Just a slight issue with windres taking a relative
path to the working directory, so it won't allow easy renaming of the
ui folder names if I do that. Can work around it with 'cd' command in
the Makefile, I suppose.

Would be nice to take advantage of rcc a bit more: it's very easy to
use 16x16 / 32x32 icons inside the UI for eg menu and config panel
list icons. Just going to be tough to make nice icons for them.

Stuff removed from hiro:
- controller graphic:
I love this graphic and want to have it in the official binary, but it
looks really odd when it's only there for one controller type ...
should we keep it anyway? If so, I'll embed it with rcc.
- trace logging hotkeys:
Want to replace these with a real debugger that will have buttons for
them. That will be a long-term goal, of course. May add shortcut keys
for the debugger functions too at that time.
- frameskipping:
Probably the biggest one, I didn't want to re-add this as the new PPU
will make it pointless anyway. If we do add this back for the fast
PPU, I'll probably keep the option hidden from the UI side.

> SFT was the acronym used for the catalog codes. For example, Poi Poi
> Ninja World had SFT-0103 on the cartridge. So there is some historic
> precedent for it at least. BSX, not so sure, but I figured
> everything else was three letters.


Sounds good, but I'd like to check with Nach first. He seems to be on
extended leave at the moment.

> Exhaust Heat 2 still bug out


Wasn't aware ST-0010 had any problems. Not sure if it's bit-perfect or
not anymore, but it definitely has no DSP timing.

[No archive available]
2009-02-15 08:23:00 +00:00
byuu
eb1eca4a6d Update to bsnes v039r14? release.
Okay, another new WIP.

Drag-and-drop is in, and it works in Windows and Linux. Tested with
Thunar under Xfce, but it should be fine with any freedesktop.org-
compatible app/WM.

Worked around the Qt bug ... either
qtreewidget->currentItem()->isSelected() returns the true current item
and the Xlib port has a bug, or it returns the previous and the
Windows port has a bug. I'm using
qtreewidget->selectedItems().count()==1 in its place. Works on Windows
and Linux, so the cheat editor should be fine now.

Forgot to add assign / unassign key disable in the last WIP, so I took
care of that this time.

Added back the readme and license viewers. Used QTextBrowser, which
lets me use HTML formatting plus anchor hyperlinks. Not terribly
useful with such small documents, but meh. We can grow "readme" into
"documentation" in the future. Maybe even add a section listbox on the
left ala CHM files. Throw in a custom CSS stylesheet to make it
prettier. Whatever, not worried about it right now, but we'll get it
ironed out before v040 official.

Got too tired (Red Bull having no effect either), forgot to add the
.zip,.gz,.jma file extensions; and didn't check if cheat codes are
saving on Linux. Also need to work on getting window show commands to
put the windows in the foreground. If they're already visible, they
aren't raising to the top / gaining focus.

Still need to add a bunch of GUI hotkey bindings back, and I think
that'll do it for the rewrite. From there it's all adding stuff the
hiro port lacked.

[No archive available]
2009-02-14 10:05:00 +00:00
byuu
07c9df31a6 Update to bsnes v039r11? release.
Finally, a new WIP.

I redid the spacing / margins on all windows, it should match the old
bsnes/hiro port better now.

Removed all instances of QGroupBox, to work around the problem with
QGtkStyle ignoring the frame entirely, as well as getting around the
ridiculously large margin-top in it that you can't remove. Using
horizontal spacers in its place. Quite a bit more annoying to code
for, but it looks better than even the working frame, at least in my
opinion.

Modified the config panel listbox trigger to use currentRowChanged()
instead of itemSelectionChanged(). This fixes an annoying glitch where
if you clicked down on an item and dragged the mouse, it'd be off-by-
one in the list.

The code editor and cheat code panel both disable buttons when actions
aren't allowed, ala bsnes/hiro. There seems to be a bug in
QTreeWidget::itemSelectionChanged() on Linux, the returned
QTreeWidgetItem::isSelected() value is inverted. Too tired to work
around that tonight.

Improved automatic window resize for the input config and ROM add-on
cart loader windows. They should fully shrink as much as possible now,
rather than leaving blank space.

Dropped the Segoe Print font for titles, as it only looks good on
Vista+.

Removed the sort stuff from the cheat core class and hiro UI, since
the Qt UI can sort by header clicks.

Scale Nx items are checked again according to config file setting.

Stuff left to do:
- work around Qt/Linux bug on edit/delete enable on cheat code panel
- cheat codes don't seem to be saving to disk
- need to re-add screensaver disable code

FitzRoy, it's hard to show you the Qt rendering issues on GNOME, if
you're not familiar with how it should look ...

http://byuu.cinnamonpirate.com/temp/clearlooks.png
http://byuu.cinnamonpirate.com/temp/qgtkstyle.png

Clearlooks is the KDE default style. Looks good, but doesn't match
GTK+ apps.

QGtkStyle is the Qt wrapper that tries to use your GTK+ theme. Biggest
annoyance would be the buttons. There's this red box in the middle
that shows up when a button has focus. With the real GTK+, the entire
button turns red (no border) when you click it, but with just focus
alone it shouldn't have any color. The fonts are also much uglier,
like it has really poor anti-aliasing and slightly wrong sizes.

[No archive available]
2009-02-13 08:20:00 +00:00
byuu
c64232a479 Update to bsnes v039r10? release.
New WIP adds a ton of refinement.

I feel it's exceeded the old UI in quality already, so I added the
platform-functions (realpath, userpath, ...), so now it'll look for
the multi-user config file, falling back on single-user. If you use an
old config, most settings from v039 will be lost, but some will be
pulled in. It now looks for bsnes.cfg and style.qss (for theming.)
Slight issue with relative paths and realpath() on Linux. New
initargs() function adds back support for non-ANSI paths.

Path window shows <startup path (/path/used)> rather than just
<startup path>.

All buttons trigger on release (mouse up / off) rather than press
(mouse down).

Revamped the centering code. All windows respect the reserved screen
areas (taskbar, dock, etc) and center perfectly. They only center on
the first show, after that they will remember where you placed them.

Completely rewrote the windowed / fullscreen handling code. It works
properly even on Linux now. Scale max is great, perfect fit to the
edges of your screen sans reserved areas. If menu+status toggle are
bound to the same key, it'll only refresh the window once to reflect
the new state now.

Going back to the forced size thing. I need to re-add the menu checks.
You can't shrink the window smaller than your current settings, and if
you make it bigger, you get black borders (since I can't disable the
resize reliably on all platforms.) Makes more sense this way anyway,
the menu options should reflect what you see, not what the startup
state is.

It remembers the fullscreen setting automatically now. I took it a bit
further, though. If you have no ROM loaded, it will show the
menu+status in fullscreen to alert you there's no cart and give you a
chance to select one. I also re-added command-line loading, and if you
successfully load a game there, it will turn off the menu+status for
you. There was a slight delay there. You see, loading a game calls
snes.init() which needs the interface (video, etc drivers) setup.
Those drivers rely on the UI being created. So we have to make the UI,
setting the menubar visibility, before we can verify that we're going
to load a game.

_Yes, I can work around this!_ Add a first-run boolean and validate
the command-line path is valid, or separate cart load from SNES init
so I can load, setup GUI then start, etc etc. It's just annoying, not
sure if it's worth the effort to hide the menubar 2ms sooner.

ROM slot loader and cheat path windows now both disable buttons when
no cart is loaded. Major work in progress, lots of stuff left to do
here. When you pick a file with the ROM loader, it doesn't steal focus
to the main window anymore. When you pick a path, it clears the audio
buffer to prevent audio looping. Not sure if I want to hook move /
resize events, since Linux doesn't block as much as Windows. Maybe
I'll #ifdef it.

Qt 4.4 has a bug with GTK+ file open, if you give it a blank path it
spits out lots of errors. It needs a fully-qualified path. Going to
make my old-style "remember last selected path" thing that I used in
hiro/gtk to fix it later.

[No archive available]
2009-02-05 11:23:00 +00:00
byuu
85b08fd24b Update to bsnes v039r09? release.
New WIP re-adds the multi-part ROM loader. For some reason that took
way too long, all I got finished.

A bit different this time, one window for all three modes (bs-x
slotted, bs-x and sufami turbo.) It auto adjusts based on what you
want. Much more compact now that I can put the labels on the same line
as the controls.

It otherwise works the same. In the future, I'll be adding a Date/Time
control when loading pure BS-X carts. Makes no sense adding it to the
UI before the core supports it.

> [X] Pause emulation when main window does not have focus
> [X] Ignore input when main window does not have focus


For the hundredth time, that creates four states instead of three.
What's the difference between pause on + ignore on and pause on +
ignore off?

I'll most likely use a QScrollArea to put a scrollbar on the right if
we end up with too many advanced options for one page.

[No archive available]
2009-02-03 09:05:00 +00:00
byuu
3b3214d1be Update to bsnes v039r08? release.
New WIP.

I guess we've tested the container resize enough, at least for
Windows. Set fullscreen container color to pure black.

To avoid the accidental mouse assignments from v039 and earlier, I
went with UI buttons to assign mouse axes / buttons. Keyboard and
joypads assign the same as before. The extra 1-3 buttons are for six-
button mice like my MX518.

Also re-added mouse capture: load a game, and have a mouse/scope set
as an input device, click in the window and it captures. Press escape
to release. I blocked mouse buttons without capture now, too. That was
allowing a fire shot to go through in previous versions without it
when you first gained focus.

Fixed up hiro/GTK+ to compile again. Should give GNOME/Xfce/Qt4.4
distro packagers some reprieve for a while. Not going to be improving
it anymore, though.

Qt/Linux now uses pkg-config, rather than hard-coded paths. No such
luck for Windows users. There's a Win port of pkg-config, but not many
will have it so the path to Qt will remain hard-coded.

Ditched a few more global nall::string functions (count, find,
(q)replace, (q)split) and moved them inside classes. Fixed the
resultant compile errors in bsnes, hiro and xkas.

The rest of the nall::string functions are also useful on char*
arrays, eg strtr, strlcpy, trim, etc; so it doesn't make a lot of
sense to put them inside the string class.

Not entirely impressed with how the code looks mixing class functions
and global functions, but meh. At least it will reduce mistakes in
trying to pass char*s to string-only functions like replace.

[No archive available]
2009-02-02 08:56:00 +00:00
byuu
b5a38d2a07 Update to bsnes v039r07? release.
New WIP. Adds menubar/statusbar toggle, defaults fullscreen to max
scale with no menu/status (you can change the scale and it will
remember your settings in the future), and I re-added all the audio
panel options.

That leaves a few more GUI shortcut key assignments, mouse support +
binding, BS-X / ST ROM loaders, readme/license windows, and a few new
controls to replace the old Firefox-style advanced screen with
something more user-friendly. After that, the rewrite should be
complete.

Trying to move my string lib to a more OO-approach: removed overloaded
strcpy,strcat in favor of =,<< or .assign,.append. Will be trying to
remove more global functions (replace(foo -> foo.replace(, etc) in the
future. Taking it slow so I don't break xkas too badly.

I also want to shave as much excess functionality as I can from it.
Its main purpose is to be a streamable, implicit-castable alternative
to std::string with a few built-in special functions unique to my
needs (eg qsplit,qreplace.)

[No archive available]
2009-02-01 08:35:00 +00:00
byuu
94004f86ec Update to bsnes v039r06? release.
New WIP, looking for feedback on these changes.

First, I switched from a standard QWidget to a more semantically-
correct QMainWindow. Not much difference, except it adds an automatic
menubar and statusbar, no need to make my own. One advantage was free
status hint support without having to catch the event. So I took that
and redesigned the status system.

First, the game name on the status bar ate up too much space for
nothing. I moved it to the titlebar: bsnes v0.nnn - Game Title (U)

Second, I merged the FPS counter with the system state and put it on
the right-hand side of the status bar. It shows "No cartridge loaded",
"Power off", "Paused" and the framerate. This is persistent and always
visible. FPS doesn't show ideal FPS next to it anymore. That just
wastes space.

Third, the new left-hand stuff. It uses the native QStatusBar support
for timed messages. I use that to pump power state changes ("System
was reset.", "Loaded Star Ocean (J), and applied UPS patch.", etc.)
They go away after ~3 seconds. Unsupported special chip warnings now
pop up a modal dialog box instead of showing in the status bar.

Fourth, we can now set special menu group / item descriptions that
appear when the items are hovered. For instance, mouse over
settings->video mode->ntsc and it explains that the setting affects
the perceived video output size, rather than the core emulator mode.
The descriptions there now suck, but it shows off the concept. We'll
leave them off for all the obvious items.

With all of that, I was able to kill off the "Status" class, ~4kb of
nasty code that polled the time constantly and maintained an internal
string queue for statusbar messages.

Also new to this WIP ... it's apparently not trivial to set a fixed
window size with Qt on Xlib. My MinimizeWindowHint that worked on
Windows was making the window top-most on Xfce, and breaking
fullscreen mode.

So, I tried again to write code that could properly switch between
windowed and fullscreen mode. For some reason, this always causes tons
of problems. Window managers like to take their sweet ass time
updating internal states, so rapid geometry changes often fail,
leaving the window in odd positions and sizes.

It took quite a while, but I have it working, hopefully, 100% on
Windows. I even account for the desk area (ignoring the taskbar and
such) and the window decorations. Centering should be truly perfect,
and scale max should be a pixel-perfect fit to all available screen
size, while maintaining the ratio.

Linux support is still kind of flaky, though. Long shot, but any
knowledgeable help here would be appreciated.

    void Utility::updateFullscreenState() {
      if(config.video.isFullscreen == false) {
        config.video.context = &config.video.windowed;
        winMain->window->showNormal();
        application.processEvents();
      } else {
        config.video.context = &config.video.fullscreen;
        winMain->window->showFullScreen();
        application.processEvents();
      }

      //refresh options that are unique to each video context
      for(unsigned i = 0; i < 2; i++) resizeMainWindow();  //call
    twice as Xlib drops window messages sometimes
      updateHardwareFilter();
      updateSoftwareFilter();
    }

    //if max exceeds x: x is set to max, and y is scaled down to keep
    proportion to x
    void Utility::constrainSize(unsigned &x, unsigned &y, unsigned
    max) {
      if(x > max) {
        double scalar = (double)max / (double)x;
        y = (unsigned)((double)y * (double)scalar);
        x = max;
      }
    }

    //0 = use config file value, 1+ = override with new multiplier
    void Utility::resizeMainWindow(unsigned multiplier /* = 0 */) {
      if(multiplier != 0) config.video.context->multiplier =
    multiplier;
      else multiplier = config.video.context->multiplier;

      unsigned width  = 256 * config.video.context->multiplier;
      unsigned height = (config.video.context->region == 0 ? 224 :
    239) * config.video.context->multiplier;

      if(config.video.context->correctAspectRatio) {
        if(config.video.context->region == 0) {
          width = (double)width * 54.0 / 47.0 + 0.5;  //NTSC adjust
        } else {
          width = (double)width * 32.0 / 23.0 + 0.5;  //PAL adjust
        }
      }

      QDesktopWidget *desktop = QApplication::desktop();

      if(config.video.isFullscreen == false) {
        //get effective desktop work area region (ignore Windows
    taskbar, OS X doc, etc.)
        QRect deskRect = desktop->availableGeometry();
        unsigned deskWidth  = (deskRect.right() - deskRect.left() +
    1);
        unsigned deskHeight = (deskRect.bottom() - deskRect.top() +
    1);

        //place window offscreen so resize events do not cause
    flickering
        winMain->window->move(desktop->width(), desktop->height());
        application.processEvents();

        //shrink window as much as possible to compute frame + menubar
    + statusbar size
        winMain->canvas->setFixedSize(0, 0);
        winMain->canvasContainer->resize(0, 0);
        application.processEvents();
        winMain->window->resize(0, 0);
        application.processEvents();
        QRect frameRect = winMain->window->frameGeometry();

        //constrain window so that it will fit inside desktop work
    area
        constrainSize(height, width, deskHeight - (frameRect.bottom()
    - frameRect.top() + 1));
        constrainSize(width, height, deskWidth  - (frameRect.right() -
    frameRect.left() + 1));

        //resize canvas to desired size
        winMain->canvas->setFixedSize(width, height);
        application.processEvents();

        //shrink window so that it contains all of canvas, but is no
    larger
        winMain->window->resize(width, height);

        //allow canvas to be resized along with window by user
        winMain->canvas->setMinimumSize(256,
    config.video.context->region == 0 ? 224 : 239);
        winMain->canvas->setMaximumSize(desktop->width(),
    desktop->height());
        winMain->canvas->setSizePolicy(QSizePolicy::Expanding,
    QSizePolicy::Expanding);
        application.processEvents();

        //force window size change to take effect
        winMain->window->resize(width, height);
        application.processEvents();

        //center window onscreen:
        //take desktop work area and window frame decorations into
    account
        QRect windowRect = winMain->window->frameGeometry();
        unsigned windowWidth  = (windowRect.right() -
    windowRect.left() + 1);
        unsigned windowHeight = (windowRect.bottom() -
    windowRect.top() + 1);

        winMain->window->move(
          deskRect.left() + (deskWidth  - windowWidth ) / 2,
          deskRect.top () + (deskHeight - windowHeight) / 2
        );
      } else {
        constrainSize(height, width,
    winMain->canvasContainer->size().height());
        constrainSize(width, height,
    winMain->canvasContainer->size().width());
        winMain->canvas->setFixedSize(width, height);
      }
    }


If anyone wanted to get stupid, a style for QWidget.backdrop {
background: url(border.png); } when designed for a specific resolution
+ scaling mode would allow Super Gameboy-style borders :P

Let's see ... properly subclassed the generic input binding pools for
clarity, and added user interface key binding support again. So far
only for exit emu + toggle fullscreen, but the rest should be easy
now.

I can't reduce the space for the QFrameWidgets. Only setMargin works,
but it reduces margins on all sides where only the top is bad. I may
have to revert it back to a section label + horizontal separator
between each area. Probably a good idea, QGtkStyle doesn't support
QFrameWidget's decoration anyway. Looks terrible on GNOME.

Finally, fixed ui_hiro for Windows. Still need to fix up the Linux
target. They share the same Makefile, so additional targets should be
easy, eg a pure SDL port or whatever.

> Darn. Oh well, guess I could keep whatever I concoct to myself.


Or tell me what you want to do, as I probably won't mind :P

[No archive available]
2009-01-27 07:24:00 +00:00
byuu
148bbddb1a Update to bsnes v039r05? release.
Man, I don't have time to read all that ... >_>;

New WIP. Lots of UI refinements.
- re-added power on / power off / reset to main menu (expansion port /
region won't be coming back here)
- re-added status message system
- figured out a way to hide the child indicators in list boxes, as
well as enable sorting while starting with default ordering (so
headers are now clickable to sort, you can even rearrange them)
- merged driver settings and input focus policy into advanced panel
- old advanced panel list is dead, driver panel is dead
- replaced scale 5x with scale max; minor help to 1920x1200+
resolutions
- re-added smart scaling + window size clamping
- Linux port should build out-of-the-box, but there's definitely some
issues in regards to window sizing (even Qt has trouble with this)
- new $(ui)/Makefile system -- as if I weren't abusing GNU make enough
before, new automoc rules are madness -- fear:
    # automatically generate .moc files from .hpp files whenever:
    # - they don't exist
    # - .hpp file was modified after .moc file
    %.moc: $<; $(moc) $(patsubst %.moc,%.hpp,$@) -o $@
    $(foreach object,$(moc_objects),$(eval $(object): $(patsubst
    %.moc,%.hpp,$(object))))
    ui_build: $(moc_objects);
    ui_clean:; -$(foreach object,$(moc_objects),@$(call
    delete,$(object)))

- lots of other crap

http://byuu.cinnamonpirate.com/images/b ... 090126.png

Now to update the locales for v039 finally ...

[No archive available]
2009-01-26 09:59:00 +00:00
byuu
e5b2e87ff8 Update to bsnes v039r04? release.
Well that wore me out ... the UI went from 45kb to 109kb in one night,
with no copy/pasting.

New WIP:
- re-added the InputManager + InputDevicePool classes. The latter is
very complicated, but impressive
- re-added Input Configuration Editor
- re-added Cheat Code Editor
- re-designed individual cheat code editor
- re-added Path Editor
- stopped subclassing QWidget w/Q_OBJECT to work around Qt stylesheet
bug
- re-added controller port selections

Sorting by column header clicking is screwy. It has to be manually
enabled, and the second you do that it re-orders everything. This is
really bad when you want the default order, eg "up, down, left ..." or
your default cheat ordering; so I had to leave it off. Would be too
tacky to add a numeral ID column to work around that.

Seems Qt also has a ridiculously complex tree view (MVC-based), but
thankfully they added a simplified version that works well enough,
QTreeWidget. Only problem is I can't seem to make it hide the child
expander space at the very left-most side. This creates an annoying
little gap. Anyone know how to hide those with Qt?

Even got checkboxes inside the list to toggle cheat codes.
Documentation could've been clearer there.

Speaking of which, I was able to use child nodes on the cheat code
list to show each individual cheat code, but it just didn't look right
to me. There was a ton of blank space on the sides. I can actually
fill in multi-line descriptions as well here, but it still looks
really tacky in my opinion.

Thought about using add code + append code + delete code and putting
the textboxes back, but that just seems tacky and error prone, too.
I'm not adding individual descriptions for each code sub-part.

Only way I can think to make it work that way would be to replace the
multi-code method with a grouping affinity (eg group codes 1+3 into a
set), but then we're getting really complex, with a minimum of 5-6
buttons on the window and 3 text boxes. I think the learning curve
would be too high to be worth it.

So, I used the old method, but instead of a textbox to paste in codes,
I went with a slightly less error prone method of a textbox for the
description and a listbox for each code part. Threw in add / delete /
delete all for the code list. Takes a bit longer if you're trying to
copy/paste codes off the web, but the increased intuitiveness and
consistency is worth it in my opinion.

New cheat code editor (description typo due to extreme fatigue)

There's a lot of rough edges and few safety checks, so if you try to
break things you probably can.

Overall, really having fun with the Qt API. It can be awkward at
times, but it's definitely the most straight-forward API I've seen so
far.

[No archive available]
2009-01-25 08:16:00 +00:00
162 changed files with 6391 additions and 2836 deletions

View File

@@ -1,5 +1,6 @@
include lib/nall/Makefile.string
prefix = /usr/local
ui = ui_qt
################
### compiler ###
@@ -7,23 +8,33 @@ prefix = /usr/local
ifneq ($(findstring gcc,$(compiler)),) # GCC family
flags = -O3 -fomit-frame-pointer -Ilib
c = $(compiler) $(flags)
cpp = $(subst cc,++,$(compiler)) $(flags)
# note: libco *requires* -fomit-frame-pointer on i386 arch
libcoflags := $(flags) -static
c = $(compiler)
cpp = $(subst cc,++,$(compiler))
obj = o
rule = -c $< -o $@
link = -s
mkbin = -o$1
mkdef = -D$1
mkinc = -I$1
mklib = -l$1
# profile-guided optimization:
# flags += -fprofile-generate
# link += -lgcov
# flags += -fprofile-use
else ifeq ($(compiler),cl) # Visual C++
flags = /nologo /wd4355 /wd4805 /wd4996 /Ox /GL /EHsc /Ilib
c = cl $(flags)
cpp = cl $(flags)
libcoflags = $(flags)
c = cl
cpp = cl
obj = obj
rule = /c $< /Fo$@
link = /link
mkbin = /Fe$1
mkdef = /D$1
mkinc = /I$1
mklib = $1.lib
else
unknown_compiler: help;
@@ -35,21 +46,19 @@ endif
ifeq ($(platform),x) # X11
ruby = video.glx video.xv video.sdl audio.alsa audio.openal audio.oss audio.pulseaudio audio.ao input.sdl input.x
link += `pkg-config --libs gtk+-2.0`
link += $(call mklib,Xtst)
delete = rm -f $1
else ifeq ($(platform),win) # Windows
# enable static linking to Qt for Windows build
mingw_link_flags = -mwindows -enable-stdcall-fixup -Wl,-s -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
ruby = video.direct3d video.wgl video.directdraw video.gdi audio.directsound input.directinput
link += $(if $(findstring mingw,$(compiler)),-mwindows)
delete = $(if $(findstring i586-mingw-gcc,$(compiler)),rm -f $1,del $(subst /,\,$1))
link += $(if $(findstring mingw,$(compiler)),$(mingw_link_flags))
link += $(call mklib,uuid)
link += $(call mklib,kernel32)
link += $(call mklib,user32)
link += $(call mklib,gdi32)
link += $(call mklib,shell32)
link += $(call mklib,winmm)
link += $(call mklib,comdlg32)
link += $(call mklib,comctl32)
delete = $(if $(findstring i586-mingw-gcc,$(compiler)),rm -f $1,del $(subst /,\,$1))
else
unknown_platform: help;
endif
@@ -58,8 +67,7 @@ endif
### ruby ###
############
rubyflags =
rubyflags += $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`)
rubyflags = $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`)
link += $(if $(findstring video.direct3d,$(ruby)),$(call mklib,d3d9))
link += $(if $(findstring video.directdraw,$(ruby)),$(call mklib,ddraw))
@@ -74,11 +82,11 @@ link += $(if $(findstring audio.pulseaudio,$(ruby)),$(call mklib,pulse-simple))
link += $(if $(findstring input.directinput,$(ruby)),$(call mklib,dinput8) $(call mklib,dxguid))
link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`)
####################################
### main target and dependencies ###
####################################
####################
### core objects ###
####################
objects = main libco hiro ruby libfilter string \
objects = libco ruby libfilter string \
reader cart cheat \
memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \
bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010
@@ -93,38 +101,27 @@ ifeq ($(enable_jma),true)
flags += $(call mkdef,JMA_SUPPORT)
endif
objects := $(patsubst %,obj/%.$(obj),$(objects))
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c))
# Windows resource file
ifeq ($(platform),win)
objects += obj/resource.$(obj)
endif
################
### implicit ###
################
######################
### implicit rules ###
######################
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(c) $1 $(rule), \
$(c) $(flags) $1 $(rule), \
$(if $(filter %.cpp,$<), \
$(cpp) $1 $(rule) \
$(cpp) $(flags) $1 $(rule) \
) \
) \
)
%.$(obj): $<; $(call compile)
%.$(obj): $<; $(call compile)
all: build;
############
### main ###
############
obj/main.$(obj) : ui/main.cpp ui/* ui/base/* ui/event/* ui/loader/* ui/settings/*
obj/resource.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/resource.$(obj)
include $(ui)/Makefile
objects := $(patsubst %,obj/%.$(obj),$(objects))
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c))
#################
### libraries ###
@@ -132,10 +129,8 @@ obj/resource.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/resource.$(obj)
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* lib/ruby/video/* lib/ruby/audio/* lib/ruby/input/*
$(call compile,$(rubydef) $(rubyflags))
obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/* lib/hiro/gtk/* lib/hiro/win/*
$(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`))
obj/libco.$(obj): lib/libco/libco.c lib/libco/*
$(call compile,$(if $(call strne,$(compiler),cl),-static))
$(c) $(libcoflags) $(rule)
obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/*
obj/string.$(obj): lib/nall/string.cpp lib/nall/*
@@ -239,14 +234,15 @@ obj/winout.$(obj) : reader/jma/winout.cpp reader/jma/*
### targets ###
###############
build: $(objects)
build: ui_build $(objects)
$(strip $(cpp) $(call mkbin,../bsnes) $(objects) $(link))
install:
install -D -m 755 ../bsnes $(DESTDIR)$(prefix)/bin/bsnes
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/icons/bsnes.png
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
clean:
clean: ui_clean
-@$(call delete,obj/*.$(obj))
-@$(call delete,*.res)
-@$(call delete,*.pgd)

View File

@@ -1,4 +1,4 @@
#define BSNES_VERSION "0.039"
#define BSNES_VERSION "0.040"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#define BUSCORE sBus
@@ -27,10 +27,10 @@
#include <nall/detect.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/moduloarray.hpp>
#include <nall/new.hpp>
#include <nall/platform.hpp>
#include <nall/property.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>

BIN
src/bsnes.lnk Normal file

Binary file not shown.

View File

@@ -7,14 +7,9 @@
#include <nall/ups.hpp>
#include "cart.hpp"
#include "cart_load.cpp"
#include "cart_normal.cpp"
#include "cart_bsx.cpp"
#include "cart_bsc.cpp"
#include "cart_st.cpp"
#include "cart_file.cpp"
#include "cart_header.cpp"
#include "cart_loader.cpp"
namespace memory {
MappedRAM cartrom, cartram, cartrtc;
@@ -25,13 +20,7 @@ namespace memory {
Cartridge cartridge;
const char* Cartridge::name() const { return info.filename; }
Cartridge::CartridgeMode Cartridge::mode() const { return info.mode; }
Cartridge::MemoryMapper Cartridge::mapper() const { return info.mapper; }
Cartridge::Region Cartridge::region() const { return info.region; }
bool Cartridge::loaded() const { return cart.loaded; }
void Cartridge::load_begin(CartridgeMode mode) {
void Cartridge::load_begin(Mode cartridge_mode) {
cart.rom = cart.ram = cart.rtc = 0;
bs.ram = 0;
stA.rom = stA.ram = 0;
@@ -42,11 +31,10 @@ void Cartridge::load_begin(CartridgeMode mode) {
stA.rom_size = stA.ram_size = 0;
stB.rom_size = stB.ram_size = 0;
info.mode = mode;
info.patched = false;
info.bsxcart = false;
info.bsxflash = false;
set(loaded, false);
set(bsx_flash_loaded, false);
set(patched, false);
set(mode, cartridge_mode);
}
void Cartridge::load_end() {
@@ -67,25 +55,25 @@ void Cartridge::load_end() {
memory::stBrom.write_protect(true);
memory::stBram.write_protect(false);
if(file::exists(get_cheat_filename(cart.fn, "cht"))) {
string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat);
if(file::exists(cheat_file)) {
cheat.clear();
cheat.load(cheatfn);
cheat.load(cheat_file);
}
cart.loaded = true;
bus.load_cart();
set(loaded, true);
}
bool Cartridge::unload() {
if(cart.loaded == false) return false;
void Cartridge::unload() {
if(loaded() == false) return;
bus.unload_cart();
switch(info.mode) {
case ModeNormal: unload_cart_normal(); break;
case ModeBSX: unload_cart_bsx(); break;
case ModeBSC: unload_cart_bsc(); break;
case ModeSufamiTurbo: unload_cart_st(); break;
switch(mode()) {
case ModeNormal: unload_normal(); break;
case ModeBsxSlotted: unload_bsx_slotted(); break;
case ModeBsx: unload_bsx(); break;
case ModeSufamiTurbo: unload_sufami_turbo(); break;
}
if(cart.rom) { delete[] cart.rom; cart.rom = 0; }
@@ -97,21 +85,44 @@ bool Cartridge::unload() {
if(stB.rom) { delete[] stB.rom; stB.rom = 0; }
if(stB.ram) { delete[] stB.ram; stB.ram = 0; }
if(cheat.count() > 0 || file::exists(get_cheat_filename(cart.fn, "cht"))) {
cheat.save(cheatfn);
string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat);
if(cheat.count() > 0 || file::exists(cheat_file)) {
cheat.save(cheat_file);
cheat.clear();
}
cart.loaded = false;
return true;
set(loaded, false);
}
Cartridge::Cartridge() {
cart.loaded = false;
set(loaded, false);
}
Cartridge::~Cartridge() {
if(cart.loaded == true) unload();
if(loaded() == true) unload();
}
void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) {
set(region, source.region);
set(mapper, source.mapper);
set(dsp1_mapper, source.dsp1_mapper);
set(has_bsx_slot, source.bsx_slot);
set(has_superfx, source.superfx);
set(has_sa1, source.sa1);
set(has_srtc, source.srtc);
set(has_sdd1, source.sdd1);
set(has_spc7110, source.spc7110);
set(has_spc7110rtc, source.spc7110rtc);
set(has_cx4, source.cx4);
set(has_dsp1, source.dsp1);
set(has_dsp2, source.dsp2);
set(has_dsp3, source.dsp3);
set(has_dsp4, source.dsp4);
set(has_obc1, source.obc1);
set(has_st010, source.st010);
set(has_st011, source.st011);
set(has_st018, source.st018);
}
//==========
@@ -127,7 +138,7 @@ void Cartridge::cartinfo_t::reset() {
rom_size = 0;
ram_size = 0;
bsxslot = false;
bsx_slot = false;
superfx = false;
sa1 = false;
srtc = false;
@@ -145,46 +156,24 @@ void Cartridge::cartinfo_t::reset() {
st018 = false;
}
//apply cart-specific settings to current cartridge mode settings
Cartridge::info_t& Cartridge::info_t::operator=(const Cartridge::cartinfo_t &source) {
mapper = source.mapper;
dsp1_mapper = source.dsp1_mapper;
region = source.region;
bsxslot = source.bsxslot;
superfx = source.superfx;
sa1 = source.sa1;
srtc = source.srtc;
sdd1 = source.sdd1;
spc7110 = source.spc7110;
spc7110rtc = source.spc7110rtc;
cx4 = source.cx4;
dsp1 = source.dsp1;
dsp2 = source.dsp2;
dsp3 = source.dsp3;
dsp4 = source.dsp4;
obc1 = source.obc1;
st010 = source.st010;
st011 = source.st011;
st018 = source.st018;
return *this;
Cartridge::cartinfo_t::cartinfo_t() {
reset();
}
//=======
//utility
//=======
//ensure file path is absolute (eg resolve relative paths)
string Cartridge::filepath(const char *filename, const char *pathname) {
//if no pathname, return filename as-is
string file(filename);
replace(file, "\\", "/");
if(!pathname || !*pathname) return file;
file.replace("\\", "/");
string path = (!pathname || !*pathname) ? (const char*)snes.config.path.current : pathname;
//ensure path ends with trailing '/'
string path(pathname);
replace(path, "\\", "/");
if(!strend(path, "/")) strcat(path, "/");
path.replace("\\", "/");
if(!strend(path, "/")) path.append("/");
//replace relative path with absolute path
if(strbegin(path, "./")) {
@@ -194,6 +183,52 @@ string Cartridge::filepath(const char *filename, const char *pathname) {
//remove folder part of filename
lstring part;
split(part, "/", file);
return path << part[count(part) - 1];
part.split("/", file);
return path << part[part.size() - 1];
}
//remove directory information and file extension ("/foo/bar.ext" -> "bar")
string Cartridge::basename(const char *filename) {
string name(filename);
//remove extension
for(signed i = strlen(name) - 1; i >= 0; i--) {
if(name[i] == '.') {
name[i] = 0;
break;
}
}
//remove directory information
for(signed i = strlen(name) - 1; i >= 0; i--) {
if(name[i] == '/' || name[i] == '\\') {
i++;
char *output = name();
while(true) {
*output++ = name[i];
if(!name[i]) break;
i++;
}
break;
}
}
return name;
}
//remove filename and return path only ("/foo/bar.ext" -> "/foo/bar/")
string Cartridge::basepath(const char *filename) {
string path(filename);
path.replace("\\", "/");
//remove filename
for(signed i = strlen(path) - 1; i >= 0; i--) {
if(path[i] == '/') {
path[i] = 0;
break;
}
}
if(!strend(path, "/")) path.append("/");
return path;
}

View File

@@ -1,36 +1,22 @@
class Cartridge {
class Cartridge : public property {
public:
enum CartridgeMode {
enum Mode {
ModeNormal,
ModeBSC,
ModeBSX,
ModeBsxSlotted,
ModeBsx,
ModeSufamiTurbo,
};
enum CartridgeType {
enum Type {
TypeNormal,
TypeBSC,
TypeBSXBIOS,
TypeBSX,
TypeSufamiTurboBIOS,
TypeBsxSlotted,
TypeBsxBios,
TypeBsx,
TypeSufamiTurboBios,
TypeSufamiTurbo,
TypeUnknown,
};
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 Region {
NTSC,
PAL,
@@ -55,144 +41,131 @@ public:
DSP1HiROM,
};
const char* name() const;
CartridgeMode mode() const;
MemoryMapper mapper() const;
Region region() const;
//properties can be read via operator(), eg "if(cartridge.loaded() == true)";
//warning: if loaded() == false, no other property is considered valid!
struct {
bool loaded;
char fn[PATH_MAX];
uint8 *rom, *ram, *rtc;
unsigned rom_size, ram_size, rtc_size;
} cart;
property_t<bool> loaded; //is a base cartridge inserted?
property_t<bool> bsx_flash_loaded; //is a BS-X flash cart connected?
property_t<bool> patched; //has a UPS patch been applied?
property_t<string> name; //display name (filename sans path and extension)
struct {
char fn[PATH_MAX];
uint8 *ram;
unsigned ram_size;
} bs;
property_t<Mode> mode;
property_t<Region> region;
property_t<MemoryMapper> mapper;
property_t<DSP1MemoryMapper> dsp1_mapper;
struct {
char fn[PATH_MAX];
uint8 *rom, *ram;
unsigned rom_size, ram_size;
} stA, stB;
property_t<bool> has_bsx_slot;
property_t<bool> has_superfx;
property_t<bool> has_sa1;
property_t<bool> has_srtc;
property_t<bool> has_sdd1;
property_t<bool> has_spc7110, has_spc7110rtc;
property_t<bool> has_cx4;
property_t<bool> has_dsp1, has_dsp2, has_dsp3, has_dsp4;
property_t<bool> has_obc1;
property_t<bool> has_st010, has_st011, has_st018;
//main interface
bool load_normal (const char *base);
bool load_bsx_slotted (const char *base, const char *slot = "");
bool load_bsx (const char *base, const char *slot = "");
bool load_sufami_turbo(const char *base, const char *slotA = "", const char *slotB = "");
void unload();
//utility functions
static string filepath(const char *filename, const char *pathname); //"./bar.ext" -> "/foo/bar.ext"
static string basename(const char *filename); //"/foo/bar.ext" -> "bar"
static string basepath(const char *filename); //"/foo/bar.ext" -> "/foo/bar/"
//this function will load 'filename', decompress it if needed, and determine what type of
//image file 'filename' refers to (eg normal cart, BS-X flash cart, Sufami Turbo cart, etc.)
//warning: this operation is very expensive, use sparingly!
Type detect_image_type(const char *filename) const;
Cartridge();
~Cartridge();
private:
void load_begin(Mode);
void load_end();
void unload_normal();
void unload_bsx_slotted();
void unload_bsx();
void unload_sufami_turbo();
struct cartinfo_t {
CartridgeType type;
Type type;
Region region;
MemoryMapper mapper;
DSP1MemoryMapper dsp1_mapper;
Region region;
unsigned rom_size, ram_size;
unsigned rom_size;
unsigned ram_size;
bool bsxslot;
bool bsx_slot;
bool superfx;
bool sa1;
bool srtc;
bool sdd1;
bool spc7110;
bool spc7110rtc;
bool spc7110, spc7110rtc;
bool cx4;
bool dsp1;
bool dsp2;
bool dsp3;
bool dsp4;
bool dsp1, dsp2, dsp3, dsp4;
bool obc1;
bool st010;
bool st011;
bool st018;
bool st010, st011, st018;
void reset();
cartinfo_t();
};
struct info_t {
char filename[PATH_MAX * 4];
bool patched;
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,
};
CartridgeMode mode;
MemoryMapper mapper;
DSP1MemoryMapper dsp1_mapper;
Region region;
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const;
unsigned find_header(const uint8_t *data, unsigned size) const;
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const;
void set_cartinfo(const cartinfo_t&);
bool bsxcart; //is BS-X cart inserted?
bool bsxflash; //is BS-X flash cart inserted into BS-X cart?
bool bsxslot;
bool superfx;
bool sa1;
bool srtc;
bool sdd1;
bool spc7110;
bool spc7110rtc;
bool cx4;
bool dsp1;
bool dsp2;
bool dsp3;
bool dsp4;
bool obc1;
bool st010;
bool st011;
bool st018;
info_t& operator=(const cartinfo_t&);
} info;
struct {
char fn[PATH_MAX];
uint8_t *data;
unsigned size;
} image;
bool load_image(const char*);
bool inspect_image(cartinfo_t &cartinfo, const char *filename);
bool load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init);
void load_cart_normal(const char*);
void load_cart_bsc(const char*, const char*);
void load_cart_bsx(const char*, const char*);
void load_cart_st(const char*, const char*, const char*);
void unload_cart_normal();
void unload_cart_bsx();
void unload_cart_bsc();
void unload_cart_st();
bool loaded() const;
void load_begin(CartridgeMode);
void load_end();
bool unload();
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size);
unsigned find_header(const uint8_t *data, unsigned size);
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
bool load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const;
bool load_ram (const char *filename, uint8_t *&data, unsigned size, uint8_t init_value) const;
enum CompressionMode {
CompressionNone, //always load without compression
CompressionInspect, //use file header inspection
CompressionAuto, //use file extension or file header inspection (configured by user)
};
bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone);
bool save_file(const char *fn, uint8 *data, unsigned size);
bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size);
char* modify_extension(char *filename, const char *extension);
char* get_base_filename(char *filename);
char* get_path_filename(char *filename, const char *path, const char *source, const char *extension);
char* get_patch_filename(const char *source, const char *extension);
char* get_save_filename(const char *source, const char *extension);
char* get_cheat_filename(const char *source, const char *extension);
static string filepath(const char *filename, const char *pathname);
bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone) const;
bool save_file(const char *fn, uint8 *data, unsigned size) const;
bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size) const;
Cartridge();
~Cartridge();
string modify_extension(const char *filename, const char *extension) const;
string get_filename(const char *source, const char *extension, const char *path) const;
private:
char patchfn[PATH_MAX];
char savefn[PATH_MAX];
char rtcfn[PATH_MAX];
char cheatfn[PATH_MAX];
struct {
string filename;
uint8_t *rom, *ram, *rtc;
unsigned rom_size, ram_size, rtc_size;
} cart;
struct {
string filename;
uint8_t *ram;
unsigned ram_size;
} bs;
struct {
string filename;
uint8_t *rom, *ram;
unsigned rom_size, ram_size;
} stA, stB;
};
namespace memory {

View File

@@ -1,44 +0,0 @@
#ifdef CART_CPP
void Cartridge::load_cart_bsc(const char *base, const char *slot) {
uint8_t *data;
unsigned size;
strcpy(cart.fn, base);
strcpy(bs.fn, slot);
load_begin(ModeBSC);
if(load_image(base) == false) return;
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
info = cartinfo;
if(load_image(slot) == true) {
info.bsxflash = true;
bs.ram = image.data;
bs.ram_size = image.size;
}
if(cartinfo.ram_size > 0) {
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
load_end();
//set base filename
strcpy(info.filename, base);
get_base_filename(info.filename);
if(*slot) {
char filenameBS[PATH_MAX];
strcpy(filenameBS, slot);
get_base_filename(filenameBS);
strcat(info.filename, " + ");
strcat(info.filename, filenameBS);
}
}
void Cartridge::unload_cart_bsc() {
if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size);
}
#endif

View File

@@ -1,49 +0,0 @@
#ifdef CART_CPP
void Cartridge::load_cart_bsx(const char *base, const char *slot) {
uint8_t *data;
unsigned size;
strcpy(cart.fn, base);
strcpy(bs.fn, slot);
load_begin(ModeBSX);
if(load_image(base) == false) return;
info.bsxcart = true;
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
info = cartinfo;
cart.ram = 0;
cart.ram_size = 0;
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
if(load_file(get_save_filename(cart.fn, "srm"), data, size, CompressionNone) == true) {
memcpy(bsxcart.sram.handle (), data, min(bsxcart.sram.size (), size));
delete[] data;
}
if(load_file(get_save_filename(cart.fn, "psr"), data, size, CompressionNone) == true) {
memcpy(bsxcart.psram.handle(), data, min(bsxcart.psram.size(), size));
delete[] data;
}
if(load_image(slot)) {
info.bsxflash = true;
bs.ram = image.data;
bs.ram_size = image.size;
}
load_end();
strcpy(info.filename, !*slot ? base : slot);
get_base_filename(info.filename);
}
void Cartridge::unload_cart_bsx() {
save_file(get_save_filename(cart.fn, "srm"), bsxcart.sram.handle (), bsxcart.sram.size ());
save_file(get_save_filename(cart.fn, "psr"), bsxcart.psram.handle(), bsxcart.psram.size());
}
#endif

View File

@@ -11,66 +11,23 @@
#include "../reader/jmareader.hpp"
#endif
char* Cartridge::modify_extension(char *filename, const char *extension) {
string Cartridge::modify_extension(const char *filename_, const char *extension) const {
string filename = filename_;
int i;
for(i = strlen(filename); i >= 0; i--) {
if(filename[i] == '.') break;
if(filename[i] == '/') break;
if(filename[i] == '.') break;
if(filename[i] == '/') break;
if(filename[i] == '\\') break;
}
if(i > 0 && filename[i] == '.') filename[i] = 0;
strcat(filename, ".");
strcat(filename, extension);
return filename;
return filename << "." << extension;
}
//remove directory information and file extension ("/foo/bar.ext" -> "bar")
char* Cartridge::get_base_filename(char *filename) {
//remove extension
for(int i = strlen(filename) - 1; i >= 0; i--) {
if(filename[i] == '.') {
filename[i] = 0;
break;
}
}
//remove directory information
for(int i = strlen(filename) - 1; i >= 0; i--) {
if(filename[i] == '/' || filename[i] == '\\') {
i++;
char *output = filename;
while(true) {
*output++ = filename[i];
if(!filename[i]) break;
i++;
}
break;
}
}
return filename;
string Cartridge::get_filename(const char *source, const char *extension, const char *path) const {
return filepath(modify_extension(source, extension), path);
}
char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) {
strcpy(filename, source);
modify_extension(filename, extension);
strcpy(filename, filepath(filename, path));
return filename;
}
char* Cartridge::get_patch_filename(const char *source, const char *extension) {
return get_path_filename(patchfn, snes.config.path.patch, source, extension);
}
char* Cartridge::get_save_filename(const char *source, const char *extension) {
return get_path_filename(savefn, snes.config.path.save, source, extension);
}
char* Cartridge::get_cheat_filename(const char *source, const char *extension) {
return get_path_filename(cheatfn, snes.config.path.cheat, source, extension);
}
bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) {
bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) const {
if(file::exists(fn) == false) return false;
Reader::Type filetype = Reader::Normal;
@@ -117,7 +74,7 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, Compress
return true;
}
bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) {
bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) const {
uint8_t *outdata = 0;
unsigned outsize;
ups patcher;
@@ -126,7 +83,7 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t
bool apply = false;
if(result == ups::ok) apply = true;
if(snes.config.file.bypass_patch_crc32 == true) {
if(result == ups::input_crc32_invalid) apply = true;
if(result == ups::input_crc32_invalid) apply = true;
if(result == ups::output_crc32_invalid) apply = true;
}
@@ -141,7 +98,7 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t
return apply;
}
bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) {
bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) const {
file fp;
if(!fp.open(fn, file::mode_write)) return false;
fp.write(data, size);

View File

@@ -1,6 +1,6 @@
#ifdef CART_CPP
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) {
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const {
info.reset();
unsigned index = find_header(data, size);
@@ -13,7 +13,7 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
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) {
info.type = TypeBSX;
info.type = TypeBsx;
info.mapper = BSXROM;
info.region = NTSC; //BS-X only released in Japan
return;
@@ -28,7 +28,7 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
info.type = TypeSufamiTurboBIOS;
info.type = TypeSufamiTurboBios;
} else {
info.type = TypeSufamiTurbo;
}
@@ -53,22 +53,21 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
uint8 n13 = data[index - 13];
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
info.bsxslot = true;
info.bsx_slot = true;
}
}
}
}
if(info.bsxslot == true) {
if(info.bsx_slot == true) {
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
//BS-X base cart
info.type = TypeBSXBIOS;
info.type = TypeBsxBios;
info.mapper = BSXROM;
info.region = NTSC; //BS-X only released in Japan
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
} else {
//BS-X slotted cart
info.type = TypeBSC;
info.type = TypeBsxSlotted;
info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
}
} else {
@@ -174,7 +173,7 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
}
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) {
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) const {
unsigned score_lo = score_header(data, size, 0x007fc0);
unsigned score_hi = score_header(data, size, 0x00ffc0);
unsigned score_ex = score_header(data, size, 0x40ffc0);
@@ -189,7 +188,7 @@ unsigned Cartridge::find_header(const uint8_t *data, unsigned size) {
}
}
unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) const {
if(size < addr + 64) return 0; //image too small to contain header at this location?
int score = 0;

View File

@@ -1,50 +0,0 @@
#ifdef CART_CPP
bool Cartridge::load_image(const char *filename) {
if(!filename || !*filename) return false;
uint8_t *data;
unsigned size;
if(!load_file(filename, data, size, CompressionAuto)) return false;
if((size & 0x7fff) != 512) {
image.data = data;
image.size = size;
} else {
//remove 512-byte header
image.data = new uint8_t[image.size = size - 512];
memcpy(image.data, data + 512, image.size);
}
if(load_file(get_patch_filename(filename, "ups"), data, size, CompressionInspect) == true) {
apply_patch(data, size, image.data, image.size);
delete[] data;
info.patched = true;
}
return true;
}
bool Cartridge::inspect_image(cartinfo_t &cartinfo, const char *filename) {
cartinfo.reset();
if(!load_image(filename)) return false;
read_header(cartinfo, image.data, image.size);
delete[] image.data;
return true;
}
bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) {
data = new uint8_t[size];
memset(data, init, size);
uint8_t *savedata;
unsigned savesize;
if(load_file(filename, savedata, savesize, CompressionNone) == false) return false;
memcpy(data, savedata, min(size, savesize));
delete[] savedata;
return true;
}
#endif

244
src/cart/cart_loader.cpp Normal file
View File

@@ -0,0 +1,244 @@
#ifdef CART_CPP
//================
//Normal cartridge
//================
bool Cartridge::load_normal(const char *base) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
load_begin(ModeNormal);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
if(cartinfo.ram_size > 0) {
load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
if(cartinfo.srtc || cartinfo.spc7110rtc) {
load_ram(get_filename(base, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size = 20, 0x00);
}
load_end();
set(name, basename(base));
return true;
}
void Cartridge::unload_normal() {
if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size);
if(cart.rtc) save_file(get_filename(cart.filename, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size);
}
//======================
//BS-X slotted cartridge
//======================
bool Cartridge::load_bsx_slotted(const char *base, const char *slot) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
bs.filename = slot;
load_begin(ModeBsxSlotted);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
if(load_image(slot, data, size, patch_applied) == true) {
set(bsx_flash_loaded, true);
if(patch_applied) set(patched, true);
bs.ram = data;
bs.ram_size = size;
}
if(cartinfo.ram_size > 0) {
load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
load_end();
string filename = basename(base);
if(*slot) filename << " + " << basename(slot);
set(name, filename);
return true;
}
void Cartridge::unload_bsx_slotted() {
if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size);
}
//====================
//BS-X flash cartridge
//====================
bool Cartridge::load_bsx(const char *base, const char *slot) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
bs.filename = slot;
load_begin(ModeBsx);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
cart.ram = 0;
cart.ram_size = 0;
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
if(load_file(get_filename(base, "srm", snes.config.path.save), data, size, CompressionNone) == true) {
memcpy(bsxcart.sram.handle (), data, min(bsxcart.sram.size (), size));
delete[] data;
}
if(load_file(get_filename(base, "psr", snes.config.path.save), data, size, CompressionNone) == true) {
memcpy(bsxcart.psram.handle(), data, min(bsxcart.psram.size(), size));
delete[] data;
}
if(load_image(slot, data, size, patch_applied)) {
set(bsx_flash_loaded, true);
if(patch_applied) set(patched, true);
bs.ram = data;
bs.ram_size = size;
}
load_end();
set(name, !*slot ? basename(base) : basename(slot));
return true;
}
void Cartridge::unload_bsx() {
save_file(get_filename(cart.filename, "srm", snes.config.path.save), bsxcart.sram.handle (), bsxcart.sram.size ());
save_file(get_filename(cart.filename, "psr", snes.config.path.save), bsxcart.psram.handle(), bsxcart.psram.size());
}
//============================
//Sufami Turbo flash cartridge
//============================
bool Cartridge::load_sufami_turbo(const char *base, const char *slotA, const char *slotB) {
uint8_t *data;
unsigned size;
bool patch_applied;
cart.filename = base;
stA.filename = slotA;
stB.filename = slotB;
load_begin(ModeSufamiTurbo);
if(load_image(base, data, size, patch_applied) == false) return false;
snes.config.path.current = basepath(cart.filename);
if(patch_applied) set(patched, true);
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = data, cart.rom_size = size);
set_cartinfo(cartinfo);
if(load_image(slotA, data, size, patch_applied)) {
if(patch_applied) set(patched, true);
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
memcpy(stA.rom, data, min(size, stA.rom_size));
delete[] data;
load_ram(get_filename(slotA, "srm", snes.config.path.save), stA.ram, stA.ram_size = 0x020000, 0xff);
}
if(load_image(slotB, data, size, patch_applied)) {
if(patch_applied) set(patched, true);
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
memcpy(stB.rom, data, min(size, stB.rom_size));
delete[] data;
load_ram(get_filename(slotB, "srm", snes.config.path.save), stB.ram, stB.ram_size = 0x020000, 0xff);
}
load_end();
string filename;
if(!*slotA && !*slotB) filename << basename(base);
else if( *slotA && !*slotB) filename << basename(slotA);
else if(!*slotA && *slotB) filename << basename(slotB);
else filename << basename(slotA) << " + " << basename(slotB);
set(name, filename);
return true;
}
void Cartridge::unload_sufami_turbo() {
if(stA.ram) save_file(get_filename(stA.filename, "srm", snes.config.path.save), stA.ram, stA.ram_size);
if(stB.ram) save_file(get_filename(stB.filename, "srm", snes.config.path.save), stB.ram, stB.ram_size);
}
//=================
//utility functions
//=================
Cartridge::Type Cartridge::detect_image_type(const char *filename) const {
uint8_t *data;
unsigned size;
bool patch_applied;
if(!load_image(filename, data, size, patch_applied)) return TypeUnknown;
cartinfo_t info;
read_header(info, data, size);
delete[] data;
return info.type;
}
bool Cartridge::load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const {
if(!filename || !*filename) return false;
if(!load_file(filename, data, size, CompressionAuto)) return false;
if((size & 0x7fff) == 512) {
//remove 512-byte header
memmove(data, data + 512, size -= 512);
}
uint8_t *pdata;
unsigned psize;
if(load_file(get_filename(filename, "ups", snes.config.path.patch), pdata, psize, CompressionInspect) == true) {
apply_patch(pdata, psize, data, size);
delete[] pdata;
patched = true;
} else {
patched = false;
}
return true;
}
bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) const {
data = new uint8_t[size];
memset(data, init, size);
uint8_t *savedata;
unsigned savesize;
if(load_file(filename, savedata, savesize, CompressionNone) == false) return false;
memcpy(data, savedata, min(size, savesize));
delete[] savedata;
return true;
}
#endif

View File

@@ -1,35 +0,0 @@
#ifdef CART_CPP
void Cartridge::load_cart_normal(const char *base) {
uint8_t *data;
unsigned size;
strcpy(cart.fn, base);
load_begin(ModeNormal);
if(load_image(base) == false) return;
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
info = cartinfo;
if(cartinfo.ram_size > 0) {
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
if(cartinfo.srtc || cartinfo.spc7110rtc) {
load_ram(get_save_filename(base, "rtc"), cart.rtc, cart.rtc_size = 20, 0x00);
}
load_end();
//set base filename
strcpy(info.filename, base);
get_base_filename(info.filename);
}
void Cartridge::unload_cart_normal() {
if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size);
if(cart.rtc) save_file(get_save_filename(cart.fn, "rtc"), cart.rtc, cart.rtc_size);
}
#endif

View File

@@ -1,62 +0,0 @@
#ifdef CART_CPP
void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) {
uint8_t *data;
unsigned size;
strcpy(cart.fn, base);
strcpy(stA.fn, slotA);
strcpy(stB.fn, slotB);
load_begin(ModeSufamiTurbo);
if(load_image(base) == false) return;
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
info = cartinfo;
if(load_image(slotA)) {
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
memcpy(stA.rom, image.data, min(image.size, stA.rom_size));
delete[] image.data;
load_ram(get_save_filename(slotA, "srm"), stA.ram, stA.ram_size = 0x020000, 0xff);
}
if(load_image(slotB)) {
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
memcpy(stB.rom, image.data, min(image.size, stB.rom_size));
delete[] image.data;
load_ram(get_save_filename(slotB, "srm"), stB.ram, stB.ram_size = 0x020000, 0xff);
}
load_end();
//set base filename
if(!*slotA && !*slotB) {
strcpy(info.filename, cart.fn);
get_base_filename(info.filename);
} else if(*slotA && !*slotB) {
strcpy(info.filename, slotA);
get_base_filename(info.filename);
} else if(!*slotA && *slotB) {
strcpy(info.filename, slotB);
get_base_filename(info.filename);
} else {
char filenameA[PATH_MAX], filenameB[PATH_MAX];
strcpy(filenameA, slotA);
get_base_filename(filenameA);
strcpy(filenameB, slotB);
get_base_filename(filenameB);
strcpy(info.filename, filenameA);
strcat(info.filename, " + ");
strcat(info.filename, filenameB);
}
}
void Cartridge::unload_cart_st() {
if(stA.ram) save_file(get_save_filename(stA.fn, "srm"), stA.ram, stA.ram_size);
if(stB.ram) save_file(get_save_filename(stB.fn, "srm"), stB.ram, stB.ram_size);
}
#endif

View File

@@ -1,5 +1,4 @@
#include <../base.hpp>
#include <nall/sort.hpp>
Cheat cheat;
@@ -31,7 +30,7 @@ bool Cheat::decode(const char *s, Cheat::cheat_t &item) const {
item.count = 0;
lstring list;
split(list, "+", s);
list.split("+", s);
for(unsigned n = 0; n < list.size(); n++) {
unsigned addr;
@@ -67,6 +66,28 @@ bool Cheat::read(unsigned addr, uint8_t &data) const {
return false;
}
//==============
//master control
//==============
//global cheat system enable/disable:
//if disabled, *all* cheat codes are disabled;
//otherwise only individually disabled codes are.
bool Cheat::enabled() const {
return cheat_system_enabled;
}
void Cheat::enable() {
cheat_system_enabled = true;
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
}
void Cheat::disable() {
cheat_system_enabled = false;
cheat_enabled = false;
}
//================================
//cheat list manipulation routines
//================================
@@ -159,15 +180,15 @@ void Cheat::disable(unsigned i) {
bool Cheat::load(const char *fn) {
string data;
if(!fread(data, fn)) return false;
replace(data, "\r\n", "\n");
qreplace(data, " ", "");
if(!data.readfile(fn)) return false;
data.replace("\r\n", "\n");
data.qreplace(" ", "");
lstring line;
split(line, "\n", data);
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
lstring part;
qsplit(part, ",", line[i]);
part.qsplit(",", line[i]);
if(part.size() != 3) continue;
trim(part[0], "\"");
add(part[1] == "enabled", /* code = */ part[2], /* desc = */ part[0]);
@@ -189,22 +210,13 @@ bool Cheat::save(const char *fn) const {
return true;
}
void Cheat::sort() {
if(code.size() <= 1) return; //nothing to sort?
cheat_t *buffer = new cheat_t[code.size()];
for(unsigned i = 0; i < code.size(); i++) buffer[i] = code[i];
nall::sort(buffer, code.size());
for(unsigned i = 0; i < code.size(); i++) code[i] = buffer[i];
delete[] buffer;
}
void Cheat::clear() {
cheat_system_enabled = false;
cheat_enabled_code_exists = false;
memset(mask, 0, 0x200000);
code.reset();
}
Cheat::Cheat() {
Cheat::Cheat() : cheat_system_enabled(true) {
clear();
}
@@ -294,17 +306,18 @@ bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const {
}
}
//update_cheat_status() will scan to see if any codes are
//enabled. if any are, make sure the cheat system is on.
//otherwise, turn cheat system off to speed up emulation.
//speed up S-CPU memory reads by disabling cheat code lookup when either:
//a) cheat system is disabled by user, or b) no enabled cheat codes exist
void Cheat::update_cheat_status() {
for(unsigned i = 0; i < code.size(); i++) {
if(code[i].enabled) {
cheat_system_enabled = true;
cheat_enabled_code_exists = true;
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
return;
}
}
cheat_system_enabled = false;
cheat_enabled_code_exists = false;
cheat_enabled = false;
}
//address lookup table manipulation and mirroring
@@ -367,13 +380,13 @@ void Cheat::clear(unsigned addr) {
//these two functions are used to safely store description text inside .cfg file format.
string& Cheat::encode_description(string &desc) const {
replace(desc, "\"", "\\q");
replace(desc, "\n", "\\n");
desc.replace("\"", "\\q");
desc.replace("\n", "\\n");
return desc;
}
string& Cheat::decode_description(string &desc) const {
replace(desc, "\\q", "\"");
replace(desc, "\\n", "\n");
desc.replace("\\q", "\"");
desc.replace("\\n", "\n");
return desc;
}

View File

@@ -21,8 +21,12 @@ public:
bool decode(const char *s, cheat_t &item) const;
bool read(unsigned addr, uint8_t &data) const;
inline bool enabled() const { return cheat_system_enabled; }
bool enabled() const;
void enable();
void disable();
inline unsigned count() const { return code.size(); }
inline bool active() const { return cheat_enabled; }
inline bool exists(unsigned addr) const { return mask[addr >> 3] & 1 << (addr & 7); }
bool add(bool enable, const char *code, const char *desc);
@@ -36,14 +40,15 @@ public:
bool load(const char *fn);
bool save(const char *fn) const;
void sort();
void clear();
Cheat();
private:
bool cheat_enabled; //cheat_enabled == (cheat_enabled_code_exists && cheat_system_enabled);
bool cheat_enabled_code_exists;
bool cheat_system_enabled;
uint8_t mask[0x200000];
vector<cheat_t> code;

View File

@@ -28,7 +28,7 @@ void DSP1::reset() {
* of expected ranges
*****/
bool DSP1::addr_decode(uint16 addr) {
switch(cartridge.info.dsp1_mapper) {
switch(cartridge.dsp1_mapper()) {
case Cartridge::DSP1LoROM1MB: {
//$[20-3f]:[8000-bfff] = DR, $[20-3f]:[c000-ffff] = SR
return (addr >= 0xc000);

View File

@@ -10,7 +10,7 @@ const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 3
void SPC7110::init() {}
void SPC7110::enable() {
uint16_t limit = (cartridge.info.spc7110rtc ? 0x4842 : 0x483f);
uint16_t limit = (cartridge.has_spc7110rtc() ? 0x4842 : 0x483f);
for(uint16_t i = 0x4800; i <= limit; i++) memory::mmio.map(i, *this);
}
@@ -74,7 +74,7 @@ void SPC7110::reset() {
r4841 = 0x00;
r4842 = 0x00;
if(cartridge.info.spc7110rtc) {
if(cartridge.has_spc7110rtc()) {
rtc_state = RTCS_Inactive;
rtc_mode = RTCM_Linear;
rtc_index = 0;
@@ -99,7 +99,7 @@ void SPC7110::update_time(int offset) {
| (memory::cartrtc.read(17) << 8)
| (memory::cartrtc.read(18) << 16)
| (memory::cartrtc.read(19) << 24);
time_t current_time = time(0);
time_t current_time = time(0) - offset;
//sizeof(time_t) is platform-dependent; though memory::cartrtc needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by

View File

@@ -3,8 +3,14 @@
#include "dcpu.cpp"
void CPU::power() {
cpu_version = snes.config.cpu.version;
}
void CPU::reset() {
}
CPU::CPU() {
cpu_version = 2;
}
CPU::~CPU() {

View File

@@ -17,8 +17,8 @@ public:
regs_t regs;
virtual void scanline() = 0;
virtual void power() = 0;
virtual void reset() = 0;
virtual void power();
virtual void reset();
/*****
* in opcode-based CPU emulators, the main emulation routine

View File

@@ -11,6 +11,8 @@ priority_queue<unsigned> event(512, bind(&sCPU::queue_event, &cpu));
#include "timing/timing.cpp"
void sCPU::power() {
CPU::power();
regs.a = regs.x = regs.y = 0x0000;
regs.s = 0x01ff;
@@ -22,6 +24,8 @@ void sCPU::power() {
}
void sCPU::reset() {
CPU::reset();
regs.pc.d = 0x000000;
regs.pc.l = bus.read(0xfffc);
regs.pc.h = bus.read(0xfffd);

8
src/data/bsnes.desktop Normal file
View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Name=bsnes
Comment=SNES emulator
Exec=bsnes
Icon=bsnes
Terminal=false
Type=Application
Categories=Game;Emulator;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>bsnes&trade; Usage Documentation</h1><br>
bsnes is a Super Nintendo / Super Famicom emulator that strives to provide
the most faithful emulation experience possible. It focuses on accuracy and
clean code; over speed and features.
<hr>
<h2><u>Modes of Operation</u></h2><br>
bsnes is capable of running both in its default multi-user mode, as well as
in single-user mode.<br>
<br>
In multi-user mode, configuration data is stored inside the user's home
directory. On Windows, this is located at "%APPDATA%/.bsnes". On other operating
systems, this is located at "~/.bsnes".<br>
<br>
To enable single-user mode, create a blank "bsnes.cfg" file inside the same
folder as the bsnes executable. bsnes will then use this file to store
configuration data.
<hr>
<h2><u>Supported Filetypes</u></h2><br>
<b>SFC, SMC, SWC, FIG:</b> SNES cartridge &mdash; ROM image.<br>
<b>BS:</b> Satellaview BS-X flash cartridge &mdash; EEPROM image.<br>
<b>ST:</b> Sufami Turbo cartridge &mdash; ROM image.<br>
<b>SRM, PSR:</b> non-volatile memory, often used to save game data &mdash; (P)SRAM image.<br>
<b>RTC:</b> real-time clock non-volatile memory.<br>
<b>UPS:</b> patch data, used to dynamically modify cartridge of same base filename upon load.<br>
<b>CHT:</b> plain-text list of "Game Genie" / "Pro Action Replay" codes.
<hr>
<h2><u>Known Limitations</u></h2><br>
<b>Cartridge co-processors:</b> certain cartridges contain special co-processor chips to enhance
their functionality. Some of these are either partially or completely unsupported. A message box
warning will pop up when attempting to load such a cartridge.<br>
<br>
<b>Satellaview BS-X emulation:</b> this hardware is only partially supported. As a result,
most BS-X software will not function correctly.<br>
<br>
<b>Savestates:</b> due to the design of bsnes, it is not plausible to
implement support for savestate and/or rewind functionality.<br>
<br>
<b>Netplay:</b> internet multiplay is not currently supported nor planned.
<hr>
<h2><u>Contributors</u></h2>
&bull; Andreas Naive<br>
&bull; anomie<br>
&bull; Derrick Sobodash<br>
&bull; DMV27<br>
&bull; FirebrandX<br>
&bull; FitzRoy<br>
&bull; GIGO<br>
&bull; Jonas Quinn<br>
&bull; kode54<br>
&bull; krom<br>
&bull; Matthew Callis<br>
&bull; Nach<br>
&bull; neviksti<br>
&bull; Overload<br>
&bull; RedDwarf<br>
&bull; Richard Bannister<br>
&bull; Shay Green<br>
&bull; tetsuo55<br>
&bull; TRAC<br>
&bull; zones<br>
</body>
</html>

View File

@@ -1,41 +0,0 @@
static char enc_icon48[] = {
"_gAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw"
"AfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB"
"8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw"
"AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfABAAD_qgAD_p8VOwD_oRaI_qEWthD-oBbL"
"CADE_qEAFaP_oBZm_p38GBU28AHwAfAB8AHwAfBfAfAB8AFQuBCoAMe0AP4twAD_"
"BPAEIPAkAIX_-JkZCkLwAfAB8AHwAfBHAfAB8HSB_wABtABvPQQA-7DwBPAEECwA"
"0P74oBIbTvAB8AHwAfAB8EcB8AHweEGgFWwkAv0HqPAE8MSwFdX-oRr-E1LwAfAB"
"8AHwAfAB8Lzg6KEUMRAC96DwBPAE8P3EIKXAEVrwAfAB8AHwAfDzAfDAsBa3mPAE"
"8ATwwED_uAE4E1rwAfAB8AHwAfAB8PG8YKIVJDjyBPAE8ATA-KAWoF7wAfAB8AHw"
"AfB7AfD4gmmQ8ATwBPDM4-Z_XvAB8AHwAfAB8AHwwICPH_wSePQE8ATwBMClDxEB"
"YNBERP8PPz_-oEw-Pv56CACMCACCgggAXT8__yD2BA808wHwAfDAkKMSDv6AmxwS"
"_qQYH8QC6kjIAJTUAOug8ATwhOFDRABYUDw8_hWsAI9QPj7_8KwA_wTw_oL6BAC1"
"PT3-MpbwjwHwAfAB8AxmpRkUFAOOkxjzBPAEoJ8PEEAQdZQAOrwA47TwBPAoAPh9"
"yAB0hvAB8AHwAfBcsha6XFwC-KzwBPDsAOQ0EHWQAD0EAPSs8ATwBMCAn4LwAfAB"
"8AHwXLUVakz4gwTwKACbQkL-F0wBTuac8ATwiPH-_AQATh-C8AHwAfAB8ExifwAC"
"DcAAtbTwfKT8_qETejSAAJOc8ATwBPAUY9p9CAAIhvAB8AHwAfCEYZ4MGDWE8YCn"
"nEs8_3oRgAD1lPAE8ATwBKBVh4LwAQAUCwDNACQEAKBNAMwAaggAewgAoH8AywBz"
"CABbCABgNwDIAA458Nxp2IO88Hwhzf-ZEQ-0A15XjPAE8ATwhOGlfaDOVAA1oACY"
"sADhCAD9jQgA_wTwBEDMAPQEAHq7NABjCAAcq2QRvPAW9sUMXHwAiIzwBPAE8MDg"
"otZxIMsAGYQArQQATv6s8ATwzPAA4wQAUVcKAGQmYAR9MILpBAB3sP-qKgbgUPwC"
"mwQAevuAAP6U8ATwBPDAYO3QANEAHIQA3JjwBPCrBPAEoPwEAGyAQVRwAeCV_qIX"
"WPQWZNJ8AKoQgAQbSAI2hABtzAC6xAgA_aDwBPCIxOc4Ab6mjPAE8ATwBPAEAPYE"
"AK9YZwHwAfA2QwLAAEgEAK7dqPAE8MCAx8AA5Ijw_wTwBPAE8ExCFPQB8AHwAQDQ"
"Q0P-E5QAw6zwBPD1wECLcAC2iPAE8ATwBPD1BAD70AMqmvAB8AHwATDQODj-EpgA"
"1LDwxPbFBAAxcCDNAOyM8ATwewTwBOCGkvAB8AHwIOM8t-AzuPC8g66UasQwyJDw"
"VwTwBPAEIPHEBG4KAAJBDcYKAP7-LAQAT1UEAGUEAHAEAGoEAFd1BAA5BAASJAAY"
"-8Qg_gaytPB8Yeg7O_8eUTGgvwAEyABUUAK3XZAA9aTw6OJsFdgIAIOQAM8AG_11"
"_guUAKpgCAC1BADvDAD_BPAxBED-_vgIAEoA_nzDBAD4o0BA_lO88Lwgjuy4FPLw"
"AYDPABCYAKpCoABtBACKBACbEACKngQAk4wnzQBXoCd3qBe5plgh4azwBPDM8P76"
"9QQAiwwA1CjgGeQSvMD6yAQAKbHwAfAB8AHwvMC9RBH-nPAE8ATwBKDAxCFVVDXu"
"sADzCAC9dEgEX3nwAfAB8AHwvMAxBAD8X4zwBPAE8ATwTAB-3Ewh_bQADHHwAfAB"
"8AHwAfDAYL50iPAE8ATwBPDAQMRl8L8B8AHwAfAB8AHwcABMiPDvBPAE8ATwwECc"
"ZfAB8AHwrwHwAfAB8HQAAnQAsIzw9wTwBPC84OQIABy1AfAB8O8B8AHwAfDs1ZUI"
"8wTwBPD1vGDHBAAkXfAB8AHwAfCnAfAB8Mjw_zCEAJ8EAK7rpPAE8CgA9wQAvAQA"
"_lQQAJC1AfAB8AHwAfAB8FcB8MjwkBE6BABsBACPVQQApAQArwQAqQQAl_UgAHgI"
"AEsEANQsAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB"
"8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_"
"AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw"
"AfD_AfAB8AHwAfAB8AHwAfAB8A8B8AHwAfABsA"
};

BIN
src/data/joypad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

87
src/data/license.html Normal file
View File

@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>bsnes&trade; Reference License</h1><br>
<b>Copyright &copy; 2004&ndash;2009 byuu<br>
All rights reserved</b>
<hr>
<h2><u>1. Definitions</u></h2><br>
The terms "reproduce", "reproduction", "distribute" and "distribution" have the
same meaning here as under U.S. copyright law.<br><br>
"The software" means this software package as a whole, including, but not
limited to, this license, binaries, source code, documentation, and data.<br><br>
"You" means the licensee of the software.<br><br>
"The licensor" means the copyright holder of the software, byuu.
<hr>
<h2><u>2. Grant of Rights</u></h2><br>
Subject to the terms of this license, the licensor grants you a
non-transferable, non-exclusive, worldwide, royalty-free copyright license to
reproduce the software for non-commercial use only, provided the software
remains unmodified, and there is no charge for the software itself, nor for the
medium upon which the software is distributed. The reproduction of modified or
derivative works of the software is strictly prohibited without the express
consent of the licensor.
<hr>
<h2><u>3. Limitations</u></h2><br>
This license does not grant you any rights to use the licensor's name, logo or
trademarks.<br>
<br>
The software is provided "as is", and any express or implied warranties,
including, but not limited to, the implied warranties of merchantability and
fitness for a particular purpose are disclaimed. In no event shall the licensor
be liable for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of sbustitute
goods or services; loss of use, data, or profits; or business interruption)
however caused and on any theory of liability, whether in contract, strict
liability, or tort (including negligence or otherwise) arising in any way out of
the use of the software, even if advised of the possibility of such damage.<br>
<br>
In the event that this license is determined to be invalid or unenforceable, the
Grant of Rights will become null and void, and no rights shall be granted to the
licensee, within the scope of U.S. copyright law.
<hr>
<h2><u>4. Exemptions</u></h2><br>
The software includes the work of other copyrights holders, which is licensed
under different agreements, and exempt from this license. Below is a complete
list of all such software, and their respective copyright holders and licenses.
Note that explicit permission has been granted to the licensor to use included
software which is ordinarily not compatible with this license, such as the GPL.
<br>
<table border="1" cellpadding="3">
<tr><td><b>Name</b></td><td><b>License</b></td><td><b>Author(s)</b></td></tr>
<tr><td>Cx4 emulator</td><td></td><td>anomie, Kris Bleakley, Nach, zsKnight</td></tr>
<tr><td>DSP-1 emulator</td><td></td><td>Andreas Naive, John Weidman, Kris Bleakley, neviksti</td></tr>
<tr><td>DSP-2 emulator</td><td></td><td>Kris Bleakley</td></tr>
<tr><td>DSP-3 emulator</td><td></td><td>John Weidman, Kris Bleakley, Lancer, z80 gaiden</td></tr>
<tr><td>DSP-4 emulator</td><td></td><td>Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden</td></tr>
<tr><td>S-DD1 decompressor</td><td>Public Domain</td><td>Andreas Naive</td></tr>
<tr><td>S-DSP emulator</td><td>LGPL 2.1</td><td>Shay Green</td></tr>
<tr><td>SPC7110 decompressor</td><td>Public Domain</td><td>neviksti</td></tr>
<tr><td>ST-0010 emulator</td><td></td><td>Feather, John Weidman, Kris Bleakley, Matthew Kendora</td></tr>
<tr><td>Qt toolkit</td><td>LGPL 2.1</td><td>Nokia</td></tr>
<tr><td>HQ2x filter</td><td>LGPL 2.1</td><td>MaxST</td></tr>
<tr><td>JMA decompressor</td><td>GPL 2</td><td>NSRT team</td></tr>
<tr><td>NTSC filter</td><td>LGPL 2.1</td><td>Shay Green</td></tr>
<tr><td>zlib decompressor</td><td>zlib license</td><td>zlib team</td></tr>
</table>
</body>
</html>

BIN
src/data/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -131,15 +131,15 @@ bool pHiro::file_open(Window *focus, char *filename, const char *path, const cha
if(filter && *filter) {
lstring filterlist;
split(filterlist, "\n", filter);
for(unsigned i = 0; i < count(filterlist); i++) {
filterlist.split("\n", filter);
for(unsigned i = 0; i < filterlist.size(); i++) {
GtkFileFilter *filter = gtk_file_filter_new();
lstring filterpart;
split(filterpart, "\t", filterlist[i]);
filterpart.split("\t", filterlist[i]);
gtk_file_filter_set_name(filter, string() << filterpart[0] << " (" << filterpart[1] << ")");
lstring patternlist;
split(patternlist, ",", filterpart[1]);
for(unsigned l = 0; l < count(patternlist); l++) {
patternlist.split(",", filterpart[1]);
for(unsigned l = 0; l < patternlist.size(); l++) {
gtk_file_filter_add_pattern(filter, patternlist[l]);
}
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
@@ -174,15 +174,15 @@ bool pHiro::file_save(Window *focus, char *filename, const char *path, const cha
if(filter && *filter) {
lstring filterlist;
split(filterlist, "\n", filter);
for(unsigned i = 0; i < count(filterlist); i++) {
filterlist.split("\n", filter);
for(unsigned i = 0; i < filterlist.size(); i++) {
GtkFileFilter *filter = gtk_file_filter_new();
lstring filterpart;
split(filterpart, "\t", filterlist[i]);
filterpart.split("\t", filterlist[i]);
gtk_file_filter_set_name(filter, string() << filterpart[0] << " (" << filterpart[1] << ")");
lstring patternlist;
split(patternlist, ",", filterpart[1]);
for(unsigned l = 0; l < count(patternlist); l++) {
patternlist.split(",", filterpart[1]);
for(unsigned l = 0; l < patternlist.size(); l++) {
gtk_file_filter_add_pattern(filter, patternlist[l]);
}
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);

View File

@@ -29,11 +29,11 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollbox), GTK_SHADOW_ETCHED_IN);
lstring list;
split(list, "\t", columns);
list.split("\t", columns);
GType *v = (GType*)malloc(count(list) * sizeof(GType));
for(unsigned i = 0; i < count(list); i++) v[i] = G_TYPE_STRING;
store = gtk_list_store_newv(count(list), v);
GType *v = (GType*)malloc(list.size() * sizeof(GType));
for(unsigned i = 0; i < list.size(); i++) v[i] = G_TYPE_STRING;
store = gtk_list_store_newv(list.size(), v);
free(v);
listbox = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
@@ -44,8 +44,8 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
gtk_widget_show(scrollbox);
//alternate colors for each listbox entry if there are multiple columns
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(listbox), count(list) >= 2 ? true : false);
for(unsigned i = 0; i < count(list); i++) {
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(listbox), list.size() >= 2 ? true : false);
for(unsigned i = 0; i < list.size(); i++) {
unsigned i = column.size();
column[i].renderer = gtk_cell_renderer_text_new();
column[i].column = gtk_tree_view_column_new_with_attributes(
@@ -61,8 +61,8 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
}
if(text && *text) {
split(list, "\n", text);
for(unsigned i = 0; i < count(list); i++) add_item(list[i]);
list.split("\n", text);
for(unsigned i = 0; i < list.size(); i++) add_item(list[i]);
}
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(listbox), header);
@@ -85,9 +85,9 @@ void pListbox::set_column_width(unsigned index, unsigned width) {
void pListbox::add_item(const char *text) {
lstring list;
split(list, "\t", text);
list.split("\t", text);
gtk_list_store_append(store, &iter);
for(unsigned i = 0; i < count(list); i++) {
for(unsigned i = 0; i < list.size(); i++) {
gtk_list_store_set(store, &iter, i, (const char*)list[i], -1);
}
}
@@ -101,8 +101,8 @@ void pListbox::set_item(unsigned index, const char *text) {
}
lstring list;
split(list, "\t", text);
for(unsigned i = 0; i < count(list); i++) {
list.split("\t", text);
for(unsigned i = 0; i < list.size(); i++) {
gtk_list_store_set(store, &iter, i, (const char*)list[i], -1);
}
}

View File

@@ -28,8 +28,8 @@ void pEditbox::resize(unsigned width, unsigned height) {
void pEditbox::set_text(const char *text) {
string temp = text ? text : "";
replace(temp, "\r", "");
replace(temp, "\n", "\r\n");
temp.replace("\r", "");
temp.replace("\n", "\r\n");
SetWindowText(hwnd, utf16(temp));
update();
}
@@ -39,7 +39,7 @@ unsigned pEditbox::get_text(char *text, unsigned length) {
GetWindowText(hwnd, buffer, length);
string temp = (const char*)utf8(buffer);
delete[] buffer;
replace(temp, "\r", "");
temp.replace("\r", "");
strlcpy(text, temp, length);
return strlen(text);
}

View File

@@ -106,23 +106,22 @@ bool pHiro::folder_select(Window *focus, char *filename, const char *path) {
bool pHiro::file_open(Window *focus, char *filename, const char *path, const char *filter) {
string dir, f;
strcpy(dir, path ? path : "");
replace(dir, "/", "\\");
dir = path ? path : "";
dir.replace("/", "\\");
lstring type, part;
strcpy(f, "");
split(type, "\n", filter);
for(int i = 0; i < count(type); i++) {
split(part, "\t", type[i]);
if(count(part) != 2) continue;
type.split("\n", filter);
for(int i = 0; i < type.size(); i++) {
part.split("\t", type[i]);
if(part.size() != 2) continue;
strcat(f, part[0]);
strcat(f, " (");
strcat(f, part[1]);
strcat(f, ")\t");
replace(part[1], ",", ";");
strcat(f, part[1]);
strcat(f, "\t");
f.append(part[0]);
f.append(" (");
f.append(part[1]);
f.append(")\t");
part[1].replace(",", ";");
f.append(part[1]);
f.append("\t");
}
utf16 wfilter(f);
@@ -154,23 +153,22 @@ bool pHiro::file_open(Window *focus, char *filename, const char *path, const cha
bool pHiro::file_save(Window *focus, char *filename, const char *path, const char *filter) {
string dir, f;
strcpy(dir, path ? path : "");
replace(dir, "/", "\\");
dir = path ? path : "";
dir.replace("/", "\\");
lstring type, part;
strcpy(f, "");
split(type, "\n", filter);
for(int i = 0; i < count(type); i++) {
split(part, "\t", type[i]);
if(count(part) != 2) continue;
type.split("\n", filter);
for(int i = 0; i < type.size(); i++) {
part.split("\t", type[i]);
if(part.size() != 2) continue;
strcat(f, part[0]);
strcat(f, " (");
strcat(f, part[1]);
strcat(f, ")\t");
replace(part[1], ",", ";");
strcat(f, part[1]);
strcat(f, "\t");
f.append(part[0]);
f.append(" (");
f.append(part[1]);
f.append(")\t");
part[1].replace(",", ";");
f.append(part[1]);
f.append("\t");
}
utf16 wfilter(f);

View File

@@ -27,8 +27,8 @@ using nall::min;
using nall::max;
#include <nall/utf8.hpp>
using nall::utf8;
using nall::utf16;
#define utf8 nall::utf8_t
#define utf16 nall::utf16_t
extern int hiromain(int argc, const char *const argv[]);

View File

@@ -16,21 +16,21 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT);
lstring list;
split(list, "\t", columns ? columns : "");
column_count = count(list);
for(unsigned i = 0; i < count(list); i++) {
list.split("\t", columns ? columns : "");
column_count = list.size();
for(unsigned i = 0; i < list.size(); i++) {
LVCOLUMN column;
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
column.fmt = LVCFMT_LEFT;
column.iSubItem = count(list);
column.iSubItem = list.size();
utf16 ulist(list[i]);
column.pszText = ulist;
ListView_InsertColumn(hwnd, i, &column);
}
if(text && *text) {
split(list, "\n", text);
for(unsigned i = 0; i < count(list); i++) add_item(list[i]);
list.split("\n", text);
for(unsigned i = 0; i < list.size(); i++) add_item(list[i]);
}
autosize_columns();
}
@@ -47,7 +47,7 @@ void pListbox::set_column_width(unsigned column, unsigned width) {
void pListbox::add_item(const char *text) {
lstring list;
split(list, "\t", text ? text : "");
list.split("\t", text ? text : "");
LVITEM item;
unsigned pos = ListView_GetItemCount(hwnd);
item.mask = LVIF_TEXT;
@@ -57,7 +57,7 @@ void pListbox::add_item(const char *text) {
item.pszText = wtext;
ListView_InsertItem(hwnd, &item);
for(unsigned i = 1; i < count(list); i++) {
for(unsigned i = 1; i < list.size(); i++) {
utf16 wtext(list[i]);
ListView_SetItemText(hwnd, pos, i, wtext);
}
@@ -65,8 +65,8 @@ void pListbox::add_item(const char *text) {
void pListbox::set_item(unsigned index, const char *text) {
lstring list;
split(list, "\t", text ? text : "");
for(unsigned i = 0; i < count(list); i++) {
list.split("\t", text ? text : "");
for(unsigned i = 0; i < list.size(); i++) {
utf16 wtext(list[i]);
ListView_SetItemText(hwnd, index, i, wtext);
}

View File

@@ -48,6 +48,11 @@ namespace nall {
operator[](buffersize) = data;
}
signed find(const T data) {
for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return i;
return -1; //not found
}
void clear() {
memset(pool, 0, buffersize * sizeof(T));
}

View File

@@ -16,24 +16,28 @@ namespace nall {
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, string_t, unknown_t };
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() {
string get() const {
switch(type) {
case boolean_t: return (*(bool*)data == false ? "false" : "true");
case signed_t: return string() << (int)*(signed*)data;
case unsigned_t: return string() << (int)*(unsigned*)data;
case 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 "???";
@@ -42,8 +46,9 @@ namespace nall {
void set(string s) {
switch(type) {
case boolean_t: *(bool*)data = (s == "true"); break;
case signed_t: *(signed*)data = s; break;
case unsigned_t: *(unsigned*)data = s; break;
case 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: trim(s, "\""); *(string*)data = s; break;
}
}
@@ -60,24 +65,25 @@ namespace nall {
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;
}
bool load(const char *filename) {
virtual bool load(const char *filename) {
string data;
if(fread(data, filename) == true) {
replace(data, "\r", "");
if(data.readfile(filename) == true) {
data.replace("\r", "");
lstring line;
split(line, "\n", data);
line.split("\n", data);
for(unsigned i = 0; i < count(line); i++) {
for(unsigned i = 0; i < line.size(); i++) {
int position = qstrpos(line[i], "#");
if(position >= 0) line[i][position] = 0;
if(qstrpos(line[i], " = ") < 0) continue;
lstring part;
qsplit(part, " = ", line[i]);
part.qsplit(" = ", line[i]);
trim(part[0]);
trim(part[1]);
@@ -95,11 +101,15 @@ namespace nall {
}
}
bool save(const char *filename) {
virtual bool save(const char *filename) const {
file fp;
if(fp.open(filename, file::mode_write)) {
for(unsigned i = 0; i < list.size(); i++) {
fp.print(string() << list[i].name << " = " << list[i].get() << " # " << list[i].desc << "\r\n");
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();

View File

@@ -1,176 +0,0 @@
#ifndef NALL_CONFIG_HPP
#define NALL_CONFIG_HPP
#include <nall/array.hpp>
#include <nall/file.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
class setting;
class configuration {
public:
array<setting*> list;
bool load(const char *fn);
bool save(const char *fn) const;
void add(setting *setting_) { list.add(setting_); }
};
class setting {
public:
enum setting_type {
integral_type,
string_type,
} type;
const char *name;
const char *description;
virtual void set(const char *input) = 0;
virtual void get(string &output) const = 0;
virtual void get_default(string &output) const = 0;
};
class integral_setting : public setting {
public:
enum integral_type {
boolean,
decimal,
hex,
} type;
intmax_t value;
intmax_t default_value;
void set(const char *input) {
if(type == boolean) { value = !strcmp(input, "true"); }
if(type == decimal) { value = strdec(input); }
if(type == hex) { value = strhex(input); }
}
void get(string &output) const {
if(type == boolean) { output = value ? "true" : "false"; }
if(type == decimal) { output = strdec(value); }
if(type == hex) { output = string() << "0x" << strhex(value); }
}
void get_default(string &output) const {
if(type == boolean) { output = default_value ? "true" : "false"; }
if(type == decimal) { output = strdec(default_value); }
if(type == hex) { output = string() << "0x" << strhex(default_value); }
}
operator intmax_t() const { return value; }
integral_setting& operator=(intmax_t value_) { value = value_; return *this; }
integral_setting(const char *name_, const char *description_, integral_type type_, intmax_t value_) {
initialize(name_, description_, type_, value_);
}
integral_setting(configuration &parent, const char *name_, const char *description_, integral_type type_, intmax_t value_) {
initialize(name_, description_, type_, value_);
parent.add(this);
}
private:
void initialize(const char *name_, const char *description_, integral_type type_, intmax_t value_) {
setting::type = setting::integral_type;
name = name_;
description = description_;
type = type_;
value = default_value = value_;
}
};
class string_setting : public setting {
public:
string value;
string default_value;
void set(const char *input) { value = input; trim(value(), "\""); }
void get(string &output) const { output = string() << "\"" << value << "\""; }
void get_default(string &output) const { output = string() << "\"" << default_value << "\""; }
operator const char*() const { return value; }
string_setting& operator=(const char *value_) { value = value_; return *this; }
bool operator==(const char *value_) const { return value == value_; }
bool operator!=(const char *value_) const { return value != value_; }
string_setting(const char *name_, const char *description_, const char *value_) {
initialize(name_, description_, value_);
}
string_setting(configuration &parent, const char *name_, const char *description_, const char *value_) {
initialize(name_, description_, value_);
parent.add(this);
}
private:
void initialize(const char *name_, const char *description_, const char *value_) {
setting::type = setting::string_type;
name = name_;
description = description_;
value = default_value = value_;
}
};
inline bool configuration::load(const char *fn) {
//load the config file into memory
string data;
if(!fread(data, fn)) return false;
//split the file into lines
replace(data, "\r\n", "\n");
qreplace(data, "\t", "");
qreplace(data, " ", "");
lstring line, part, subpart;
split(line, "\n", data);
for(unsigned i = 0; i < count(line); i++) {
if(strlen(line[i]) == 0) continue;
if(strbegin(line[i], "#")) continue;
qsplit(part, "=", line[i]);
for(unsigned l = 0; l < list.size(); l++) {
if(part[0] == list[l]->name) {
list[l]->set(part[1]);
break;
}
}
}
return true;
}
inline bool configuration::save(const char *fn) const {
file fp;
if(!fp.open(fn, file::mode_write)) return false;
for(unsigned i = 0; i < list.size(); i++) {
string data;
lstring line, part, subpart;
strcpy(data, list[i]->description);
replace(data, "\r\n", "\n");
split(line, "\n", data);
string temp;
for(unsigned l = 0; l < count(line); l++) {
if(line[l] != "") fp.print(string() << "# " << line[l] << "\r\n");
}
string default_, value_;
list[i]->get_default(default_);
fp.print(string() << "# (default = " << default_ << ")\r\n");
list[i]->get(value_);
fp.print(string() << list[i]->name << " = " << value_ << "\r\n\r\n");
}
fp.close();
return true;
}
}
#endif

View File

@@ -27,17 +27,17 @@ namespace nall {
bool import(const char *filename) {
string data;
if(fread(data, filename) == false) return false;
if(data.readfile(filename) == false) return false;
ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
replace(data, "\r", "");
data.replace("\r", "");
lstring line;
split(line, "\n", data);
for(unsigned i = 0; i < count(line); i++) {
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
lstring part;
//format: "Input" = "Output"
qsplit(part, "=", line[i]);
if(count(part) != 2) continue;
part.qsplit("=", line[i]);
if(part.size() != 2) continue;
//remove whitespace
trim(part[0]);

View File

@@ -121,7 +121,7 @@ namespace nall {
#if !defined(_WIN32)
FILE *fp = fopen(fn, "rb");
#else
FILE *fp = _wfopen(utf16(fn), L"rb");
FILE *fp = _wfopen(utf16_t(fn), L"rb");
#endif
if(fp) {
fclose(fp);
@@ -144,10 +144,10 @@ namespace nall {
case mode_readwrite: fp = fopen(fn, "rb+"); break;
case mode_writeread: fp = fopen(fn, "wb+"); break;
#else
case mode_read: fp = _wfopen(utf16(fn), L"rb"); break;
case mode_write: fp = _wfopen(utf16(fn), L"wb+"); break;
case mode_readwrite: fp = _wfopen(utf16(fn), L"rb+"); break;
case mode_writeread: fp = _wfopen(utf16(fn), L"wb+"); break;
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;

View File

@@ -72,7 +72,7 @@ namespace nall {
break;
}
p_filehandle = CreateFileW(utf16(filename), desired_access, FILE_SHARE_READ, NULL,
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;

45
src/lib/nall/property.hpp Normal file
View File

@@ -0,0 +1,45 @@
#ifndef NALL_PROPERTY_HPP
#define NALL_PROPERTY_HPP
//nall::property implements a variable container that disallows write access
//to non-derived objects. This requires use of property::set(), as C++ lacks
//the ability to make this implementation completely transparent.
namespace nall {
class property {
public:
template<typename T> class property_t;
protected:
template<typename T> T& get(property_t<T>&);
template<typename T> property_t<T>& set(property_t<T>&, const T);
public:
template<typename T>
class property_t {
public:
const T& operator()() const { return value; }
property_t() : value() {}
property_t(const T value_) : value(value_) {}
protected:
T value;
operator T&() { return value; }
property_t& operator=(const T newValue) { value = newValue; return *this; }
friend T& property::get<T>(property_t<T>&);
friend property_t<T>& property::set<T>(property_t<T>&, const T);
};
};
template<typename T>
T& property::get(property::property_t<T> &p) {
return p.operator T&();
}
template<typename T>
property::property_t<T>& property::set(property::property_t<T> &p, const T value) {
return p.operator=(value);
}
}
#endif

View File

@@ -7,14 +7,18 @@
#include <nall/utf8.hpp>
#include <nall/string.hpp>
#include <nall/string/class.cpp>
#include <nall/string/compare.cpp>
#include <nall/string/convert.cpp>
#include <nall/string/match.cpp>
#include <nall/string/math.cpp>
#include <nall/string/replace.cpp>
#include <nall/string/split.cpp>
#include <nall/string/strl.cpp>
#include <nall/string/trim.cpp>
#include <nall/string/utility.cpp>
namespace nall {
#include <nall/string/core.cpp>
#include <nall/string/replace.cpp>
#include <nall/string/split.cpp>
}
#endif

View File

@@ -7,113 +7,169 @@
#include <nall/stdint.hpp>
#include <nall/vector.hpp>
//compare
//===============
//libc extensions
//===============
//compare.cpp
char chrlower(char c);
char chrupper(char c);
int stricmp(const char *dest, const char *src);
int strpos(const char *str, const char *key);
int strpos (const char *str, const char *key);
int qstrpos(const char *str, const char *key);
bool strbegin(const char *str, const char *key);
bool strbegin (const char *str, const char *key);
bool stribegin(const char *str, const char *key);
bool strend(const char *str, const char *key);
bool strend (const char *str, const char *key);
bool striend(const char *str, const char *key);
//convert
//convert.cpp
char* strlower(char *str);
char* strupper(char *str);
char* strtr(char *dest, const char *before, const char *after);
uintmax_t strhex(const char *str);
intmax_t strdec(const char *str);
uintmax_t strbin(const char *str);
double strdouble(const char *str);
size_t strhex(char *str, uintmax_t value, size_t length = 0);
size_t strdec(char *str, intmax_t value, size_t length = 0);
size_t strbin(char *str, uintmax_t value, size_t length = 0);
size_t strdouble(char *str, double value, size_t length = 0);
//match
char* strtr(char *dest, const char *before, const char *after);
uintmax_t strhex (const char *str);
intmax_t strsigned (const char *str);
uintmax_t strunsigned(const char *str);
uintmax_t strbin (const char *str);
double strdouble (const char *str);
size_t strhex (char *str, uintmax_t value, size_t length = 0);
size_t strsigned (char *str, intmax_t value, size_t length = 0);
size_t strunsigned(char *str, uintmax_t value, size_t length = 0);
size_t strbin (char *str, uintmax_t value, size_t length = 0);
size_t strdouble (char *str, double value, size_t length = 0);
//match.cpp
bool match(const char *pattern, const char *str);
//math
//math.cpp
bool strint (const char *str, int &result);
bool strmath(const char *str, int &result);
//strl
//strl.cpp
size_t strlcpy(char *dest, const char *src, size_t length);
size_t strlcat(char *dest, const char *src, size_t length);
//trim
//trim.cpp
char* ltrim(char *str, const char *key = " ");
char* rtrim(char *str, const char *key = " ");
char* trim (char *str, const char *key = " ");
char* ltrim_once(char *str, const char *key = " ");
char* rtrim_once(char *str, const char *key = " ");
char* trim_once (char *str, const char *key = " ");
//================
//string + lstring
//================
namespace nall {
class string;
template<typename T> inline string to_string(T);
class string {
public:
char *data;
size_t size;
void reserve(size_t);
unsigned length() const;
void reserve(size_t size_);
string& assign(const char*);
string& append(const char*);
template<typename T> string& operator= (T value) { return assign(to_string<T>(value)); }
template<typename T> string& operator<<(T value) { return append(to_string<T>(value)); }
operator int() const;
operator const char*() const;
char* operator()();
char& operator[](int);
string& operator=(int num);
string& operator=(double num);
string& operator=(const char *str);
string& operator=(const string &str);
string& operator<<(int num);
string& operator<<(double num);
string& operator<<(const char *str);
string& operator<<(const string& str);
bool operator==(const char *str) const;
bool operator!=(const char *str) const;
bool operator< (const char *str) const;
bool operator<=(const char *str) const;
bool operator> (const char *str) const;
bool operator>=(const char *str) const;
bool operator==(const char*) const;
bool operator!=(const char*) const;
bool operator< (const char*) const;
bool operator<=(const char*) const;
bool operator> (const char*) const;
bool operator>=(const char*) const;
string();
string(int num);
string(double num);
string(const char *source);
string(const string &source);
string(const char*);
string(const string&);
string& operator=(const string&);
~string();
//core.cpp
bool readfile(const char*);
//replace.cpp
string& replace (const char*, const char*);
string& qreplace(const char*, const char*);
protected:
char *data;
size_t size;
};
typedef vector<string> lstring;
class lstring : public vector<string> {
public:
template<typename T> lstring& operator<<(T value) {
operator[](size()).assign(to_string<T>(value));
return *this;
}
//core.cpp
int find(const char*);
//split.cpp
void split (const char*, const char*, unsigned = 0);
void qsplit(const char*, const char*, unsigned = 0);
};
}
size_t count(nall::lstring&);
int find(nall::lstring &str, const char *key);
//=====================
//string<>libc wrappers
//=====================
size_t strlcpy(nall::string &dest, const char *src, size_t length);
size_t strlcat(nall::string &dest, const char *src, size_t length);
void strcpy(nall::string &dest, const char *src);
void strcat(nall::string &dest, const char *src);
nall::string substr(const char *src, size_t start = 0, size_t length = 0);
nall::string& strlower(nall::string &str);
nall::string& strupper(nall::string &str);
nall::string& strtr(nall::string &dest, const char *before, const char *after);
nall::string strhex(uintmax_t value);
nall::string strdec(intmax_t value);
nall::string strbin(uintmax_t value);
nall::string strdouble(double value);
bool fread(nall::string &str, const char *filename);
nall::string& replace (nall::string &str, const char *key, const char *token);
nall::string& qreplace(nall::string &str, const char *key, const char *token);
void split (nall::lstring &dest, const char *key, const char *src, size_t limit = 0);
void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit = 0);
nall::string& ltrim(nall::string &str, const char *key = " ");
nall::string& rtrim(nall::string &str, const char *key = " ");
nall::string& trim (nall::string &str, const char *key = " ");
nall::string& ltrim_once(nall::string &str, const char *key = " ");
nall::string& rtrim_once(nall::string &str, const char *key = " ");
nall::string& trim_once (nall::string &str, const char *key = " ");
//==============
//misc functions
//==============
nall::string substr(const char *src, size_t start = 0, size_t length = 0);
nall::string strhex (uintmax_t value);
nall::string strsigned (intmax_t value);
nall::string strunsigned(uintmax_t value);
nall::string strbin (uintmax_t value);
nall::string strdouble (double value);
namespace nall {
//this is needed, as C++98 does not support explicit template specialization inside classes;
//redundant memory allocation should hopefully be avoided via compiler optimizations.
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; }
}
#endif

View File

@@ -1,232 +0,0 @@
#ifdef NALL_STRING_CPP
size_t count(nall::lstring &str) {
return str.size();
}
int find(nall::lstring &str, const char *key) {
for(size_t i = 0; i < count(str); i++) {
if(str[i] == key) return i;
}
return -1;
}
namespace nall {
void string::reserve(size_t size_) {
if(size_ > size) {
size = size_;
data = (char*)realloc(data, size + 1);
data[size] = 0;
}
}
//implicit-conversion, read-only
string::operator int() const {
return strdec(data);
}
//implicit-conversion, read-only
string::operator const char*() const {
return data;
}
//explicit-coversion, read-write
char* string::operator()() {
return data;
}
//index, read-write
char& string::operator[](int index) {
reserve(index);
return data[index];
}
string& string::operator=(int num) {
operator=(strdec(num));
return *this;
}
string& string::operator=(double num) {
operator=(strdouble(num));
return *this;
}
string& string::operator=(const char *str) {
strcpy(*this, str);
return *this;
}
string& string::operator=(const string &str) {
strcpy(*this, str);
return *this;
}
string& string::operator<<(int num) {
string temp(num);
strcat(*this, temp);
return *this;
}
string& string::operator<<(double num) {
string temp(num);
strcat(*this, temp);
return *this;
}
string& string::operator<<(const char *str) {
strcat(*this, str);
return *this;
}
string& string::operator<<(const string &str) {
strcat(*this, str);
return *this;
}
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() {
size = 64;
data = (char*)malloc(size + 1);
*data = 0;
}
string::string(int source) {
size = 64;
data = (char*)malloc(size + 1);
*data = 0;
operator=(strdec(source));
}
string::string(double source) {
size = 64;
data = (char*)malloc(size + 1);
*data = 0;
operator=(strdouble(source));
}
string::string(const char *source) {
size = strlen(source);
data = (char*)malloc(size + 1);
strcpy(data, source);
}
string::string(const string &source) {
size = strlen(source);
data = (char*)malloc(size + 1);
strcpy(data, source);
}
string::~string() {
if(data) free(data);
}
} //namespace nall
void strcpy(nall::string &dest, const char *src) {
int srclen = strlen(src);
dest.reserve(srclen);
strcpy(dest(), src);
}
void strcat(nall::string &dest, const char *src) {
int srclen = strlen(src);
int destlen = strlen(dest);
dest.reserve(srclen + destlen);
strcat(dest(), src);
}
size_t strlcpy(nall::string &dest, const char *src, size_t length) {
dest.reserve(length);
return strlcpy(dest(), src, length);
}
size_t strlcat(nall::string &dest, const char *src, size_t length) {
dest.reserve(length);
return strlcat(dest(), src, length);
}
nall::string substr(const char *src, size_t start, size_t length) {
nall::string dest;
if(length == 0) { //copy entire string
strcpy(dest, src + start);
} else { //copy partial string
strlcpy(dest, src + start, length + 1);
}
return dest;
}
/* very simplistic wrappers to return nall::string& instead of char* type */
nall::string& strlower(nall::string &str) { strlower(str()); return str; }
nall::string& strupper(nall::string &str) { strupper(str()); return str; }
nall::string& strtr(nall::string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; }
nall::string& ltrim(nall::string &str, const char *key) { ltrim(str(), key); return str; }
nall::string& rtrim(nall::string &str, const char *key) { rtrim(str(), key); return str; }
nall::string& trim (nall::string &str, const char *key) { trim (str(), key); return str; }
nall::string& ltrim_once(nall::string &str, const char *key) { ltrim_once(str(), key); return str; }
nall::string& rtrim_once(nall::string &str, const char *key) { rtrim_once(str(), key); return str; }
nall::string& trim_once (nall::string &str, const char *key) { trim_once (str(), key); return str; }
/* arithmetic <> string */
nall::string strhex(uintmax_t value) {
nall::string temp;
temp.reserve(strhex(0, value));
strhex(temp(), value);
return temp;
}
nall::string strdec(intmax_t value) {
nall::string temp;
temp.reserve(strdec(0, value));
strdec(temp(), value);
return temp;
}
nall::string strbin(uintmax_t value) {
nall::string temp;
temp.reserve(strbin(0, value));
strbin(temp(), value);
return temp;
}
nall::string strdouble(double value) {
nall::string temp;
temp.reserve(strdouble(0, value));
strdouble(temp(), value);
return temp;
}
/* ... */
bool fread(nall::string &str, const char *filename) {
strcpy(str, "");
#if !defined(_WIN32)
FILE *fp = fopen(filename, "rb");
#else
FILE *fp = _wfopen(nall::utf16(filename), L"rb");
#endif
if(!fp) return false;
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
rewind(fp);
char *fdata = (char*)malloc(size + 1);
unsigned unused = fread(fdata, 1, size, fp);
fclose(fp);
fdata[size] = 0;
strcpy(str, fdata);
free(fdata);
return true;
}
#endif

View File

@@ -37,18 +37,6 @@ char* strtr(char *dest, const char *before, const char *after) {
return dest;
}
//note: ISO C++ only guarantees that 0-9 is contigious,
//it does not guarantee that either A-F or a-f are also contigious
//however, A-F and a-f are contigious on virtually every platform in existence
//the optimizations and simplifications made possible by this are therefore unignorable
//however, just to be safe, this is tested at compile-time with the below assertion ...
//if false, a compiler error will be thrown
static nall::static_assert<
('A' == 'B' - 1) && ('B' == 'C' - 1) && ('C' == 'D' - 1) && ('D' == 'E' - 1) && ('E' == 'F' - 1) &&
('a' == 'b' - 1) && ('b' == 'c' - 1) && ('c' == 'd' - 1) && ('d' == 'e' - 1) && ('e' == 'f' - 1)
> hex_contigious_assertion_;
uintmax_t strhex(const char *str) {
if(!str) return 0;
uintmax_t result = 0;
@@ -69,7 +57,7 @@ uintmax_t strhex(const char *str) {
return result;
}
intmax_t strdec(const char *str) {
intmax_t strsigned(const char *str) {
if(!str) return 0;
intmax_t result = 0;
bool negate = false;
@@ -90,6 +78,20 @@ intmax_t strdec(const char *str) {
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;
@@ -143,6 +145,8 @@ double strdouble(const char *str) {
return !negate ? result : -result;
}
//
size_t strhex(char *str, uintmax_t value, size_t length /* = 0 */) {
if(length == 0) length -= 1U; //"infinite" length
size_t initial_length = length;
@@ -168,7 +172,7 @@ size_t strhex(char *str, uintmax_t value, size_t length /* = 0 */) {
return nall::min(initial_length, digits + 1);
}
size_t strdec(char *str, intmax_t value_, size_t length /* = 0 */) {
size_t strsigned(char *str, intmax_t value_, size_t length /* = 0 */) {
if(length == 0) length = -1U; //"infinite" length
size_t initial_length = length;
@@ -200,6 +204,31 @@ size_t strdec(char *str, intmax_t value_, size_t length /* = 0 */) {
return nall::min(initial_length, digits + 1);
}
size_t strunsigned(char *str, uintmax_t value, size_t length /* = 0 */) {
if(length == 0) length = -1U; //"infinite" length
size_t initial_length = length;
//count number of digits in value
int digits_integral = 1;
uintmax_t digits_integral_ = value;
while(digits_integral_ /= 10) digits_integral++;
int digits = digits_integral;
if(!str) return digits_integral + 1; //only computing required length?
length = nall::min(digits, length - 1);
str += length; //seek to end of target string
*str = 0; //set null terminator
while(length--) {
uint8_t x = '0' + (value % 10);
value /= 10;
*--str = x; //iterate backwards to write string
}
return nall::min(initial_length, digits + 1);
}
size_t strbin(char *str, uintmax_t value, size_t length /* = 0 */) {
if(length == 0) length = -1U; //"infinite" length
size_t initial_length = length;

View File

@@ -0,0 +1,103 @@
#ifdef NALL_STRING_CPP
void string::reserve(size_t 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::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() {
size = 64;
data = (char*)malloc(size + 1);
*data = 0;
}
string::string(const char *value) {
size = strlen(value);
data = strdup(value);
}
string::string(const string &value) {
size = strlen(value);
data = strdup(value);
}
string& string::operator=(const string &value) {
assign(value);
}
string::~string() {
free(data);
}
bool string::readfile(const char *filename) {
assign("");
#if !defined(_WIN32)
FILE *fp = fopen(filename, "rb");
#else
FILE *fp = _wfopen(nall::utf16_t(filename), L"rb");
#endif
if(!fp) return false;
fseek(fp, 0, SEEK_END);
size_t 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;
}
int lstring::find(const char *key) {
for(unsigned i = 0; i < size(); i++) {
if(operator[](i) == key) return i;
}
return -1;
}
#endif

View File

@@ -1,90 +1,98 @@
#ifdef NALL_STRING_CPP
nall::string &replace(nall::string &str, const char *key, const char *token) {
int i, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str);
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 *data;
char *buffer;
if(ksl > ssl)return str;
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(str() + i, key, ksl)) {
replace_count++;
i += ksl;
} else i++;
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);
}
size = ssl + ((tsl - ksl) * replace_count);
str.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;
}
data = (char*)malloc(size + 1);
for(i = z = 0; i < ssl;) {
if(i <= ssl - ksl) {
if(!memcmp(str() + i, key, ksl)) {
memcpy(data + z, token, tsl);
z += tsl;
i += ksl;
} else data[z++] = str()[i++];
} else data[z++] = str()[i++];
}
data[z] = 0;
strcpy(str, data);
free(data);
return str;
return *this;
}
nall::string &qreplace(nall::string &str, const char *key, const char *token) {
int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str);
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 *data;
char *buffer;
if(ksl > ssl)return str;
if(tsl > ksl) {
for(i = 0; i <= ssl - ksl;) {
x = str()[i];
if(x == '\"' || x == '\'') {
l = i;
i++;
while(str()[i++] != x) {
if(i == ssl) {
i = l;
break;
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++;
}
if(!memcmp(str() + i, key, ksl)) {
replace_count++;
i += ksl;
} else i++;
size = ssl + ((tsl - ksl) * replace_count);
reserve(size);
}
size = ssl + ((tsl - ksl) * replace_count);
str.reserve(size);
}
data = (char*)malloc(size + 1);
for(i = z = 0; i < ssl;) {
x = str()[i];
if(x == '\"' || x == '\'') {
l = i++;
while(str()[i] != x && i < ssl)i++;
if(i >= ssl)i = l;
else {
memcpy(data + z, str() + l, i - l);
z += i - l;
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++];
}
if(i <= ssl - ksl) {
if(!memcmp(str() + i, key, ksl)) {
memcpy(data + z, token, tsl);
z += tsl;
i += ksl;
replace_count++;
} else data[z++] = str()[i++];
} else data[z++] = str()[i++];
buffer[z] = 0;
assign(buffer);
delete[] buffer;
}
data[z] = 0;
strcpy(str, data);
free(data);
return str;
return *this;
}
#endif

View File

@@ -1,25 +1,25 @@
#ifdef NALL_STRING_CPP
void split(nall::lstring &dest, const char *key, const char *src, size_t limit) {
dest.reset();
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(dest[split_count++], src + lp, i - lp + 1);
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
i += ksl;
lp = i;
if(!--limit) break;
} else i++;
}
strcpy(dest[split_count++], src + lp);
operator[](split_count++) = src + lp;
}
void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit) {
dest.reset();
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;
@@ -28,24 +28,24 @@ void qsplit(nall::lstring &dest, const char *key, const char *src, size_t limit)
uint8_t x = src[i];
if(x == '\"' || x == '\'') {
int z = i++; //skip opening quote
int z = i++; //skip opening quote
while(i < ssl && src[i] != x) i++;
if(i >= ssl) i = z; //failed match, rewind 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
i++; //skip closing quote
continue; //restart in case next char is also a quote
}
}
if(!memcmp(src + i, key, ksl)) {
strlcpy(dest[split_count++], src + lp, i - lp + 1);
strlcpy(operator[](split_count++), src + lp, i - lp + 1);
i += ksl;
lp = i;
if(!--limit) break;
} else i++;
}
strcpy(dest[split_count++], src + lp);
operator[](split_count++) = src + lp;
}
#endif

View File

@@ -0,0 +1,74 @@
#ifdef NALL_STRING_CPP
size_t strlcpy(nall::string &dest, const char *src, size_t length) {
dest.reserve(length);
return strlcpy(dest(), src, length);
}
size_t strlcat(nall::string &dest, const char *src, size_t length) {
dest.reserve(length);
return strlcat(dest(), src, length);
}
nall::string substr(const char *src, size_t start, size_t length) {
nall::string dest;
if(length == 0) {
//copy entire string
dest = src + start;
} else {
//copy partial string
strlcpy(dest, src + start, length + 1);
}
return dest;
}
/* very simplistic wrappers to return nall::string& instead of char* type */
nall::string& strlower(nall::string &str) { strlower(str()); return str; }
nall::string& strupper(nall::string &str) { strupper(str()); return str; }
nall::string& strtr(nall::string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; }
nall::string& ltrim(nall::string &str, const char *key) { ltrim(str(), key); return str; }
nall::string& rtrim(nall::string &str, const char *key) { rtrim(str(), key); return str; }
nall::string& trim (nall::string &str, const char *key) { trim (str(), key); return str; }
nall::string& ltrim_once(nall::string &str, const char *key) { ltrim_once(str(), key); return str; }
nall::string& rtrim_once(nall::string &str, const char *key) { rtrim_once(str(), key); return str; }
nall::string& trim_once (nall::string &str, const char *key) { trim_once (str(), key); return str; }
/* arithmetic <> string */
nall::string strhex(uintmax_t value) {
nall::string temp;
temp.reserve(strhex(0, value));
strhex(temp(), value);
return temp;
}
nall::string strsigned(intmax_t value) {
nall::string temp;
temp.reserve(strsigned(0, value));
strsigned(temp(), value);
return temp;
}
nall::string strunsigned(uintmax_t value) {
nall::string temp;
temp.reserve(strunsigned(0, value));
strunsigned(temp(), value);
return temp;
}
nall::string strbin(uintmax_t value) {
nall::string temp;
temp.reserve(strbin(0, value));
strbin(temp(), value);
return temp;
}
nall::string strdouble(double value) {
nall::string temp;
temp.reserve(strdouble(0, value));
strdouble(temp(), value);
return temp;
}
#endif

View File

@@ -14,7 +14,7 @@
namespace nall {
//UTF-8 to UTF-16
class utf16 {
class utf16_t {
public:
operator wchar_t*() {
return buffer;
@@ -24,14 +24,14 @@ namespace nall {
return buffer;
}
utf16(const char *s = "") {
utf16_t(const char *s = "") {
if(!s) s = "";
unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0);
buffer = new(zeromemory) wchar_t[length + 1];
MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length);
}
~utf16() {
~utf16_t() {
delete[] buffer;
}
@@ -40,7 +40,7 @@ namespace nall {
};
//UTF-16 to UTF-8
class utf8 {
class utf8_t {
public:
operator char*() {
return buffer;
@@ -50,14 +50,14 @@ namespace nall {
return buffer;
}
utf8(const wchar_t *s = L"") {
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(zeromemory) char[length + 1];
WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0);
}
~utf8() {
~utf8_t() {
delete[] buffer;
}

View File

@@ -3,7 +3,7 @@
//================
//Keyboard and mouse are controlled directly via Xlib,
//as SDL cannot capture input from windows it does not create itself.
//SDL is used only to handle joysticks.
//SDL is used only to handle joysticks / gamepads.
#include <SDL/SDL.h>
#include <sys/ipc.h>
@@ -15,21 +15,21 @@
namespace ruby {
#include "sdl.hpp"
using namespace nall;
class pInputSDL {
public:
struct pInputSDL {
#include "xlibkeys.hpp"
InputSDL &self;
Display *display;
Window rootwindow;
unsigned screenwidth, screenheight;
unsigned relativex, relativey;
bool mouseacquired;
Cursor InvisibleCursor;
SDL_Joystick *gamepad[joypad<>::count];
struct {
Display *display;
Window rootwindow;
Cursor InvisibleCursor;
SDL_Joystick *gamepad[joypad<>::count];
unsigned screenwidth, screenheight;
unsigned relativex, relativey;
bool mouseacquired;
//mouse device settings
int accel_numerator;
int accel_denominator;
@@ -73,35 +73,35 @@ public:
bool acquire() {
if(acquired()) return true;
if(XGrabPointer(display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync,
rootwindow, InvisibleCursor, CurrentTime) == GrabSuccess) {
if(XGrabPointer(device.display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync,
device.rootwindow, device.InvisibleCursor, CurrentTime) == GrabSuccess) {
//backup existing cursor acceleration settings
XGetPointerControl(display, &device.accel_numerator, &device.accel_denominator, &device.threshold);
XGetPointerControl(device.display, &device.accel_numerator, &device.accel_denominator, &device.threshold);
//disable cursor acceleration
XChangePointerControl(display, True, False, 1, 1, 0);
XChangePointerControl(device.display, True, False, 1, 1, 0);
//center cursor (so that first relative poll returns 0, 0 if mouse has not moved)
XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, screenwidth / 2, screenheight / 2);
XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2);
return mouseacquired = true;
return device.mouseacquired = true;
} else {
return mouseacquired = false;
return device.mouseacquired = false;
}
}
bool unacquire() {
if(acquired()) {
//restore cursor acceleration and release cursor
XChangePointerControl(display, True, True, device.accel_numerator, device.accel_denominator, device.threshold);
XUngrabPointer(display, CurrentTime);
mouseacquired = false;
XChangePointerControl(device.display, True, True, device.accel_numerator, device.accel_denominator, device.threshold);
XUngrabPointer(device.display, CurrentTime);
device.mouseacquired = false;
}
return true;
}
bool acquired() {
return mouseacquired;
return device.mouseacquired;
}
bool poll(int16_t *table) {
@@ -112,7 +112,7 @@ public:
//========
char state[32];
XQueryKeymap(display, state);
XQueryKeymap(device.display, state);
for(unsigned i = 0; i < keyboard::limit; i++) {
uint8_t code = keycode[i];
@@ -128,28 +128,28 @@ public:
int root_x_return = 0, root_y_return = 0;
int win_x_return = 0, win_y_return = 0;
unsigned int mask_return = 0;
XQueryPointer(display, settings.handle,
XQueryPointer(device.display, settings.handle,
&root_return, &child_return, &root_x_return, &root_y_return,
&win_x_return, &win_y_return, &mask_return);
if(acquired()) {
XWindowAttributes attributes;
XGetWindowAttributes(display, settings.handle, &attributes);
XGetWindowAttributes(device.display, settings.handle, &attributes);
//absolute -> relative conversion
table[mouse::x] = (int16_t)(root_x_return - screenwidth / 2);
table[mouse::y] = (int16_t)(root_y_return - screenheight / 2);
table[mouse::x] = (int16_t)(root_x_return - device.screenwidth / 2);
table[mouse::y] = (int16_t)(root_y_return - device.screenheight / 2);
if(table[mouse::x] != 0 || table[mouse::y] != 0) {
//if mouse movement occurred, re-center mouse for next poll
XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, screenwidth / 2, screenheight / 2);
XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2);
}
} else {
table[mouse::x] = (int16_t)(root_x_return - relativex);
table[mouse::y] = (int16_t)(root_y_return - relativey);
table[mouse::x] = (int16_t)(root_x_return - device.relativex);
table[mouse::y] = (int16_t)(root_y_return - device.relativey);
relativex = root_x_return;
relativey = root_y_return;
device.relativex = root_x_return;
device.relativey = root_y_return;
}
//manual device polling is limited to only five buttons ...
@@ -165,7 +165,7 @@ public:
SDL_JoystickUpdate();
for(unsigned i = 0; i < joypad<>::count; i++) {
if(!gamepad[i]) continue;
if(!device.gamepad[i]) continue;
unsigned index = joypad<>::index(i, joypad<>::none);
table[index + joypad<>::up ] = false;
@@ -177,11 +177,12 @@ public:
resistance = max(1, min(99, resistance));
resistance = (int)((double)resistance * 32768.0 / 100.0);
unsigned axes = min((unsigned)joypad<>::axes, SDL_JoystickNumAxes(gamepad[i]));
//axes
unsigned axes = min((unsigned)joypad<>::axes, SDL_JoystickNumAxes(device.gamepad[i]));
for(unsigned axis = 0; axis < axes; axis++) {
int16_t value = (int16_t)SDL_JoystickGetAxis(gamepad[i], axis);
int16_t value = (int16_t)SDL_JoystickGetAxis(device.gamepad[i], axis);
table[index + joypad<>::axis + axis] = value;
if(axis == 0) { //X-axis
if(axis == 0) { //X-axis
table[index + joypad<>::left ] |= value < -resistance;
table[index + joypad<>::right] |= value > +resistance;
} else if(axis == 1) { //Y-axis
@@ -190,8 +191,18 @@ public:
}
}
//POV hats
if(SDL_JoystickNumHats(device.gamepad[i]) >= 1) {
uint8_t state = SDL_JoystickGetHat(device.gamepad[i], 0);
table[index + joypad<>::up ] |= state & SDL_HAT_UP;
table[index + joypad<>::down ] |= state & SDL_HAT_DOWN;
table[index + joypad<>::left ] |= state & SDL_HAT_LEFT;
table[index + joypad<>::right] |= state & SDL_HAT_RIGHT;
}
//buttons
for(unsigned button = 0; button < joypad<>::buttons; button++) {
table[index + joypad<>::button + button] = SDL_JoystickGetButton(gamepad[i], button);
table[index + joypad<>::button + button] = SDL_JoystickGetButton(device.gamepad[i], button);
}
}
@@ -203,12 +214,12 @@ public:
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
SDL_JoystickEventState(SDL_IGNORE);
display = XOpenDisplay(0);
rootwindow = DefaultRootWindow(display);
device.display = XOpenDisplay(0);
device.rootwindow = DefaultRootWindow(device.display);
XWindowAttributes attributes;
XGetWindowAttributes(display, rootwindow, &attributes);
screenwidth = attributes.width;
screenheight = attributes.height;
XGetWindowAttributes(device.display, device.rootwindow, &attributes);
device.screenwidth = attributes.width;
device.screenheight = attributes.height;
//Xlib: "because XShowCursor(false) would be too easy."
//create a fully transparent cursor named InvisibleCursor,
@@ -216,19 +227,19 @@ public:
Pixmap pixmap;
XColor black, unused;
static char invisible_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
Colormap colormap = DefaultColormap(display, DefaultScreen(display));
XAllocNamedColor(display, colormap, "black", &black, &unused);
pixmap = XCreateBitmapFromData(display, settings.handle, invisible_data, 8, 8);
InvisibleCursor = XCreatePixmapCursor(display, pixmap, pixmap, &black, &black, 0, 0);
XFreePixmap(display, pixmap);
XFreeColors(display, colormap, &black.pixel, 1, 0);
Colormap colormap = DefaultColormap(device.display, DefaultScreen(device.display));
XAllocNamedColor(device.display, colormap, "black", &black, &unused);
pixmap = XCreateBitmapFromData(device.display, settings.handle, invisible_data, 8, 8);
device.InvisibleCursor = XCreatePixmapCursor(device.display, pixmap, pixmap, &black, &black, 0, 0);
XFreePixmap(device.display, pixmap);
XFreeColors(device.display, colormap, &black.pixel, 1, 0);
mouseacquired = false;
relativex = 0;
relativey = 0;
device.mouseacquired = false;
device.relativex = 0;
device.relativey = 0;
for(unsigned i = 0; i < joypad<>::count && i < SDL_NumJoysticks(); i++) {
gamepad[i] = SDL_JoystickOpen(i);
device.gamepad[i] = SDL_JoystickOpen(i);
}
return true;
@@ -236,18 +247,18 @@ public:
void term() {
unacquire();
XFreeCursor(display, InvisibleCursor);
XFreeCursor(device.display, device.InvisibleCursor);
for(unsigned i = 0; i < joypad<>::count; i++) {
if(gamepad[i]) SDL_JoystickClose(gamepad[i]);
gamepad[i] = 0;
if(device.gamepad[i]) SDL_JoystickClose(device.gamepad[i]);
device.gamepad[i] = 0;
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
pInputSDL(InputSDL &self_) : self(self_) {
for(unsigned i = 0; i < joypad<>::count; i++) gamepad[i] = 0;
for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0;
settings.analog_axis_resistance = 75;
}
};
@@ -265,4 +276,4 @@ void InputSDL::term() { p.term(); }
InputSDL::InputSDL() : p(*new pInputSDL(*this)) {}
InputSDL::~InputSDL() { delete &p; }
} //namespace ruby
}

View File

@@ -1,4 +1,6 @@
#include <ruby/ruby.hpp>
using namespace nall;
#include <ruby/ruby_impl.cpp>
namespace ruby {

View File

@@ -12,10 +12,6 @@
#include <nall/input.hpp>
#include <nall/new.hpp>
#include <nall/stdint.hpp>
using nall::min;
using nall::max;
using nall::sclamp;
using nall::zeromemory;
namespace ruby {

View File

@@ -3,9 +3,9 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
#include <X11/extensions/XShm.h>
extern "C" XvImage* XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*);
@@ -16,18 +16,35 @@ namespace ruby {
class pVideoXv {
public:
VideoXv &self;
Display *display;
GC gc;
int screen, xv_port, xv_depth, xv_visualid;
XvImage *xvimage;
XShmSegmentInfo shminfo;
uint32_t *buffer;
uint8_t *ytable, *utable, *vtable;
bool use_child_window;
Window xwindow;
Colormap colormap;
enum XvFormat {
XvFormatRGB32,
XvFormatRGB24,
XvFormatRGB16,
XvFormatRGB15,
XvFormatYUY2,
XvFormatUYVY,
XvFormatUnknown
};
uint8_t *ytable, *utable, *vtable;
struct {
Display *display;
GC gc;
Window window;
Colormap colormap;
XShmSegmentInfo shminfo;
int port;
int depth;
int visualid;
XvImage *image;
XvFormat format;
uint32_t fourcc;
} device;
struct {
Window handle;
@@ -57,9 +74,9 @@ public:
if(setting == Video::Synchronize) {
Display *display = XOpenDisplay(0);
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
if(atom != None) {
if(atom != None && device.port >= 0) {
settings.synchronize = param;
XvSetPortAttribute(display, xv_port, atom, settings.synchronize);
XvSetPortAttribute(display, device.port, atom, settings.synchronize);
return true;
}
return false;
@@ -85,136 +102,181 @@ public:
void refresh(unsigned width, unsigned height) {
XWindowAttributes target;
XGetWindowAttributes(display, xwindow, &target);
XGetWindowAttributes(device.display, device.window, &target);
if(use_child_window) {
//we must ensure that the child window is the same size as the parent window.
//unfortunately, we cannot hook the parent window resize event notification,
//as we did not create the parent window, nor have any knowledge of the toolkit used.
//therefore, inelegant as it may be, we query each window size and resize as needed.
XWindowAttributes parent;
XGetWindowAttributes(display, settings.handle, &parent);
if(target.width != parent.width || target.height != parent.height) {
XResizeWindow(display, xwindow, parent.width, parent.height);
}
//update target width and height attributes
XGetWindowAttributes(display, xwindow, &target);
}
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)xvimage->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width >> 1; x++) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f);
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f);
uint8_t u = (utable[p0] + utable[p1]) >> 1;
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
*output++ = (u << 8) | ytable[p0];
*output++ = (v << 8) | ytable[p1];
}
input += 1024 - width;
output += 1024 - width;
//we must ensure that the child window is the same size as the parent window.
//unfortunately, we cannot hook the parent window resize event notification,
//as we did not create the parent window, nor have any knowledge of the toolkit used.
//therefore, query each window size and resize as needed.
XWindowAttributes parent;
XGetWindowAttributes(device.display, settings.handle, &parent);
if(target.width != parent.width || target.height != parent.height) {
XResizeWindow(device.display, device.window, parent.width, parent.height);
}
XvShmPutImage(display, xv_port, xwindow, gc, xvimage,
//update target width and height attributes
XGetWindowAttributes(device.display, device.window, &target);
switch(device.format) {
case XvFormatRGB32: render_rgb32(width, height); break;
case XvFormatRGB24: render_rgb24(width, height); break;
case XvFormatRGB16: render_rgb16(width, height); break;
case XvFormatRGB15: render_rgb15(width, height); break;
case XvFormatYUY2: render_yuy2 (width, height); break;
case XvFormatUYVY: render_uyvy (width, height); break;
}
XvShmPutImage(device.display, device.port, device.window, device.gc, device.image,
0, 0, width, height,
0, 0, target.width, target.height,
true);
}
bool init() {
display = XOpenDisplay(0);
screen = DefaultScreen(display);
device.display = XOpenDisplay(0);
//XShm is required for rendering
if(!XShmQueryExtension(display)) {
if(!XShmQueryExtension(device.display)) {
fprintf(stderr, "VideoXv: XShm extension not found.\n");
return false;
}
//find an appropriate port, if possible
xv_port = -1;
//find an appropriate Xv port
device.port = -1;
XvAdaptorInfo *adaptor_info;
unsigned adaptor_count;
XvQueryAdaptors(display, DefaultRootWindow(display), &adaptor_count, &adaptor_info);
XvQueryAdaptors(device.display, DefaultRootWindow(device.display), &adaptor_count, &adaptor_info);
for(unsigned i = 0; i < adaptor_count; i++) {
//find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks
if(adaptor_info[i].num_formats < 1) continue;
if(!(adaptor_info[i].type & XvInputMask)) continue;
if(!(adaptor_info[i].type & XvImageMask)) continue;
xv_port = adaptor_info[i].base_id;
xv_depth = adaptor_info[i].formats->depth;
xv_visualid = adaptor_info[i].formats->visual_id;
device.port = adaptor_info[i].base_id;
device.depth = adaptor_info[i].formats->depth;
device.visualid = adaptor_info[i].formats->visual_id;
break;
}
XvFreeAdaptorInfo(adaptor_info);
if(xv_port == -1) {
if(device.port < 0) {
fprintf(stderr, "VideoXv: failed to find valid XvPort.\n");
return false;
}
//create child window to attach to parent window.
//this is so that even if parent window visual depth doesn't match Xv visual
//(common with composited windows), Xv can still render to child window.
XWindowAttributes window_attributes;
XGetWindowAttributes(display, settings.handle, &window_attributes);
XGetWindowAttributes(device.display, settings.handle, &window_attributes);
if(xv_depth == window_attributes.depth) {
//Xv port is depth-compatible with target output window
use_child_window = false;
xwindow = settings.handle;
} else {
//Xv port is not depth-compatible with target output window
//this is often the case when a 32bpp composited window is used with a 24bpp-only Xv adaptor
//the only way to render to target is to create a child window with the Xv ports' depth
use_child_window = true;
XVisualInfo visualtemplate;
visualtemplate.visualid = xv_visualid;
visualtemplate.screen = screen;
visualtemplate.depth = xv_depth;
visualtemplate.visual = 0;
int visualmatches = 0;
XVisualInfo *visualinfo = XGetVisualInfo(display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
if(visualmatches < 1 || !visualinfo->visual) {
if(visualinfo) XFree(visualinfo);
fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n");
return false;
}
colormap = XCreateColormap(display, settings.handle, visualinfo->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = colormap;
attributes.border_pixel = 0;
attributes.event_mask = StructureNotifyMask;
xwindow = XCreateWindow(display, /* parent = */ settings.handle,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, xv_depth, InputOutput, visualinfo->visual,
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XFree(visualinfo);
XSetWindowBackground(display, xwindow, /* color = */ 0);
XMapWindow(display, xwindow);
XVisualInfo visualtemplate;
visualtemplate.visualid = device.visualid;
visualtemplate.screen = DefaultScreen(device.display);
visualtemplate.depth = device.depth;
visualtemplate.visual = 0;
int visualmatches = 0;
XVisualInfo *visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
if(visualmatches < 1 || !visualinfo->visual) {
if(visualinfo) XFree(visualinfo);
fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n");
return false;
}
gc = XCreateGC(display, xwindow, 0, 0);
device.colormap = XCreateColormap(device.display, settings.handle, visualinfo->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = device.colormap;
attributes.border_pixel = 0;
attributes.event_mask = StructureNotifyMask;
device.window = XCreateWindow(device.display, /* parent = */ settings.handle,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, device.depth, InputOutput, visualinfo->visual,
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XFree(visualinfo);
XSetWindowBackground(device.display, device.window, /* color = */ 0);
XMapWindow(device.display, device.window);
device.gc = XCreateGC(device.display, device.window, 0, 0);
//set colorkey to auto paint, so that Xv video output is always visible
Atom atom = XInternAtom(display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(display, xv_port, atom, 1);
Atom atom = XInternAtom(device.display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(device.display, device.port, atom, 1);
//0x32595559 = 16-bit Y8U8,Y8V8 (YUY2)
xvimage = XvShmCreateImage(display, xv_port, 0x32595559, 0, 1024, 1024, &shminfo);
if(!xvimage) {
//find optimal rendering format
device.format = XvFormatUnknown;
signed format_count;
XvImageFormatValues *format = XvListImageFormats(device.display, device.port, &format_count);
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 32) {
device.format = XvFormatRGB32;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 24) {
device.format = XvFormatRGB24;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 16) {
device.format = XvFormatRGB16;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvRGB && format[i].bits_per_pixel == 15) {
device.format = XvFormatRGB15;
device.fourcc = format[i].id;
break;
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
if(format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U'
&& format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V'
) {
device.format = XvFormatYUY2;
device.fourcc = format[i].id;
break;
}
}
}
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
if(format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y'
&& format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y'
) {
device.format = XvFormatUYVY;
device.fourcc = format[i].id;
break;
}
}
}
free(format);
if(device.format == XvFormatUnknown) {
fprintf(stderr, "VideoXv: unable to find a supported image format.\n");
return false;
}
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, 1024, 1024, &device.shminfo);
if(!device.image) {
fprintf(stderr, "VideoXv: XShmCreateImage failed.\n");
return false;
}
shminfo.shmid = shmget(IPC_PRIVATE, xvimage->data_size, IPC_CREAT | 0777);
shminfo.shmaddr = xvimage->data = (char*)shmat(shminfo.shmid, 0, 0);
shminfo.readOnly = false;
if(!XShmAttach(display, &shminfo)) {
device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777);
device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0);
device.shminfo.readOnly = false;
if(!XShmAttach(device.display, &device.shminfo)) {
fprintf(stderr, "VideoXv: XShmAttach failed.\n");
return false;
}
@@ -226,18 +288,16 @@ public:
}
void term() {
XShmDetach(display, &shminfo);
XShmDetach(device.display, &device.shminfo);
if(use_child_window) {
if(xwindow) {
XUnmapWindow(display, xwindow);
xwindow = 0;
}
if(device.window) {
XUnmapWindow(device.display, device.window);
device.window = 0;
}
if(colormap) {
XFreeColormap(display, colormap);
colormap = 0;
}
if(device.colormap) {
XFreeColormap(device.display, device.colormap);
device.colormap = 0;
}
if(buffer) { delete[] buffer; buffer = 0; }
@@ -246,6 +306,110 @@ public:
if(vtable) { delete[] vtable; vtable = 0; }
}
void render_rgb32(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint32_t *output = (uint32_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
memcpy(output, input, width * 4);
input += 1024 - width;
output += 1024 - width;
}
}
void render_rgb24(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint8_t *output = (uint8_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint32_t p = *input++;
*output++ = p;
*output++ = p >> 8;
*output++ = p >> 16;
}
input += (1024 - width);
output += (1024 - width) * 3;
}
}
void render_rgb16(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint32_t p = *input++;
*output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16
}
input += 1024 - width;
output += 1024 - width;
}
}
void render_rgb15(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint32_t p = *input++;
*output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15
}
input += 1024 - width;
output += 1024 - width;
}
}
void render_yuy2(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width >> 1; x++) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); //RGB32->RGB16
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); //RGB32->RGB16
uint8_t u = (utable[p0] + utable[p1]) >> 1;
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
*output++ = (u << 8) | ytable[p0];
*output++ = (v << 8) | ytable[p1];
}
input += 1024 - width;
output += 1024 - width;
}
}
void render_uyvy(unsigned width, unsigned height) {
uint32_t *input = (uint32_t*)buffer;
uint16_t *output = (uint16_t*)device.image->data;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width >> 1; x++) {
uint32_t p0 = *input++;
uint32_t p1 = *input++;
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f);
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f);
uint8_t u = (utable[p0] + utable[p1]) >> 1;
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
*output++ = (ytable[p0] << 8) | u;
*output++ = (ytable[p1] << 8) | v;
}
input += 1024 - width;
output += 1024 - width;
}
}
void init_yuv_tables() {
ytable = new uint8_t[65536];
utable = new uint8_t[65536];
@@ -254,17 +418,18 @@ public:
for(unsigned i = 0; i < 65536; i++) {
//extract RGB565 color data from i
uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31;
r = (r << 3) | (r >> 2); //R5->R8
g = (g << 2) | (g >> 4); //G6->G8
b = (b << 3) | (b >> 2); //B5->B8
r = (r << 3) | (r >> 2); //R5->R8
g = (g << 2) | (g >> 4); //G6->G8
b = (b << 3) | (b >> 2); //B5->B8
//RGB->YUV conversion
//ITU-R Recommendation BT.601
//double lr = 0.299, lg = 0.587, lb = 0.114;
int y = int( +(double(r) * 0.257) + (double(g) * 0.504) + (double(b) * 0.098) + 16.0 );
int u = int( -(double(r) * 0.148) - (double(g) * 0.291) + (double(b) * 0.439) + 128.0 );
int v = int( +(double(r) * 0.439) - (double(g) * 0.368) - (double(b) * 0.071) + 128.0 );
//RGB->YCbCr conversion
//double lr = 0.2126, lb = 0.0722, lg = (1.0 - lr - lb);
//ITU-R Recommendation BT.709
//double lr = 0.2126, lg = 0.7152, lb = 0.0722;
//int y = int( double(r) * lr + double(g) * lg + double(b) * lb );
//int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 );
//int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 );
@@ -276,15 +441,15 @@ public:
}
pVideoXv(VideoXv &self_) : self(self_) {
use_child_window = false;
xwindow = 0;
colormap = 0;
device.window = 0;
device.colormap = 0;
device.port = -1;
ytable = 0;
utable = 0;
vtable = 0;
settings.handle = 0;
settings.handle = 0;
settings.synchronize = false;
}
};
@@ -301,4 +466,4 @@ void VideoXv::term() { p.term(); }
VideoXv::VideoXv() : p(*new pVideoXv(*this)) {}
VideoXv::~VideoXv() { delete &p; }
} //namespace ruby
}

View File

@@ -1,8 +1,6 @@
#include <../base.hpp>
#define MEMORY_CPP
#include "memory_rw.cpp"
namespace memory {
MMIOAccess mmio;
StaticRAM wram(128 * 1024);

View File

@@ -1,14 +1,7 @@
struct Memory {
virtual unsigned size() { return 0; }
virtual unsigned size() const { return 0; }
virtual uint8 read(unsigned addr) = 0;
virtual void write(unsigned addr, uint8 data) = 0;
//deprecated, still used by S-CPU, S-SMP disassemblers
enum { WRAP_NONE = 0, WRAP_BANK = 1, WRAP_PAGE = 2 };
virtual uint16 read_word(unsigned addr, unsigned wrap = WRAP_NONE);
virtual void write_word(unsigned addr, uint16 data, unsigned wrap = WRAP_NONE);
virtual uint32 read_long(unsigned addr, unsigned wrap = WRAP_NONE);
virtual void write_long(unsigned addr, uint32 data, unsigned wrap = WRAP_NONE);
};
struct MMIO {
@@ -28,7 +21,7 @@ struct UnmappedMMIO : MMIO {
struct StaticRAM : Memory {
uint8* handle() { return data; }
unsigned size() { return datasize; }
unsigned size() const { return datasize; }
inline uint8 read(unsigned addr) { return data[addr]; }
inline void write(unsigned addr, uint8 n) { data[addr] = n; }
@@ -47,7 +40,7 @@ struct MappedRAM : Memory {
void map(uint8 *source, unsigned length) { data = source; datasize = length > 0 ? length : -1U; }
void write_protect(bool status) { write_protection = status; }
uint8* handle() { return data; }
unsigned size() { return datasize; }
unsigned size() const { return datasize; }
inline uint8 read(unsigned addr) { return data[addr]; }
inline void write(unsigned addr, uint8 n) { if(!write_protection) data[addr] = n; }
@@ -83,7 +76,7 @@ public:
alwaysinline uint8 read(unsigned addr) {
#if defined(CHEAT_SYSTEM)
if(cheat.enabled() && cheat.exists(addr)) {
if(cheat.active() && cheat.exists(addr)) {
uint8 r;
if(cheat.read(addr, r)) return r;
}
@@ -112,9 +105,8 @@ public:
return 12;
}
virtual void load_cart() = 0;
virtual bool load_cart() = 0;
virtual void unload_cart() = 0;
virtual bool cart_loaded() = 0;
virtual void power() = 0;
virtual void reset() = 0;
@@ -131,12 +123,12 @@ protected:
};
namespace memory {
extern MMIOAccess mmio; //S-CPU, S-PPU
extern StaticRAM wram; //S-CPU
extern StaticRAM apuram; //S-SMP, S-DSP
extern StaticRAM vram; //S-PPU
extern StaticRAM oam; //S-PPU
extern StaticRAM cgram; //S-PPU
extern MMIOAccess mmio; //S-CPU, S-PPU
extern StaticRAM wram; //S-CPU
extern StaticRAM apuram; //S-SMP, S-DSP
extern StaticRAM vram; //S-PPU
extern StaticRAM oam; //S-PPU
extern StaticRAM cgram; //S-PPU
extern UnmappedMemory memory_unmapped;
extern UnmappedMMIO mmio_unmapped;

View File

@@ -1,81 +0,0 @@
#ifdef MEMORY_CPP
uint16 Memory::read_word(unsigned addr, unsigned wrap) {
uint16 r;
switch(wrap) {
case WRAP_NONE: {
r = read(addr);
r |= read(addr + 1) << 8;
} break;
case WRAP_BANK: {
r = read(addr);
r |= read((addr & 0xff0000) | ((addr + 1) & 0xffff)) << 8;
} break;
case WRAP_PAGE: {
r = read(addr);
r |= read((addr & 0xffff00) | ((addr + 1) & 0xff)) << 8;
} break;
}
return r;
}
void Memory::write_word(unsigned addr, uint16 data, unsigned wrap) {
switch(wrap) {
case WRAP_NONE: {
write(addr, data);
write(addr + 1, data >> 8);
} return;
case WRAP_BANK: {
write(addr, data);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8);
} return;
case WRAP_PAGE: {
write(addr, data);
write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8);
} return;
}
}
uint32 Memory::read_long(unsigned addr, unsigned wrap) {
uint32 r;
switch(wrap) {
case WRAP_NONE: {
r = read(addr);
r |= read(addr + 1) << 8;
r |= read(addr + 2) << 16;
} break;
case WRAP_BANK: {
r = read(addr);
r |= read((addr & 0xff0000) | ((addr + 1) & 0xffff)) << 8;
r |= read((addr & 0xff0000) | ((addr + 2) & 0xffff)) << 16;
} break;
case WRAP_PAGE: {
r = read(addr);
r |= read((addr & 0xffff00) | ((addr + 1) & 0xff)) << 8;
r |= read((addr & 0xffff00) | ((addr + 2) & 0xff)) << 16;
} break;
}
return r;
}
void Memory::write_long(unsigned addr, uint32 data, unsigned wrap) {
switch(wrap) {
case WRAP_NONE: {
write(addr, data);
write(addr + 1, data >> 8);
write(addr + 2, data >> 16);
} return;
case WRAP_BANK: {
write(addr, data);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8);
write((addr & 0xff0000) | ((addr + 2) & 0xffff), data >> 16);
} return;
case WRAP_PAGE: {
write(addr, data);
write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8);
write((addr & 0xffff00) | ((addr + 2) & 0xff), data >> 16);
} return;
}
}
#endif //ifdef MEMORY_CPP

View File

@@ -6,7 +6,7 @@ void sBus::map_cx4() {
}
void sBus::map_dsp1() {
switch(cartridge.info.dsp1_mapper) {
switch(cartridge.dsp1_mapper()) {
case Cartridge::DSP1LoROM1MB: {
map(MapDirect, 0x20, 0x3f, 0x8000, 0xffff, dsp1);
map(MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, dsp1);
@@ -51,4 +51,4 @@ void sBus::map_st010() {
map(MapDirect, 0xe8, 0xef, 0x0000, 0x0fff, st010);
}
#endif //ifdef SMEMORY_CPP
#endif

View File

@@ -33,13 +33,13 @@ void sBus::map_generic() {
} break;
case Cartridge::SPC7110ROM: {
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port
map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM
map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM
map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port
map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM
map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM
map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM
} break;
case Cartridge::BSXROM: {
@@ -97,8 +97,8 @@ void sBus::map_generic_sram() {
//otherwise, default to safer, larger SRAM address window
uint16 addr_hi = (memory::cartrom.size() > 0x200000 || memory::cartram.size() > 32 * 1024) ? 0x7fff : 0xffff;
map(MapLinear, 0x70, 0x7f, 0x0000, addr_hi, memory::cartram);
if(cartridge.info.mapper != Cartridge::LoROM) return;
if(cartridge.mapper() != Cartridge::LoROM) return;
map(MapLinear, 0xf0, 0xff, 0x0000, addr_hi, memory::cartram);
}
#endif //ifdef SMEMORY_CPP
#endif

View File

@@ -18,4 +18,4 @@ void sBus::map_system() {
map(MapLinear, 0x7e, 0x7f, 0x0000, 0xffff, memory::wram);
}
#endif //ifdef SMEMORY_CPP
#endif

View File

@@ -17,38 +17,29 @@ void sBus::reset() {
set_speed(false);
}
void sBus::load_cart() {
if(is_cart_loaded == true) return;
bool sBus::load_cart() {
if(cartridge.loaded() == true) return false;
map_reset();
map_generic();
map_system();
if(cartridge.info.cx4) map_cx4();
if(cartridge.info.dsp1) map_dsp1();
if(cartridge.info.dsp2) map_dsp2();
if(cartridge.info.dsp3) map_dsp3();
if(cartridge.info.dsp4) map_dsp4();
if(cartridge.info.obc1) map_obc1();
if(cartridge.info.st010) map_st010();
if(cartridge.has_cx4()) map_cx4();
if(cartridge.has_dsp1()) map_dsp1();
if(cartridge.has_dsp2()) map_dsp2();
if(cartridge.has_dsp3()) map_dsp3();
if(cartridge.has_dsp4()) map_dsp4();
if(cartridge.has_obc1()) map_obc1();
if(cartridge.has_st010()) map_st010();
is_cart_loaded = true;
return true;
}
void sBus::unload_cart() {
if(is_cart_loaded == false) return;
is_cart_loaded = false;
}
bool sBus::cart_loaded() {
return is_cart_loaded;
}
sBus::sBus() {
is_cart_loaded = false;
}
sBus::~sBus() {
unload_cart();
}

View File

@@ -1,8 +1,7 @@
class sBus : public Bus {
public:
void load_cart();
bool load_cart();
void unload_cart();
bool cart_loaded();
void power();
void reset();
@@ -11,8 +10,6 @@ public:
~sBus();
private:
bool is_cart_loaded;
void map_reset();
void map_system();
void map_generic();

View File

@@ -70,7 +70,7 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
py >>= 8;
switch(regs.mode7_repeat) {
case 0: //screen repitition outside of screen area
case 0: //screen repetition outside of screen area
case 1: { //same as case 0
px &= 1023;
py &= 1023;

View File

@@ -39,7 +39,7 @@ void bPPU::build_sprite_list() {
}
sprite_list[i].x = (x << 8) + tableA[0];
sprite_list[i].y = tableA[1] + 1;
sprite_list[i].y = (tableA[1] + 1) & 0xff;
sprite_list[i].character = tableA[2];
sprite_list[i].vflip = !!(tableA[3] & 0x80);
sprite_list[i].hflip = !!(tableA[3] & 0x40);
@@ -66,7 +66,7 @@ bool bPPU::is_sprite_on_scanline() {
void bPPU::load_oam_tiles() {
uint16 tile_width = spr->width >> 3;
int x = spr->x;
int y = line - spr->y;
int y = (line - spr->y) & 0xff;
if(regs.oam_interlace == true) {
y <<= 1;
}

View File

@@ -25,6 +25,8 @@ void PPU::frame() {
}
void PPU::power() {
ppu1_version = snes.config.ppu1.version;
ppu2_version = snes.config.ppu2.version;
}
void PPU::reset() {
@@ -38,9 +40,6 @@ PPU::PPU() {
status.frames_updated = false;
status.frames_rendered = 0;
status.frames_executed = 0;
ppu1_version = 1;
ppu2_version = 3;
}
PPU::~PPU() {

View File

@@ -38,7 +38,7 @@ GZReader::GZReader(const char *fn) : gp(0) {
#if !defined(_WIN32)
fp = fopen(fn, "rb");
#else
fp = _wfopen(utf16(fn), L"rb");
fp = _wfopen(utf16_t(fn), L"rb");
#endif
if(!fp) return;

View File

@@ -1,17 +1,12 @@
/*****
* SNES Interface class
*
* Interfaces SNES core with platform-specific functionality
* (video, audio, input, ...)
*****/
//====================
//SNES interface class
//====================
//Interfaces SNES core with platform-specific functionality (video, audio, input, ...)
class SNESInterface {
public:
void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height);
void audio_sample(uint16_t l_sample, uint16_t r_sample);
function<bool ()> input_ready;
void input_poll();
int16_t input_poll(unsigned deviceid, unsigned id);

View File

@@ -80,20 +80,20 @@ void SNES::power() {
ppu.power();
bus.power();
if(expansion() == ExpansionBSX) bsxbase.power();
if(expansion() == ExpansionBSX) bsxbase.power();
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.power();
if(cartridge.bsx_flash_loaded()) bsxflash.power();
if(cartridge.info.bsxcart) bsxcart.power();
if(cartridge.info.bsxflash) bsxflash.power();
if(cartridge.info.srtc) srtc.power();
if(cartridge.info.sdd1) sdd1.power();
if(cartridge.info.spc7110) spc7110.power();
if(cartridge.info.cx4) cx4.power();
if(cartridge.info.dsp1) dsp1.power();
if(cartridge.info.dsp2) dsp2.power();
if(cartridge.info.dsp3) dsp3.power();
if(cartridge.info.dsp4) dsp4.power();
if(cartridge.info.obc1) obc1.power();
if(cartridge.info.st010) st010.power();
if(cartridge.has_srtc()) srtc.power();
if(cartridge.has_sdd1()) sdd1.power();
if(cartridge.has_spc7110()) spc7110.power();
if(cartridge.has_cx4()) cx4.power();
if(cartridge.has_dsp1()) dsp1.power();
if(cartridge.has_dsp2()) dsp2.power();
if(cartridge.has_dsp3()) dsp3.power();
if(cartridge.has_dsp4()) dsp4.power();
if(cartridge.has_obc1()) obc1.power();
if(cartridge.has_st010()) st010.power();
for(unsigned i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu);
for(unsigned i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu);
@@ -102,20 +102,20 @@ void SNES::power() {
for(unsigned i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu);
for(unsigned i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu);
if(expansion() == ExpansionBSX) bsxbase.enable();
if(expansion() == ExpansionBSX) bsxbase.enable();
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.enable();
if(cartridge.bsx_flash_loaded()) bsxflash.enable();
if(cartridge.info.bsxcart) bsxcart.enable();
if(cartridge.info.bsxflash) bsxflash.enable();
if(cartridge.info.srtc) srtc.enable();
if(cartridge.info.sdd1) sdd1.enable();
if(cartridge.info.spc7110) spc7110.enable();
if(cartridge.info.cx4) cx4.enable();
if(cartridge.info.dsp1) dsp1.enable();
if(cartridge.info.dsp2) dsp2.enable();
if(cartridge.info.dsp3) dsp3.enable();
if(cartridge.info.dsp4) dsp4.enable();
if(cartridge.info.obc1) obc1.enable();
if(cartridge.info.st010) st010.enable();
if(cartridge.has_srtc()) srtc.enable();
if(cartridge.has_sdd1()) sdd1.enable();
if(cartridge.has_spc7110()) spc7110.enable();
if(cartridge.has_cx4()) cx4.enable();
if(cartridge.has_dsp1()) dsp1.enable();
if(cartridge.has_dsp2()) dsp2.enable();
if(cartridge.has_dsp3()) dsp3.enable();
if(cartridge.has_dsp4()) dsp4.enable();
if(cartridge.has_obc1()) obc1.enable();
if(cartridge.has_st010()) st010.enable();
input.port_set_device(0, snes.config.controller_port1);
input.port_set_device(1, snes.config.controller_port2);
@@ -133,20 +133,20 @@ void SNES::reset() {
ppu.reset();
bus.reset();
if(expansion() == ExpansionBSX) bsxbase.reset();
if(expansion() == ExpansionBSX) bsxbase.reset();
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.reset();
if(cartridge.bsx_flash_loaded()) bsxflash.reset();
if(cartridge.info.bsxcart) bsxcart.reset();
if(cartridge.info.bsxflash) bsxflash.reset();
if(cartridge.info.srtc) srtc.reset();
if(cartridge.info.sdd1) sdd1.reset();
if(cartridge.info.spc7110) spc7110.reset();
if(cartridge.info.cx4) cx4.reset();
if(cartridge.info.dsp1) dsp1.reset();
if(cartridge.info.dsp2) dsp2.reset();
if(cartridge.info.dsp3) dsp3.reset();
if(cartridge.info.dsp4) dsp4.reset();
if(cartridge.info.obc1) obc1.reset();
if(cartridge.info.st010) st010.reset();
if(cartridge.has_srtc()) srtc.reset();
if(cartridge.has_sdd1()) sdd1.reset();
if(cartridge.has_spc7110()) spc7110.reset();
if(cartridge.has_cx4()) cx4.reset();
if(cartridge.has_dsp1()) dsp1.reset();
if(cartridge.has_dsp2()) dsp2.reset();
if(cartridge.has_dsp3()) dsp3.reset();
if(cartridge.has_dsp4()) dsp4.reset();
if(cartridge.has_obc1()) obc1.reset();
if(cartridge.has_st010()) st010.reset();
input.port_set_device(0, snes.config.controller_port1);
input.port_set_device(1, snes.config.controller_port2);
@@ -184,16 +184,18 @@ SNES::SNES() : snes_region(NTSC), snes_expansion(ExpansionNone) {
config.file.autodetect_type = false;
config.file.bypass_patch_crc32 = false;
config.path.base = "";
config.path.user = "";
config.path.rom = "";
config.path.save = "";
config.path.patch = "";
config.path.cheat = "";
config.path.exportdata = "";
config.path.bsx = "";
config.path.st = "";
config.path.base = "";
config.path.user = "";
config.path.current = "";
config.path.rom = "";
config.path.save = "";
config.path.patch = "";
config.path.cheat = "";
config.path.data = "";
config.path.bsx = "";
config.path.st = "";
config.cpu.version = 2;
config.cpu.ntsc_clock_rate = 21477272;
config.cpu.pal_clock_rate = 21281370;
config.cpu.alu_mul_delay = 2;
@@ -202,4 +204,7 @@ SNES::SNES() : snes_region(NTSC), snes_expansion(ExpansionNone) {
config.smp.ntsc_clock_rate = 32041 * 768;
config.smp.pal_clock_rate = 32041 * 768;
config.ppu1.version = 1;
config.ppu2.version = 3;
}

View File

@@ -22,12 +22,15 @@ public:
} file;
struct Path {
string base, user;
string rom, save, patch, cheat, exportdata;
string base; //binary path
string user; //user profile path (bsnes.cfg, ...)
string current; //current working directory (path to currently loaded cartridge)
string rom, save, patch, cheat, data;
string bsx, st;
} path;
struct CPU {
unsigned version;
unsigned ntsc_clock_rate;
unsigned pal_clock_rate;
unsigned alu_mul_delay;
@@ -39,6 +42,14 @@ public:
unsigned ntsc_clock_rate;
unsigned pal_clock_rate;
} smp;
struct PPU1 {
unsigned version;
} ppu1;
struct PPU2 {
unsigned version;
} ppu2;
} config;
//system functions

View File

@@ -47,7 +47,7 @@ void Tracer::trace_smpop() {
void Tracer::enable(bool en) {
if(en == true && enabled() == false) {
fp = fopen(Cartridge::filepath("trace.log", snes.config.path.exportdata), "wb");
fp = fopen(Cartridge::filepath("trace.log", snes.config.path.data), "wb");
} else if(en == false && enabled() == true) {
fclose(fp);
fp = 0;

View File

@@ -1,36 +0,0 @@
namespace resource {
#include "../data/icon48.h"
#include "../data/controller.h"
static uint8_t *icon48;
static uint8_t *controller;
//call once at program startup
void init() {
uint8_t *lzssdata;
uint8_t *rawdata;
unsigned length;
base64::decode(lzssdata, length, enc_icon48);
lzss::decode(icon48, lzssdata, 48 * 48 * 4);
delete[] lzssdata;
//controller data stored as 24-bit RGB888
//expand to 32-bit ARGB8888 for direct use with hiro::Canvas
base64::decode(lzssdata, length, enc_controller);
lzss::decode(rawdata, lzssdata, 372 * 178 * 3);
delete[] lzssdata;
controller = new uint8_t[372 * 178 * 4];
for(unsigned dp = 0, sp = 0, y = 0; y < 178; y++) {
for(unsigned x = 0; x < 372; x++) {
controller[dp++] = rawdata[sp++]; //blue
controller[dp++] = rawdata[sp++]; //green
controller[dp++] = rawdata[sp++]; //red
controller[dp++] = 255; //alpha
}
}
delete[] rawdata;
}
} //namespace resource

31
src/ui_hiro/Makefile Normal file
View File

@@ -0,0 +1,31 @@
##############################
### platform configuration ###
##############################
objects := main hiro $(if $(call streq,$(platform),win),resource) $(objects)
ifeq ($(platform),x)
link += `pkg-config --libs gtk+-2.0`
link += $(call mklib,Xtst)
hiroflags = `pkg-config --cflags gtk+-2.0`
else ifeq ($(platform),win)
link += $(call mklib,comctl32)
link += $(call mklib,comdlg32)
hiroflags =
endif
#############
### rules ###
#############
obj/main.$(obj): $(ui)/main.cpp $(ui)/* $(ui)/base/* $(ui)/loader/* $(ui)/settings/* $(ui)/event/*
obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/*
$(call compile,$(hiroflags))
obj/resource.$(obj): $(ui)/bsnes.rc; windres $(ui)/bsnes.rc obj/resource.$(obj)
###############
### targets ###
###############
ui_build:;
ui_clean:;

View File

@@ -1,4 +1,4 @@
#define IDI_APP_ICON 100
1 24 "ui/bsnes.Manifest"
1 24 "data/bsnes.Manifest"
IDI_APP_ICON ICON DISCARDABLE "data/bsnes.ico"

View File

@@ -79,7 +79,6 @@ public:
struct Misc {
bool start_in_fullscreen_mode;
unsigned window_opacity;
unsigned cheat_autosort;
bool show_advanced_options;
} misc;
@@ -96,13 +95,13 @@ public:
attach(snes.config.file.autodetect_type = false, "file.autodetect_type", "Detect filetype by header, rather than file extension");
attach(snes.config.file.bypass_patch_crc32 = false, "file.bypass_patch_crc32", "Apply UPS patches even when checksum match fails");
attach(snes.config.path.rom = "", "path.rom");
attach(snes.config.path.save = "", "path.save");
attach(snes.config.path.patch = "", "path.patch");
attach(snes.config.path.cheat = "", "path.cheat");
attach(snes.config.path.exportdata = "", "path.exportdata");
attach(snes.config.path.bsx = "", "path.bsx");
attach(snes.config.path.st = "", "path.st");
attach(snes.config.path.rom = "", "path.rom");
attach(snes.config.path.save = "", "path.save");
attach(snes.config.path.patch = "", "path.patch");
attach(snes.config.path.cheat = "", "path.cheat");
attach(snes.config.path.data = "", "path.data");
attach(snes.config.path.bsx = "", "path.bsx");
attach(snes.config.path.st = "", "path.st");
attach(snes.config.cpu.ntsc_clock_rate = 21477272, "cpu.ntsc_clock_rate");
attach(snes.config.cpu.pal_clock_rate = 21281370, "cpu.pal_clock_rate");
@@ -242,7 +241,6 @@ public:
attach(misc.start_in_fullscreen_mode = false, "misc.start_in_fullscreen_mode");
attach(misc.window_opacity = 100, "misc.window_opacity", "Translucency percentage of helper windows (50%-100%)");
attach(misc.cheat_autosort = false, "misc.cheat_autosort");
attach(misc.show_advanced_options = false, "misc.show_advanced_options", "Enable developer-oriented GUI options");
}

View File

@@ -1,23 +1,23 @@
void export_memory() {
file fp;
fp.open(Cartridge::filepath("wram.bin", snes.config.path.exportdata), file::mode_write);
fp.open(Cartridge::filepath("wram.bin", snes.config.path.data), file::mode_write);
for(unsigned i = 0; i < memory::wram.size(); i++) fp.write(memory::wram[i]);
fp.close();
fp.open(Cartridge::filepath("apuram.bin", snes.config.path.exportdata), file::mode_write);
fp.open(Cartridge::filepath("apuram.bin", snes.config.path.data), file::mode_write);
for(unsigned i = 0; i < memory::apuram.size(); i++) fp.write(memory::apuram[i]);
fp.close();
fp.open(Cartridge::filepath("vram.bin", snes.config.path.exportdata), file::mode_write);
fp.open(Cartridge::filepath("vram.bin", snes.config.path.data), file::mode_write);
for(unsigned i = 0; i < memory::vram.size(); i++) fp.write(memory::vram[i]);
fp.close();
fp.open(Cartridge::filepath("oam.bin", snes.config.path.exportdata), file::mode_write);
fp.open(Cartridge::filepath("oam.bin", snes.config.path.data), file::mode_write);
for(unsigned i = 0; i < memory::oam.size(); i++) fp.write(memory::oam[i]);
fp.close();
fp.open(Cartridge::filepath("cgram.bin", snes.config.path.exportdata), file::mode_write);
fp.open(Cartridge::filepath("cgram.bin", snes.config.path.data), file::mode_write);
for(unsigned i = 0; i < memory::cgram.size(); i++) fp.write(memory::cgram[i]);
fp.close();

View File

@@ -155,20 +155,20 @@ void modify_system_state(system_state_t state) {
status.flush();
string t = translate["Loaded $."];
replace(t, "$", cartridge.info.filename);
t.replace("$", cartridge.name());
status.enqueue(t);
if(cartridge.info.patched) status.enqueue(translate["UPS patch applied."]);
if(cartridge.patched()) status.enqueue(translate["UPS patch applied."]);
//warn if unsupported hardware detected
string message;
message = translate["Warning: unsupported $ chip detected."];
if(cartridge.info.superfx) { replace(message, "$", "SuperFX"); status.enqueue(message); }
if(cartridge.info.sa1) { replace(message, "$", "SA-1"); status.enqueue(message); }
if(cartridge.info.st011) { replace(message, "$", "ST011"); status.enqueue(message); }
if(cartridge.info.st018) { replace(message, "$", "ST018"); status.enqueue(message); }
if(cartridge.has_superfx()) { message.replace("$", "SuperFX"); status.enqueue(message); }
if(cartridge.has_sa1()) { message.replace("$", "SA-1"); status.enqueue(message); }
if(cartridge.has_st011()) { message.replace("$", "ST011"); status.enqueue(message); }
if(cartridge.has_st018()) { message.replace("$", "ST018"); status.enqueue(message); }
message = translate["Warning: partially supported $ chip detected."];
if(cartridge.info.dsp3) { replace(message, "$", "DSP-3"); status.enqueue(message); }
if(cartridge.has_dsp3()) { message.replace("$", "DSP-3"); status.enqueue(message); }
} break;
case UnloadCart: {
@@ -180,7 +180,7 @@ void modify_system_state(system_state_t state) {
status.flush();
string t = translate["Unloaded $."];
replace(t, "$", cartridge.info.filename);
t.replace("$", cartridge.name());
status.enqueue(t);
} break;
@@ -368,16 +368,16 @@ bool load_cart(char *fn) {
lstring dir;
strcpy(fn, "");
strcpy(dir[0], snes.config.path.rom);
replace(dir[0], "\\", "/");
if(strlen(dir[0]) && !strend(dir[0], "/")) strcat(dir[0], "/");
dir[0] = snes.config.path.rom;
dir[0].replace("\\", "/");
if(dir[0].length() && !strend(dir[0], "/")) dir[0].append("/");
//append base path if rom path is relative
if(strbegin(dir[0], "./")) {
ltrim(dir[0], "./");
strcpy(dir[1], dir[0]);
strcpy(dir[0], snes.config.path.base);
strcat(dir[0], dir[1]);
dir[1].assign(dir[0]);
dir[0].assign(snes.config.path.base);
dir[0].append(dir[1]);
}
return hiro().file_open(0, fn,
@@ -404,15 +404,12 @@ void load_cart() {
}
void load_image(const char *filename) {
Cartridge::cartinfo_t cartinfo;
if(!cartridge.inspect_image(cartinfo, filename)) return;
switch(cartinfo.type) {
switch(cartridge.detect_image_type(filename)) {
case Cartridge::TypeNormal: {
load_cart_normal(filename);
} break;
case Cartridge::TypeBSC: {
case Cartridge::TypeBsxSlotted: {
window_bsxloader.mode = BSXLoaderWindow::ModeBSC;
window_bsxloader.set_text(translate["Load BS-X Slotted Cartridge"]);
window_bsxloader.tbase.set_text(filename);
@@ -421,7 +418,7 @@ void load_image(const char *filename) {
window_bsxloader.focus();
} break;
case Cartridge::TypeBSXBIOS: {
case Cartridge::TypeBsxBios: {
window_bsxloader.mode = BSXLoaderWindow::ModeBSX;
window_bsxloader.set_text(translate["Load BS-X Cartridge"]);
window_bsxloader.tbase.set_text(filename);
@@ -430,7 +427,7 @@ void load_image(const char *filename) {
window_bsxloader.focus();
} break;
case Cartridge::TypeBSX: {
case Cartridge::TypeBsx: {
window_bsxloader.mode = BSXLoaderWindow::ModeBSX;
window_bsxloader.set_text(translate["Load BS-X Cartridge"]);
window_bsxloader.tbase.set_text(snes.config.path.bsx);
@@ -439,7 +436,7 @@ void load_image(const char *filename) {
window_bsxloader.focus();
} break;
case Cartridge::TypeSufamiTurboBIOS: {
case Cartridge::TypeSufamiTurboBios: {
window_stloader.tbase.set_text(filename);
window_stloader.tslotA.set_text("");
window_stloader.tslotB.set_text("");
@@ -461,7 +458,7 @@ void load_cart_normal(const char *base) {
if(!base || !*base) return;
unload_cart();
cartridge.load_cart_normal(base);
cartridge.load_normal(base);
if(cartridge.loaded() == false) return;
modify_system_state(LoadCart);
}
@@ -470,7 +467,7 @@ void load_cart_bsc(const char *base, const char *slot) {
if(!base || !*base) return;
unload_cart();
cartridge.load_cart_bsc(base, slot);
cartridge.load_bsx_slotted(base, slot);
if(cartridge.loaded() == false) return;
modify_system_state(LoadCart);
}
@@ -479,7 +476,7 @@ void load_cart_bsx(const char *base, const char *slot) {
if(!base || !*base) return;
unload_cart();
cartridge.load_cart_bsx(base, slot);
cartridge.load_bsx(base, slot);
if(cartridge.loaded() == false) return;
modify_system_state(LoadCart);
}
@@ -488,7 +485,7 @@ void load_cart_st(const char *base, const char *slotA, const char *slotB) {
if(!base || !*base) return;
unload_cart();
cartridge.load_cart_st(base, slotA, slotB);
cartridge.load_sufami_turbo(base, slotA, slotB);
if(cartridge.loaded() == false) return;
modify_system_state(LoadCart);
}

View File

@@ -51,7 +51,7 @@ void SNESInterface::audio_sample(uint16 l_sample, uint16 r_sample) {
//input
void SNESInterface::input_poll() {
if(input_ready && input_ready() == false) {
if(window_main.input_ready() == false) {
input_manager.clear();
} else {
input_manager.poll();

12
src/ui_hiro/resource.cpp Normal file
View File

@@ -0,0 +1,12 @@
namespace resource {
static uint8_t *icon48;
static uint8_t *controller;
void init() {
//note: resources were removed, as hiro port is deprecated
icon48 = new(zeromemory) uint8_t[48 * 48 * 4];
controller = new(zeromemory) uint8_t[372 * 178 * 4];
}
} //namespace resource

View File

@@ -69,7 +69,6 @@ void AdvancedWindow::load() {
if(strbegin(name, "input.justifier")) continue;
if(strbegin(name, "input.gui")) continue;
if(strbegin(name, "input.debugger")) continue;
if(name == "misc.cheat_autosort") continue;
list.add_item(string()
<< name << "\t"

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