Compare commits

..

70 Commits
v077 ... v083

Author SHA1 Message Date
Tim Allen
7fa8ad755d Update to v083 release.
byuu says:

This release adds preliminary Nintendo / Famicom emulation. It's only
a week or two old, so a lot of work still needs to be done before it can
compete with the most popular NES emulators.

It's important to clarify: bsnes is primarily an SNES emulator. That
will always be its forte and my core focus. I have added Game Boy
support previously for Super Game Boy emulation, and I've added NES
support mostly for something fun to work on to break up the monotony of
working on one system for seven years now. Obviously, I'd like the
emulation to be accurate and highly compatible, but I simply cannot
afford to invest the same amount of time and money into any other
systems.

Still, either way the NES and GB emulation serve as fun side-diversions,
and allow for a unified emulator interface with all of bsnes' unique
features applied to all systems. My personal favorite feature is
mightymo's extended built-in cheat code database that now also includes
NES and Game Boy codes. And it even works in Super Game Boy mode now,
too!

I'm also not worried about speed at all: so long as NES/GB are faster
than SNES/compatibility, it's fine by me. Note that due to the NES audio
running at 1.78MHz, and Game Boy audio at 4MHz stereo, a more
sophisticated audio resampler was needed: Ryphecha (Mednafen author) has
graciously written a first-rate resampler: it is a band-limited
Kaiser-windowed polyphase sinc resampler. It is combined with two
highpass filters to remove DC bias. The filter itself is SSE optimized,
but even still, approximately 50% of CPU usage for NES/GB emulation goes
to the audio filtering alone. However, you now have the best sound
possible for NES and Game Boy emulation as a result.

The GUI has also been heavily re-structured to accommodate multiple
emulators from the same interface. As such, it's quite likely a few bugs
are still lurking here and there. Please report them and I'll iron them
out for the next release.

Changelog:
- license is now GPLv3
- re-structured GUI as a multi-system emulator
- added NES emulation [byuu, Ryphecha]
- added NES ICs: MMC1, MMC2, MMC3, MMC4, MMC5, VRC4, VRC6+audio, VRC7,
  Sunsoft-5B+audio, Bandai-LZ93D50
- added NES boards: AxROM, BNROM, CNROM, ExROM, FxROM, GxROM, NROM,
  PxROM, SxROM, TxROM, UxROM
- Game Boy emulation improvements [Jonas Quinn]
- SNES core outputs full 19-bit color (4-bit luma included) for more
  accurate color reproduction (~5% speed hit)
- audio resampler is now a band-limited polyphase resampler [Ryphecha]
- cheat database includes NES+GB codes as well [mightymo, tukuyomi]
- lots of other changes
2011-10-14 21:05:25 +11:00
Tim Allen
ef85f7ccb0 Update to v082r33 release.
byuu says:

Added MMC2, MMC4, VRC4, VRC7 (no audio.)
Split NES audio code up into individual modules.
Fixed libsnes to compile: Themaister, can you please test to make sure
it works? I don't have a libsnes client on my work PC to test it.
Added about / license information to bottom of advanced settings screen
for now (better than nothing, I guess.)
Blocked PPU reads/writes while rendering for now, easier than coming up
with a bus address locking thing :/

I can't seem to fix MMC5 graphics during the intro to Uchuu Keibitai.
Without that, trying to implement vertical-split screen mode doesn't
make sense.

So as far as special audio chips go ...
* VRC6 is completed
* Sunsoft 5B has everything the only game to use it uses, but there are
  more unused channels I'd like to support anyway (they aren't
  documented, though.)
* MMC5 audio unsupported for now
* VRC7 audio unsupported, probably for a long time (hardest audio driver
  of all. More complex than core NES APU.)
* audio PCM games (Moero Pro Yakyuu!) I probably won't ever support
  (they require external WAV packs.)
2011-10-12 23:03:58 +11:00
Tim Allen
b8d607d16b Update to v082r32 release.
byuu says:

Added delay to MMC1 register writes, to fix Bill & Ted's Godawful
Adventure.
Fixed up MMC5 RAM+fill mode, and added EXRAM mode support (8x8
tiles/attributes.)
Just Breed is fully playable now.

MMC5 is a total pain in the ass, the documentation on it is just
terrible. I basically just tried seven hundred variations until
something worked.
I still need to add MMC5 vertical split screen (for one single game's
attract screen, ugh), and the extra sound channels.
Would like to rework the NES APU first. Since the pulse channels are
identical sans sweep, it'd be nice to just inherit those and mask out
the sweep register bit writes.
So that probably won't make it into the first release, at least.

Still, overall I think it'll be an impressive showing of complex mappers
for a first release: MMC3, MMC5, VRC6 and 5B. The latter two with full
audio. The only other really, really hard bit is the VRC7 audio,
supposedly.
2011-10-08 18:34:16 +11:00
Tim Allen
4c47cc203f Update to v082r31 release.
byuu says:

Enable Overscan->Mask Overscan [best I'm doing]
Video settings -> Overscan mask: (horizontal, vertical: 0-16 on each
side) [only works on NES+SNES]
BPS patching works for NES+SNES+GB; note that long-term I want BPS to
only patch headerless PRG+CHR files, but we'll need a database
/ completed board mapping system first.
MMC1 splits the board/chip markups a bit better. My attempts to emulate
the extra CHR bits per hardware fail repeatedly. Docs do not explain how
it works at all.
Emulated enough of the MMC5 to play Castlevania 3.

The MMC5 is easily the most complicated mapper the NES has to offer, and
of course, has the most pitifully vague and difficult documentation of
any mapper around.
It seems the only way anyone is able to emulate this chip is
empirically.
Everyone else apparently hooks the MMC5 right into the PPU core, which
I of course cannot do. So I had to come up with my own (probably wrong)
way to synchronize the PPU simply by observing CHR bus accesses.

I must say, I over-estimated how well fleshed out the NES hardware
documentation was. Shit hits the fan right after MMC3.
It's miles beyond the GB scene, but I find myself wanting for someone
with the technical writing ability of anomie.
I can't find anything at all on how we're supposed to support the $2007
port reads/writes without it extra-clocking the PPU's bus, which could
throw off mapper timing.
Absolutely nothing at all on the subject anywhere, something everybody
is required to do for all cycle-based emulators and ... nada.

Anyway, I'd like to refine the MMC5 a bit, getting Just Breed playable
even without sound would be really nice (it's a fun game.)
Then we need to get libsnes building again (ugh, getting worn out in
backporting changes to it.)
Once v083 is public, we can start discussing a new API for multiple
emulators.
2011-10-06 20:53:16 +11:00
Tim Allen
4cbaf4e4ec Update to v082r30 release.
byuu says:

cheats.xml -> cheats.bml, includes NES+SNES+GB codes now. Absolutely
awesome, thanks to mightymo and tukuyomi.

I also added Sunsoft-FME7/5B (with sound) emulation. Really only useful
for playing the Japanese release of Gimmick!
Fun game, but balls to the wall hard.
2011-10-05 20:37:00 +11:00
Tim Allen
21f9fe4cd5 Update to v082r29 release.
byuu says:

I doubt anyone is going to like these changes, but oh well.

The base height output for NES+SNES is now always 256x240. The Enable
Overscan option blanks out borders around the screen. This eliminates
the need for an overscan software filter. For NES, it's 16px from the
top and bottom, and 8px from the left and right. Anything less and you
get scrolling artifacts in countless games. For the SNES, it's only 16px
from the top and bottom. Main point is that most NTSC SNES games are
224-height games, so you'll have black borders. Oh well, hack the source
if you want. Game Boy overscan option does nothing.

Everything except for the cheats.xml file now uses BML markup. I need to
write a converter for cheats.xml still. Cut the SNES board parsing code
in half, 30KB->16KB. Much cleaner now.
Took the opportunity to fix a mistake I made back with the XML spec: all
numbers are integers, but can be prefixed with 0x to become hexadecimal.
Before, offset/size values defaulted to hex-mode even without a prefix,
unlike frequency/etc values.

The XML shaders have gone in their own direction anyway, with most being
multi-pass and incompatible with bsnes. So that said, please don't
extend the BML functionality from your end. But f eel free to add to the
XML spec, since other emulators now use that as well. And don't
misunderstand, I love the work that's being done there. It's pretty
awesome to see multi-pass shader capabilities, and the RAM watching
stuff is just amazing.
If there are any really awesome single-pass shaders that people would
like, I can convert it from XML and include it with future releases.
On that topic, I removed the watercolor/hdr-tv ones from the binary
packages (still in the source archive) ... they are neat, but not very
useful for actual gaming.
If we had more than one, I'd remove the Direct3D sepia one. Not going to
use shaders from a certain bipolar manic, because I'd never hear the end
of it if I did :/

Oh, one change I think people will like: MSU1 no longer requires
a memory map specification, so MSU1 authors won't have to keep updating
to my newer revisions of board markups. Basically, if there's not
a board with an msu1 section, it'll check if "gamename.msu" exists. If
it does, MSU1 gets mapped to 00-3f,80-bf:2000-2007. If all you want is
music, make a blank, zero-byte gamename.msu file.
2011-10-04 22:55:39 +11:00
Tim Allen
b629a46779 Update to v082r28 release.
byuu says:

Was mostly working on BML. Still working on the spec.
Added NES-BNROM, NES-GNROM/NES-MHROM boards. I don't even know why. The
latter games do not work very well without Zapper support.
2011-10-02 21:05:45 +11:00
Tim Allen
ba2e6b5789 Update to v082r27 release.
byuu says:

Finished porting over all mappers to board/chip disambiguations. Had to
nearly rewrite the MMC1 code to do it, but all variants should be
supported.
iNES1 is too stupid to express them all, so you'll need a board markup
if you want to play the >8KB PRG RAM games.
For whatever reason, they call VRC6's memory WRAM, and MMC1's PRG RAM.
I am calling them all PRG RAM, since it's the same damn thing.
Board spec is not going to be stable until I have a hell of a lot more
mappers implemented, so be wary of that.
Anyway, at this time, all games can be loaded sans iNES header, if you
have the board markup. I'd also like to have a board database for all
commercial titles.
I'm treating *.fc as PRG-ROM(+CHR-ROM). Will work on loading the split
files later possibly.
2011-10-01 22:06:48 +10:00
Tim Allen
7115047d85 Update to v082r26 release.
byuu says:

.cht files now use BML-formatted data. I'm still going to request the
cheats.xml file as-is, and will write my own converter for embedding
during releases.
This is where parsing 2MB markup files in 10ms is really going to be
nice. Had to use an evil hack before for actually searching for games.

This has the start of the board/chip separation from mappers for NES,
and it has a barebones iNES->board markup converter.
You can specify your own board markup and bypass the need for an iNES
header, so in other words it will load No-Intro style games with
a proper board file.
Long-term, we'll have an internal database for commercial boards, and
probably folder.fc/prg.rom{,chr.rom} loading support.

Since they can't co-exist, the mappers are currently disabled, and I've
only ported the easy ones. So no MMC1/MMC3/VRC6 in this release. I need
to make them into chips first.
2011-10-01 21:16:57 +10:00
Tim Allen
e8b1af0917 Update to v082r25 release.
byuu says:

Ryphecha fixed Gun Nac, it was some sort of problem with blank sprite
address fetching messing with the MMC3
I've started on an XML parser for iNES-free loading, but it's pretty
barebones right now. Only NROM-256 loads, and you have to make it
a compile-time thing (so other games work for now.)
Updated nall with nullptr stuff.
nall/detect is now nall/intrinsic and has both #defines + static
constants that can be used to detect the platform (allows for run-time
platform checks where practical.)
ruby has a Makefile now, that makes using it in other projects a lot
easier
2011-09-29 22:08:22 +10:00
Tim Allen
1d4f778176 Update to v082r24 release.
byuu says:

Upgraded to GCC 4.6.1.
Removed nall/foreach and nall/concept, upgraded iterator support on all
of my containers, and replaced everything with range-for.
Fixed up Qt geometry a good bit, should at least create windows now without bouncing around.
Added some initial nullptr / constexpr changes.
Some other minor cleanups ... removing foreach() took about 6-8 hours
alone.
2011-09-27 21:55:02 +10:00
Tim Allen
875ed46d79 Update to v082r23 release.
byuu says:

Ryphecha fixed the FF1 glitch, added two highpass filters to NES audio
output (still working on a lowpass), and fixed VRC6 audio issues.
I reduced the complexity of all eight supported mapping modes, and
standardized them; and added in an overscan filter (not in archive) for
chopping off all the NES edge garbage (8 pixels on the left and right,
16 on the top and bottom.)
It's extreme, but anything less shows junk. I may make this part of the
menu option, just clip off more on NES mode than SNES mode.
2011-09-26 21:38:57 +10:00
Tim Allen
046e478d86 Update to v082r22 release.
byuu says:

Mappers are now optionally threaded.
Fixed up MMC3 emulation, SMB3 and MM3-6 are all fully playable. However,
many unusual variants of this chip are not supported still.
Added UNROM+UOROM for Contra and MM1, allowing all six MM games to play
now.
Added VRC6 with sound emulation, because I wanted to get audio mixing in
place.

Chose VRC6 because it has Esper Dream 2, which is an absolutely amazing
game that everyone should play :D
The game didn't use sawtooth, and I didn't test any other VRC6 games, so
hopefully that is emulated passably well enough.
2011-09-26 21:27:06 +10:00
Tim Allen
82a17ac0f5 Update to v082r21 release.
byuu says:

2-6% speed hit in SNES core for outputting 19-bit (rounded to 32-bit ...
sigh) video, so that luma non-linearity can eventually be emulated
properly.
Now using sinc audio resampler, massive speed hit of course to NES+GB
only, but it's required to get rid of aliasing (buzzing) present in
many, many games otherwise.
Fixed fast forward and none/blur select.
Finally fixed texture clearing for changing pixel shaders and video
filters.
Some realllly basic NES MMC3, extremely broken so don't bug me about it.
Other stuff, probably.
2011-09-24 19:51:08 +10:00
Tim Allen
979aa640af Update to v082r20 release.
byuu says:

NES now has save state support.
NES A/B buttons were indeed swapped, so that's fixed now.
nall/dsp now puts resamplers into separate classes, so that each can
have their own state information.
opengl.hpp uses GL_RGBA internal format and doesn't regenerate textures
on resize. No speedup, no fix to junk on resize, so I will be very
unhappy if this breaks things for anyone.
GLSL shaders use <fragment filter="nearest/point"> as you guys wanted.
ui-snes was removed.
2011-09-23 21:13:57 +10:00
Tim Allen
98ec338285 Update snesfilter to release 20110920.
This was released beside bsnes v082r19. byuu didn't mention it in the
v082r19 release notes, but in a previous post mentioned that a number of
filters stopped working when bsnes switched to using RGB555 for all its
internal data.
2011-09-22 10:03:11 +10:00
Tim Allen
5b4dcbfdfe Update to v082r19 release.
byuu says:

This will be the last release with the ui-snes folder (it's broken now
anyway.)
Re-added cheat code database searching + add window. It hashes
NES+SNES+GB images now and will look them up in the database.
Re-added filter support, all filters now output at RGB555. Stacking is
possible, but I don't currently allow it.
Removed mouse capture + test options from tools menu.
Removed smooth video output from settings menu.
There are now two built-in "shaders": "None" (point filtering) and
"Blur" (linear filtering).
OpenGL shaders can now use <shader language="GLSL" filter="point"> (or
"linear") to specify their filtering mode.
Individual emulator versions are gone, and names are hidden from the
GUI: you just see bsnes v082.19 now. A new release bumps all core
versions.

I cannot for the life of me get the video to clear properly when
toggling the shaders. Say you set pixellate2x filter, then turn on
curvature shader, then turn off the filter, you get junk at the bottom
right.
I have tried clearing and flipping the OpenGL surface 64x in a row ...
I don't know where the hell it's getting the data from. If anyone can
make a small patch to fix that, I'd greatly appreciate it.
2011-09-22 10:00:21 +10:00
Tim Allen
101c9507b1 Update to v082r18 release.
byuu says:

There we go, the GUI is nearly feature-complete once again.

All cores now output their native video format (NES={emphasis}{palette},
SNES=BGR555, GameBoy={ bright, normal, darker, darkest }), and are
transformed to RGB555 data that is passed to the video renderer.
The video renderer then uses its internal palette to apply
brightness/contrast/gamma/ramp adjustments and outputs in RGB888 color
space.
This does add in another rendering pass, unfortunately, but it's
a necessary one for universal support.
The plan is to adapt all filters to take RGB555 input, and output RGB555
data as well. By doing this, it will be possible to stack filters.
However, it's a bit complicated: I need to plan how the stacking should
occur (eg we never want to apply scanlines before HQ2x, etc.)
Added input frequency adjustments for all three systems. I can easily
get perfect video/audio sync on all three now, hooray.
Long-term, it seems like we only really need one, and we can do
a video/audio delta to get an adjusted value. But for now, this gets the
job done.
Added audio volume adjust. I left out the balance for now, since it's
obviously impossible to balance the NES' single channel audio (I can
duplicate the channel, and do twice the filtering work, but ... why?)
I replaced NTSC/PAL TV mode selection with an "Enable Overscan"
checkbox. On, you get 240 lines on NES+SNES. Off, you get 224 lines on
NES+SNES.
Also added aspect correction box back. I don't do that gross PAL
distortion shit anymore, sorry PAL people. I just scale up the
54/47*(240/224) aspect correction for overscan off mode.
All memory is loaded and saved now, for all three systems (hooray, now
you can actually play Zelda 1&2.)
Added all of the old bsnes hotkeys, with the exception of capture
screenshot. May add again later. May come up with something a bit
different for extra features.
Re-added the NSS DIP switch setting window. Since geometry is saved,
I didn't want to auto-hide rows, so now you'll see all eight possible
DIPs, and the ones not used are grayed out.
Ultimately, nobody will notice since we only have DIPs for ActRaiser
NSS, and nobody's probably even using the XML file for that anyway.
Whatever, it's nice to have anyway.
Took FitzRoy's advice and single-item combo boxes on the input selection
are disabled, so the user doesn't waste time checking them.
I wanted to leave text so that you know there's not a problem. Qt
disabled radio box items look almost exactly like enabled ones.
Fixed lots of issues in phoenix and extended it a bit. But I was still
having trouble with radio box grouping, so I said fuck it and made the
panels show/hide based instead of append/remove based.
That's all for stuff off the checklist, I did a bunch of other things
I don't recall.

So yeah, I'd say the GUI is 100% usable now. This is my opinion on how
multi-platform GUIs should be done =)

Oh, I figure I should mention, but the NES core is GPLv3, and all future
SNES+GB releases will be as well. It's a move against Microsoft's Metro
store.
2011-09-21 00:04:43 +10:00
Tim Allen
69ed35db99 Update to v082r17 release.
byuu says:

Adds BS-X/Slotted/SufamiTurbo/SGB cartridge loading. Calling it
Satellaview as I'm more partial to that at the moment.
FileBrowser now remembers your folders per filter type like before, and
will keep your place in the list if you don't switch away.
I wanted there to be ONE slot loader, so the loader will show a grayed
out secondary slot on non-ST loading, but it's more consistent to only
have one window instead of two for geometry placement.
Removed help menu. Will try and work it in somewhere unobtrusive later
on I suppose.
Added timed messages and the usual "no cart loaded / paused" messages
and such.
2011-09-19 22:34:18 +10:00
Tim Allen
5c2d16828c Update to v082r16 release.
byuu says:

Binary output is once again called bsnes. No versioning on the title
without a system cartridge loaded. Still saving config files to
.config/batch for now.
Finally fixed NES APU frame IRQ clearing on $4015 reads.
Added mouse button/axis binding through buttons on the input capture
window.
Added advanced settings window with driver selection and focus policy
settings. Will show your default driver properly if none are selected
now, unlike old bsnes.
That exposed a small bug where phoenix isn't removing widgets on
Layout::remove, worked around it for now by hiding the panels. Damn,
sick of working on phoenix.
Added all missing input controllers, which can all now be mapped, and
bound them to the main menu, and added NES support for selecting "no
connected controller."
Added mouse capture and the requisite tools menu option for it.
Added WindowManager class that keeps track of both position and size now
(eg full geometry), so now you can resize your windows and save the
settings, unlike old bsnes.
WindowManager has more stringent geometry checks. The *client area* (not
the window border) can't be below 0,0 or above the width/height of three
30" monitors. If you have 4+ 30" monitors, then fuck you :P
settings.cfg is now also saved, captures all currently available
settings. Right now, there's only one path for the file browser to
remember. I will probably make this per-system later.
FileBrowser has been made a bit more friendly. The bottom left tells you
what type of files the list is filtered by (so you see "*.sfc" for
SNES), and the bottom right has an open button that can enter folders or
load files.
Added video shader support.
Fixed nall/dsp variadic-channel support, was only outputting the left
channel.
2011-09-19 22:25:56 +10:00
Tim Allen
382ae1e61e Update to v082r15 release.
byuu says:

7.5 hours of power coding. Das Keyboard definitely helped (but didn't
eliminate) RSI, neato.

Okay, the NES resampler was using 315 / 88.8 by mistake, so the output
rate was wrong, causing way more video/audio stuttering than necessary.
STILL forgot the NES APU frame IRQ clear thing on $4015 reads, blah. Why
do I always remember things right after uploading the WIPs?
Recreated the input manager with a new design, works much nicer than the
old one, a whole lot less duplicated code.
Recreated the input settings window to work with the new multi-system
emulation.
All input settings are saved to their own configuration file, input.cfg.
Going to batch folder for now.

Okay, so the new input settings window ... basically there are now three
drop-downs, and I'm not even trying to label them anymore.
They are primary, secondary, tertiary selectors for the listed group
below. Examples:
"NES -> Controller Port 1 -> Gamepad"
"SNES -> Controller Port 2 -> Super Scope"
"User Interface -> Hotkeys -> Save States"

I am aware that "Clear" gets disabled when assigning. I will work on
that later, being lazy for now and disabling the entire window. Have to
add the mouse binders back, too.
Escape and modifiers are both mappable as individual keys now. If you
want to clear, click the damn clear button :P

Oh, and all input goes to all windows for now. That'll be fixed too when
input focus stuff is re-added.
2011-09-17 16:42:17 +10:00
Tim Allen
7619805266 Update to v082r14 release.
byuu says:

Emulates DMC channel (sound effect when Link gets hit in Zelda 1, for
instance), fixes up bugs in rectangle/sweep and triangle channels, adds
DMC/frame APU IRQs, adds proper stalling for DMC ROM reads (should even
be cycle accurate, but has one extra cycle when triggered during OAM
DMA, I think), but forgets the frame IRQ acknowledge clear on $4015 read
(will fix next WIP.) All sound courtesy of Ryphecha.

Made template configuration settings window (empty for now.) Simplified
SNES cheat.cpp code. Some other stuff.

Further developed RSI.
2011-09-16 21:44:07 +10:00
Tim Allen
e3c7bbfb63 Update to v082r13 release.
byuu says:

I've updated the {System}::Interface classes to encapsulate all common
functionality, so they are C++ equivalents of libsnes now.
The idea being, use the interface class and you'll never have to reach
into core objects (unless you really want to.)
Not guaranteeing as stable an API as I do with libsnes for that, though.
C++ doesn't make for nice dynamic libraries, anyway.

Added back the state manager, and it now works for both SNES and Game
Boy. NES save states aren't in yet.
Anyway, this should give you a pretty good feel for what the combined UI
is going to be like: same as before, everything works the same. Only
difference is the dynamic system menu and cartridge menu with more load
options. The settings window will be mostly the same as well, but will
obviously have options that only apply to some systems.
2011-09-16 21:30:45 +10:00
Tim Allen
5f099b8ad0 Update to v082r12 release.
byuu says:

Merged Ryphecha's APU emulation, so NES has sound output now. We are
still missing the DMC memory fetch, so there will be missing sound
effects here and there.
2011-09-15 22:33:26 +10:00
Tim Allen
cb3460a673 Update to v082r11 release.
byuu says:

Emulates grayscale and color emphasis modes, improves sprite timing and
PPU bus fetching behavior with the PPU disabled.
2011-09-15 22:27:34 +10:00
Tim Allen
278cf8462c Update to v082r10 release.
byuu says:

Emulated the Game Genie for the NES and Game Boy, and wrote a new cheat
editor that doesn't reach into specific emulation cores so that it would
work.
Before you ask: yes, long-term I'd like Super Game Boy mode to accept
Game Boy codes. But that's not high on the priority list.
Renamed the mappers toward board names, LZ...->BandaiFCG,
LS161...->AOROM, added CNROM emulation (Adventure Island, Gradius, etc.)
Added the tools menu load/save state stuff, but note that the NES
doesn't have save state support yet (waiting for the interface to
stabilize a bit more first.)

Note: this will be the last release to have the ui-gameboy folder, it's
been deleted locally from my end, as the new multi-GUI does all that it
does and more now.
2011-09-15 22:23:13 +10:00
Tim Allen
7f4381b505 Update to v082r09 release.
byuu says:

Skip this build if you can, it's CPU+PPU timing improvements that should
not visibly affect anything.
2011-09-15 22:14:07 +10:00
Tim Allen
c668d10ac7 Update to v082r08 release.
byuu says:

Fixed up the PPU to be as close to cycle-perfect as possible. Fixed RMW
to write twice instead of read twice. Ryphecha added AOROM and fixed up
MMC1. Have CNROM too, but I need to rethink the mapper/board
distinction. Apparently the same logic IC is used in both AOROM and
CNROM, and it's just a matter of routing the pins to it. I need to
consider how crazy it'd be to emulate the logic IC and have boards
simply reroute pins to it. If it's too much work, we'll just treat
mappers as board + logic IC combinations. We'll see.
2011-09-12 20:30:44 +10:00
Tim Allen
7fc78dae07 Update to v082r07 release.
byuu says:

Wrote a cycle-based PPU renderer, ~95% hardware accurate for BGs, not so
much for sprites yet. Mednafen has been helping out a lot.
2011-09-12 20:17:12 +10:00
Tim Allen
4ca051a22f Update to v082r06 release.
byuu says:

Emulated MMC1, currently defaults to B2 configuration. Fixed a whole
bunch of timing things, render things, nametable mirroring things, etc.
Zelda and Mega Man II are fully playable, but they have odd vertical
scrolling issues that make it a not so fun experience.
Not sure what the problem is there, yet. The Y scroll register writes
seem to be wonky ... I don't know.

Keeping the Cartridge menu always visible now, so it's faster to load
carts, but I am still hiding the non-loaded system menus.
2011-09-12 19:44:22 +10:00
Tim Allen
8618334356 Update to v082r05 release.
byuu says:

Okay, I fixed up many outstanding phoenix issues.
* Windows/GTK+ fixed by using processEvents instead of main(); Windows can run unthrottled, and GTK+ shows the window contents now
* fixed keyboard beeping once and for all on Windows: I now whitelist tabbable controls
* fixed main menubar setVisible calls
* Qt and GTK+ now allow you to resize windows smaller than they initially were

Both Qt and GTK+ still fuck up the geometry a bit when toggling fullscreen mode. I have tried, and tried, and tried and tried and tried to fix it all. Nothing works. I give up.
Easier to destroy and recreate the fucking window than figure out how to resize it on Linux (and no, I can't do that. ruby would not like the handle changing.)

As for the GUI:
* file browser is back in, still need remember place and open folder code; that needs to be extended to handle multiple systems now
* shrink window command added to tools menu.
2011-09-09 14:16:25 +10:00
Tim Allen
ec7e4087fb Update to v082r04 release.
byuu says:

So, here's the deal. I now have three emulators. I don't think the
NES/GB ones are at all useful, but I do want them to be eventually. And
having them have those pathetic little GUIs like ui-gameboy, and keeping
everything in separate project folders, just doesn't work well for me.
I kind of "got around" the issue with the Game Boy, by only allowing SGB
mode emulation. But there is no "Super Nintendo" ... er ... wait ...
uhmm ... well, you know what I mean anyway.

So, my idea is to write a multi-emulator GUI, and keep the projects
together. The GUI is not going to change much. The way I envision this
working:

At startup, you have a menubar with: "Cartridge, Settings, Tools, Help".
Cartridge has "Load NES Cartridge", "Load SNES Cartridge", etc.
When you load something, Cartridge is replaced with the appropriate
system menu, eg "SNES". Here you have all your regular items: "power,
reset, controller port selection, etc." There is also a new "Unload
Cartridge" option, which is how you restore the "Cartridge" menu again.
I have no plans to emulate any other systems, but if I ever do emulate
something that doesn't take cartridges, I'll change the name to just
"Load" or something.

The cheat editor / state manager will look and act exactly the same. The
settings panel will look exactly the same. I'll simply show/hide
system-specific options as needed, like NES/SNES aspect ratio
correction, etc. The input mapping window will just have settings for
the currently loaded system. Video and audio tweaking will apply
cross-system, as will hotkey mapping.

The GUI stuff is mostly copy-paste, so it should only take me a week to
get it 95% back to where it was, so don't worry, this isn't total GUI
rewrite #80.
I am, however, making all the objects pointers, so that I can destruct
them all prior to main() returning, which is certainly one way of fixing
that annoying Windows/Qt crash.

Please only test on Linux. The Windows port is broken to hell, and will
give you a bad impression of the idea:
- menu groups are not hiding for some reason (all groups are showing, it
  looks hideous)
- Timer interval(0) is taking 16ms per call, capping the FPS to ~64 tops
  [FWIW, bsnes/accuracy gets 130fps, bgameboy gets 450fps, bnes gets
  800fps; all run at lowest possible granularity]
- the OS keeps beeping when you press keys (AGAIN)

Of course, Qt and GTK+ don't let you shrink a window from the requested
geometry size, because they suck. So the video scaling stuff doesn't
work all that great yet.
Man, a metric fuckton of things need to be fixed in phoenix, and
I really don't know how to fix any of them :/
2011-09-09 14:08:38 +10:00
Tim Allen
496708cffe Update to v082r03 release.
byuu says:

Couple more fixes to audio from Jonas, and I converted all types from
"unsigned" to the smallest sizes possible, which simplified a bit of the
code. Love variable-length integers.

Audio core should be really good now. Not perfect, but pretty close for
99% of games. Also fixed the window stuff for Cool World and such.
2011-09-05 13:56:22 +10:00
Tim Allen
a86c5ee59d Update to v082r02 release.
byuu says:

Has Jonas Quinn's many Game Boy APU fixes, and two more from blargg's
notes that I added.

It also has the new dynamic phoenix. So yeah, it'll crash on bsnes/Qt
exit. If anyone can fix it *properly* and wants the money, I'll pay them
$20 for the trouble =)
2011-09-05 13:48:23 +10:00
Tim Allen
d8f9204e18 Update to v082r01 release.
byuu says:

Changelog:
- if config file window coordinates are >= 30000, it snaps them back to
  128,128; should end the "why aren't windows visible?" posts
- updated GUI code to match new phoenix changes
- phoenix: Layout and Widget inherit from Sizable; directional layouts
  make no distinction between widgets and layouts
- phoenix: individual widgets / layout can maintain visible/hidden
  status in spite of their parents' visibility
2011-08-22 21:27:04 +10:00
Tim Allen
e8775319c8 Update to v082 release.
byuu says:

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

Changelog (since v081):

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

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

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

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

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

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

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

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

phoenix/Windows:

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

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

phoenix/Qt:

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

phoenix/All:

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

byuu says, announcing the v080r08 release:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[...]

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

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

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

Save state support is of course there already.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

I have enough written now that I can run Zelda 3 at least (although it
gets stuck in a loop immediately after.) No real point in comparing
speed yet, because it'll definitely go down as it becomes more complete.
2011-05-02 23:53:16 +10:00
Tim Allen
0a3d6e4c53 Update to v078 release.
byuu says:

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

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

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

Changelog (since v077):

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

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

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

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

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

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

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

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

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

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

View File

@@ -1,11 +1,13 @@
include nall/Makefile
nes := nes
snes := snes
gameboy := gameboy
profile := accuracy
ui := ui
# debugger
options :=
# options += console
# options += debugger
# compiler
c := $(compiler) -std=gnu99
@@ -14,28 +16,33 @@ flags := -O3 -fomit-frame-pointer -I.
link :=
objects := libco
# profile-guided instrumentation
# flags += -fprofile-generate
# link += -lgcov
# profile-guided optimization mode
# pgo := instrument
# pgo := optimize
# profile-guided optimization
# flags += -fprofile-use
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
ifeq ($(pgo),instrument)
flags += -fprofile-generate
link += -lgcov
else ifeq ($(pgo),optimize)
flags += -fprofile-use
endif
# platform
ifeq ($(platform),x)
# tree vectorization causes code generation errors with Linux/GCC 4.6.1
flags += -fno-tree-vectorize
link += -s -ldl -lX11 -lXext
else ifeq ($(platform),osx)
else ifeq ($(platform),win)
link += -mwindows
# link += -mconsole
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
else
unknown_platform: help;
endif
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
# implicit rules
compile = \
$(strip \
@@ -70,6 +77,6 @@ clean:
-@$(call delete,*.manifest)
archive-all:
tar -cjf bsnes.tar.bz2 data gameboy libco nall obj out phoenix ruby snes ui ui-gameboy ui-libsnes Makefile cc.bat clean.bat sync.sh
tar -cjf bsnes.tar.bz2 data gameboy libco nall nes obj out phoenix ruby snes ui ui-libsnes Makefile cc.bat clean.bat sync.sh
help:;

79635
bsnes/data/cheats.bml Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,10 @@
gameboy_objects := gameboy-system gameboy-scheduler
gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler
gameboy_objects += gameboy-memory gameboy-cartridge
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
gameboy_objects += gameboy-cheat
objects += $(gameboy_objects)
obj/gameboy-interface.o: $(gameboy)/interface/interface.cpp $(call rwildcard,$(gameboy)/interface/)
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
@@ -10,3 +12,4 @@ obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/m
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)
obj/gameboy-cheat.o: $(gameboy)/cheat/cheat.cpp $(call rwildcard,$(gameboy)/cheat/)

View File

@@ -46,7 +46,7 @@ void APU::main() {
noise.run();
master.run();
system.interface->audio_sample(master.center, master.left, master.right);
interface->audioSample(master.center, master.left, master.right);
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
@@ -55,7 +55,7 @@ void APU::power() {
create(Main, 4194304);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
foreach(n, mmio_data) n = 0x00;
for(auto &n : mmio_data) n = 0x00;
sequencer_base = 0;
sequencer_step = 0;
@@ -80,10 +80,10 @@ uint8 APU::mmio_read(uint16 addr) {
if(addr == 0xff26) {
uint8 data = master.enable << 7;
if(square1.counter && square1.length) data |= 0x01;
if(square2.counter && square2.length) data |= 0x02;
if( wave.counter && wave.length) data |= 0x04;
if( noise.counter && noise.length) data |= 0x08;
if(square1.enable) data |= 0x01;
if(square2.enable) data |= 0x02;
if( wave.enable) data |= 0x04;
if( noise.enable) data |= 0x08;
return data | table[addr - 0xff10];
}

View File

@@ -1,6 +1,11 @@
#ifdef APU_CPP
void APU::Master::run() {
static int16_t volume[] = {
-16384, -14336, -12288, -10240, -8192, -6144, -4096, -2048,
+2048, +4096, +6144, +8192, +10240, +12288, +14336, +16384,
};
if(enable == false) {
center = 0;
left = 0;
@@ -14,13 +19,7 @@ void APU::Master::run() {
sample += apu.wave.output;
sample += apu.noise.output;
sample >>= 2;
center = sclamp<16>(sample);
if(left_enable == false && right_enable == false) {
left = center;
right = center;
return;
}
center = volume[sample];
sample = 0;
channels = 0;
@@ -29,7 +28,7 @@ void APU::Master::run() {
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
left = sclamp<16>(sample);
left = volume[sample];
switch(left_volume) {
case 0: left >>= 3; break; // 12.5%
@@ -41,7 +40,6 @@ void APU::Master::run() {
case 6: left -= (left >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(left_enable == false) left = 0;
sample = 0;
channels = 0;
@@ -50,7 +48,7 @@ void APU::Master::run() {
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
right = sclamp<16>(sample);
right = volume[sample];
switch(right_volume) {
case 0: right >>= 3; break; // 12.5%
@@ -62,18 +60,17 @@ void APU::Master::run() {
case 6: right -= (right >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(right_enable == false) right = 0;
}
void APU::Master::write(unsigned r, uint8 data) {
if(r == 0) {
left_enable = data & 0x80;
left_volume = (data >> 4) & 7;
right_enable = data & 0x08;
right_volume = (data >> 0) & 7;
if(r == 0) { //$ff24 NR50
left_in_enable = data & 0x80;
left_volume = (data >> 4) & 7;
right_in_enable = data & 0x08;
right_volume = (data >> 0) & 7;
}
if(r == 1) {
if(r == 1) { //$ff25 NR51
channel4_left_enable = data & 0x80;
channel3_left_enable = data & 0x40;
channel2_left_enable = data & 0x20;
@@ -84,15 +81,15 @@ void APU::Master::write(unsigned r, uint8 data) {
channel1_right_enable = data & 0x01;
}
if(r == 2) {
if(r == 2) { //$ff26 NR52
enable = data & 0x80;
}
}
void APU::Master::power() {
left_enable = 0;
left_in_enable = 0;
left_volume = 0;
right_enable = 0;
right_in_enable = 0;
right_volume = 0;
channel4_left_enable = 0;
channel3_left_enable = 0;
@@ -110,9 +107,9 @@ void APU::Master::power() {
}
void APU::Master::serialize(serializer &s) {
s.integer(left_enable);
s.integer(left_in_enable);
s.integer(left_volume);
s.integer(right_enable);
s.integer(right_in_enable);
s.integer(right_volume);
s.integer(channel4_left_enable);
s.integer(channel3_left_enable);

View File

@@ -1,8 +1,8 @@
struct Master {
bool left_enable;
unsigned left_volume;
bool right_enable;
unsigned right_volume;
bool left_in_enable;
uint3 left_volume;
bool right_in_enable;
uint3 right_volume;
bool channel4_left_enable;
bool channel3_left_enable;
bool channel2_left_enable;

View File

@@ -1,27 +1,32 @@
#ifdef APU_CPP
bool APU::Noise::dac_enable() {
return (envelope_volume || envelope_direction);
}
void APU::Noise::run() {
if(period && --period == 0) {
period = divisor << frequency;
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << 14);
if(narrow_lfsr) lfsr |= (bit << 6);
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
}
}
uint4 sample = (lfsr & 1) ? 0 : volume;
if(counter && length == 0) sample = 0;
uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
if(enable == false) sample = 0;
output = (sample * 4369) - 32768;
output = sample;
}
void APU::Noise::clock_length() {
if(counter && length) length--;
if(counter && length) {
if(--length == 0) enable = false;
}
}
void APU::Noise::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
if(enable && envelope_frequency && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
@@ -29,42 +34,42 @@ void APU::Noise::clock_envelope() {
}
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) {
initial_length = 64 - (data & 0x3f);
length = initial_length;
if(r == 1) { //$ff20 NR41
length = 64 - (data & 0x3f);
}
if(r == 2) {
if(r == 2) { //$ff21 NR42
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false;
}
if(r == 3) {
if(r == 3) { //$ff22 NR43
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
}
if(r == 4) {
if(r == 4) { //$ff34 NR44
bool initialize = data & 0x80;
counter = data & 0x40;
if(initialize) {
enable = dac_enable();
lfsr = ~0U;
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
if(length == 0) length = 64;
}
}
}
void APU::Noise::power() {
initial_length = 0;
enable = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
@@ -82,7 +87,8 @@ void APU::Noise::power() {
}
void APU::Noise::serialize(serializer &s) {
s.integer(initial_length);
s.integer(enable);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);

View File

@@ -1,20 +1,23 @@
struct Noise {
unsigned initial_length;
unsigned envelope_volume;
bool enable;
uint4 envelope_volume;
bool envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
uint3 envelope_frequency;
uint4 frequency;
bool narrow_lfsr;
unsigned divisor;
bool counter;
int16 output;
unsigned length;
unsigned envelope_period;
unsigned volume;
uint3 envelope_period;
uint4 volume;
unsigned period;
uint15 lfsr;
bool dac_enable();
void run();
void clock_length();
void clock_envelope();

View File

@@ -1,9 +1,13 @@
#ifdef APU_CPP
bool APU::Square1::dac_enable() {
return (envelope_volume || envelope_direction);
}
void APU::Square1::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
phase++;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
@@ -12,45 +16,44 @@ void APU::Square1::run() {
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
uint4 sample = (duty_output ? volume : (uint4)0);
if(enable == false) sample = 0;
output = (sample * 4369) - 32768;
output = sample;
}
void APU::Square1::sweep() {
if(enable == false) return;
void APU::Square1::sweep(bool update) {
if(sweep_enable == false) return;
signed offset = frequency_shadow >> sweep_shift;
if(sweep_direction) offset = -offset;
frequency_shadow += offset;
sweep_negate = sweep_direction;
unsigned delta = frequency_shadow >> sweep_shift;
signed freq = frequency_shadow + (sweep_negate ? -delta : delta);
if(frequency_shadow < 0) {
frequency_shadow = 0;
} else if(frequency_shadow > 2047) {
frequency_shadow = 2048;
if(freq > 2047) {
enable = false;
}
if(frequency_shadow <= 2047 && sweep_shift) {
frequency = frequency_shadow;
} else if(sweep_shift && update) {
frequency_shadow = freq;
frequency = freq & 2047;
period = 4 * (2048 - frequency);
}
}
void APU::Square1::clock_length() {
if(counter && length) length--;
if(counter && length) {
if(--length == 0) enable = false;
}
}
void APU::Square1::clock_sweep() {
if(sweep_frequency && sweep_period && --sweep_period == 0) {
if(enable && sweep_frequency && --sweep_period == 0) {
sweep_period = sweep_frequency;
sweep();
sweep(1);
sweep(0);
}
}
void APU::Square1::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
if(enable && envelope_frequency && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
@@ -58,39 +61,44 @@ void APU::Square1::clock_envelope() {
}
void APU::Square1::write(unsigned r, uint8 data) {
if(r == 0) {
if(r == 0) { //$ff10 NR10
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
sweep_frequency = (data >> 4) & 7;
sweep_direction = data & 0x08;
sweep_shift = data & 0x07;
}
if(r == 1) {
if(r == 1) { //$ff11 NR11
duty = data >> 6;
length = data & 0x3f;
length = 64 - (data & 0x3f);
}
if(r == 2) {
if(r == 2) { //$ff12 NR12
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false;
}
if(r == 3) {
if(r == 3) { //$ff13 NR13
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
if(r == 4) { //$ff14 NR14
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
enable = dac_enable();
envelope_period = envelope_frequency;
volume = envelope_volume;
frequency_shadow = frequency;
sweep_period = sweep_frequency;
enable = sweep_period || sweep_shift;
if(sweep_shift) sweep();
sweep_enable = sweep_period || sweep_shift;
sweep_negate = false;
if(sweep_shift) sweep(0);
if(length == 0) length = 64;
}
}
@@ -98,9 +106,12 @@ void APU::Square1::write(unsigned r, uint8 data) {
}
void APU::Square1::power() {
enable = 0;
sweep_frequency = 0;
sweep_direction = 0;
sweep_shift = 0;
sweep_negate = 0;
duty = 0;
length = 0;
envelope_volume = 0;
@@ -116,14 +127,17 @@ void APU::Square1::power() {
envelope_period = 0;
sweep_period = 0;
frequency_shadow = 0;
enable = 0;
sweep_enable = 0;
volume = 0;
}
void APU::Square1::serialize(serializer &s) {
s.integer(enable);
s.integer(sweep_frequency);
s.integer(sweep_direction);
s.integer(sweep_shift);
s.integer(sweep_negate);
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
@@ -139,7 +153,7 @@ void APU::Square1::serialize(serializer &s) {
s.integer(envelope_period);
s.integer(sweep_period);
s.integer(frequency_shadow);
s.integer(enable);
s.integer(sweep_enable);
s.integer(volume);
}

View File

@@ -1,27 +1,32 @@
struct Square1 {
unsigned sweep_frequency;
unsigned sweep_direction;
unsigned sweep_shift;
unsigned duty;
bool enable;
uint3 sweep_frequency;
bool sweep_direction;
uint3 sweep_shift;
bool sweep_negate;
uint2 duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
uint4 envelope_volume;
bool envelope_direction;
uint3 envelope_frequency;
uint11 frequency;
bool counter;
int16 output;
bool duty_output;
unsigned phase;
uint3 phase;
unsigned period;
unsigned envelope_period;
unsigned sweep_period;
uint3 envelope_period;
uint3 sweep_period;
signed frequency_shadow;
bool enable;
unsigned volume;
bool sweep_enable;
uint4 volume;
bool dac_enable();
void run();
void sweep();
void sweep(bool update);
void clock_length();
void clock_sweep();
void clock_envelope();

View File

@@ -1,9 +1,13 @@
#ifdef APU_CPP
bool APU::Square2::dac_enable() {
return (envelope_volume || envelope_direction);
}
void APU::Square2::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
phase++;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
@@ -12,18 +16,20 @@ void APU::Square2::run() {
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
uint4 sample = (duty_output ? volume : (uint4)0);
if(enable == false) sample = 0;
output = (sample * 4369) - 32768;
output = sample;
}
void APU::Square2::clock_length() {
if(counter && length) length--;
if(counter && length) {
if(--length == 0) enable = false;
}
}
void APU::Square2::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
if(enable && envelope_frequency && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
@@ -31,29 +37,32 @@ void APU::Square2::clock_envelope() {
}
void APU::Square2::write(unsigned r, uint8 data) {
if(r == 1) {
if(r == 1) { //$ff16 NR21
duty = data >> 6;
length = data & 0x3f;
length = 64 - (data & 0x3f);
}
if(r == 2) {
if(r == 2) { //$ff17 NR22
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false;
}
if(r == 3) {
if(r == 3) { //$ff18 NR23
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
if(r == 4) { //$ff19 NR24
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
enable = dac_enable();
envelope_period = envelope_frequency;
volume = envelope_volume;
if(length == 0) length = 64;
}
}
@@ -61,6 +70,8 @@ void APU::Square2::write(unsigned r, uint8 data) {
}
void APU::Square2::power() {
enable = 0;
duty = 0;
length = 0;
envelope_volume = 0;
@@ -78,6 +89,8 @@ void APU::Square2::power() {
}
void APU::Square2::serialize(serializer &s) {
s.integer(enable);
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);

View File

@@ -1,18 +1,22 @@
struct Square2 {
unsigned duty;
bool enable;
uint2 duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
uint4 envelope_volume;
bool envelope_direction;
uint3 envelope_frequency;
uint11 frequency;
bool counter;
int16 output;
bool duty_output;
unsigned phase;
uint3 phase;
unsigned period;
unsigned envelope_period;
unsigned volume;
uint3 envelope_period;
uint4 volume;
bool dac_enable();
void run();
void clock_length();

View File

@@ -3,57 +3,53 @@
void APU::Wave::run() {
if(period && --period == 0) {
period = 2 * (2048 - frequency);
pattern_offset = (pattern_offset + 1) & 31;
pattern_sample = pattern[pattern_offset];
pattern_sample = pattern[++pattern_offset];
}
uint4 sample = pattern_sample;
if(counter && length == 0) sample = 0;
uint4 sample = pattern_sample >> volume_shift;
if(enable == false) sample = 0;
output = (sample * 4369) - 32768;
output >>= volume;
output = sample;
}
void APU::Wave::clock_length() {
if(counter && length) length--;
if(counter && length) {
if(--length == 0) enable = false;
}
}
void APU::Wave::write(unsigned r, uint8 data) {
if(r == 0) {
if(r == 0) { //$ff1a NR30
dac_enable = data & 0x80;
if(dac_enable == false) enable = false;
}
if(r == 1) {
initial_length = 256 - data;
length = initial_length;
if(r == 1) { //$ff1b NR31
length = 256 - data;
}
if(r == 2) {
if(r == 2) { //$ff1c NR32
switch((data >> 5) & 3) {
case 0: volume = 16; break; // 0%
case 1: volume = 0; break; //100%
case 2: volume = 1; break; // 50%
case 3: volume = 2; break; // 25%
case 0: volume_shift = 4; break; // 0%
case 1: volume_shift = 0; break; //100%
case 2: volume_shift = 1; break; // 50%
case 3: volume_shift = 2; break; // 25%
}
}
if(r == 3) {
if(r == 3) { //$ff1d NR33
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
if(r == 4) { //$ff1e NR34
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize && dac_enable) {
enable = true;
if(initialize) {
enable = dac_enable;
pattern_offset = 0;
length = initial_length;
if(length == 0) length = 256;
}
}
@@ -67,17 +63,17 @@ void APU::Wave::write_pattern(unsigned p, uint8 data) {
}
void APU::Wave::power() {
enable = 0;
dac_enable = 0;
initial_length = 0;
volume = 0;
volume_shift = 0;
frequency = 0;
counter = 0;
random_cyclic r;
foreach(n, pattern) n = r() & 15;
random_lfsr r;
for(auto &n : pattern) n = r() & 15;
output = 0;
enable = 0;
length = 0;
period = 0;
pattern_offset = 0;
@@ -85,15 +81,15 @@ void APU::Wave::power() {
}
void APU::Wave::serialize(serializer &s) {
s.integer(enable);
s.integer(dac_enable);
s.integer(initial_length);
s.integer(volume);
s.integer(volume_shift);
s.integer(frequency);
s.integer(counter);
s.array(pattern);
s.integer(output);
s.integer(enable);
s.integer(length);
s.integer(period);
s.integer(pattern_offset);

View File

@@ -1,17 +1,17 @@
struct Wave {
bool enable;
bool dac_enable;
unsigned initial_length;
unsigned volume;
unsigned frequency;
unsigned volume_shift;
uint11 frequency;
bool counter;
uint8 pattern[32];
int16 output;
bool enable;
unsigned length;
unsigned period;
unsigned pattern_offset;
unsigned pattern_sample;
uint5 pattern_offset;
uint4 pattern_sample;
void run();
void clock_length();

View File

@@ -16,7 +16,7 @@ namespace GameBoy {
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
if(size == 0) size = 32768;
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
@@ -33,42 +33,24 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
info.romsize = 0;
info.ramsize = 0;
xml_element document = xml_parse(xml);
foreach(head, document.element) {
if(head.name == "cartridge") {
foreach(attr, head.attribute) {
if(attr.name == "mapper") {
if(attr.content == "none") info.mapper = Mapper::MBC0;
if(attr.content == "MBC1") info.mapper = Mapper::MBC1;
if(attr.content == "MBC2") info.mapper = Mapper::MBC2;
if(attr.content == "MBC3") info.mapper = Mapper::MBC3;
if(attr.content == "MBC5") info.mapper = Mapper::MBC5;
if(attr.content == "MMM01") info.mapper = Mapper::MMM01;
if(attr.content == "HuC1") info.mapper = Mapper::HuC1;
if(attr.content == "HuC3") info.mapper = Mapper::HuC3;
}
BML::Document document(markup);
if(attr.name == "rtc") info.rtc = (attr.content == "true" ? true : false);
if(attr.name == "rumble") info.rumble = (attr.content == "true" ? true : false);
}
auto &mapperid = document["cartridge"]["mapper"].value;
if(mapperid == "none" ) info.mapper = Mapper::MBC0;
if(mapperid == "MBC1" ) info.mapper = Mapper::MBC1;
if(mapperid == "MBC2" ) info.mapper = Mapper::MBC2;
if(mapperid == "MBC3" ) info.mapper = Mapper::MBC3;
if(mapperid == "MBC5" ) info.mapper = Mapper::MBC5;
if(mapperid == "MMM01") info.mapper = Mapper::MMM01;
if(mapperid == "HuC1" ) info.mapper = Mapper::HuC1;
if(mapperid == "HuC3" ) info.mapper = Mapper::HuC3;
foreach(elem, head.element) {
if(elem.name == "rom") {
foreach(attr, elem.attribute) {
if(attr.name == "size") info.romsize = hex(attr.content);
}
}
info.rtc = document["cartridge"]["rtc"].exists();
info.rumble = document["cartridge"]["rumble"].exists();
if(elem.name == "ram") {
info.ram = true;
foreach(attr, elem.attribute) {
if(attr.name == "size") info.ramsize = hex(attr.content);
if(attr.name == "battery") info.battery = (attr.content == "true" ? true : false);
}
}
}
}
}
info.romsize = hex(document["cartridge"]["rom"]["size"].value);
info.ramsize = hex(document["cartridge"]["ram"]["size"].value);
info.battery = document["cartridge"]["ram"]["non-volatile"].exists();
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
@@ -83,7 +65,9 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
ramdata = new uint8_t[ramsize = info.ramsize]();
system.load();
loaded = true;
sha256 = nall::sha256(romdata, romsize);
}
void Cartridge::unload() {

View File

@@ -34,6 +34,7 @@ struct Cartridge : MMIO, property<Cartridge> {
} info;
readonly<bool> loaded;
readonly<string> sha256;
uint8_t *romdata;
unsigned romsize;
@@ -44,7 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
MMIO *mapper;
bool bootrom_enable;
void load(const string &xml, const uint8_t *data, unsigned size);
void load(const string &markup, const uint8_t *data, unsigned size);
void unload();
uint8 rom_read(unsigned addr);

View File

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

91
bsnes/gameboy/cheat/cheat.cpp Executable file
View File

@@ -0,0 +1,91 @@
#include <gameboy/gameboy.hpp>
namespace GameBoy {
Cheat cheat;
bool Cheat::decode(const string &code_, unsigned &addr, unsigned &data, unsigned &comp) {
static bool initialize = false;
static uint8 mapProActionReplay[256], mapGameGenie[256];
if(initialize == false) {
initialize = true;
for(auto &n : mapProActionReplay) n = ~0;
mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3;
mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7;
mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11;
mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15;
for(auto &n : mapGameGenie) n = ~0;
mapGameGenie['0'] = 0; mapGameGenie['1'] = 1; mapGameGenie['2'] = 2; mapGameGenie['3'] = 3;
mapGameGenie['4'] = 4; mapGameGenie['5'] = 5; mapGameGenie['6'] = 6; mapGameGenie['7'] = 7;
mapGameGenie['8'] = 8; mapGameGenie['9'] = 9; mapGameGenie['A'] = 10; mapGameGenie['B'] = 11;
mapGameGenie['C'] = 12; mapGameGenie['D'] = 13; mapGameGenie['E'] = 14; mapGameGenie['F'] = 15;
}
string code = code_;
code.upper();
unsigned length = code.length(), bits = 0;
if(code.wildcard("????:??")) {
code = { substr(code, 0, 4), substr(code, 5, 2) };
for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 8) & 0xffff;
data = (bits >> 0) & 0xff;
comp = ~0;
return true;
}
if(code.wildcard("????:??:??")) {
code = { substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2) };
for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 16) & 0xffff;
data = (bits >> 8) & 0xff;
comp = (bits >> 0) & 0xff;
return true;
}
if(code.wildcard("???" "-" "???")) {
code = { substr(code, 0, 3), substr(code, 4, 3) };
for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4);
addr = (bits >> 0) & 0xffff;
data = (bits >> 16) & 0xff;
comp = ~0;
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
return true;
}
if(code.wildcard("???" "-" "???" "-" "???")) {
code = { substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1) };
for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4);
addr = (bits >> 8) & 0xffff;
data = (bits >> 24) & 0xff;
comp = (bits >> 0) & 0xff;
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba;
return true;
}
return false;
}
void Cheat::synchronize() {
for(auto &n : override) n = false;
for(unsigned n = 0; n < size(); n++) {
override[operator[](n).addr] = true;
}
}
}

14
bsnes/gameboy/cheat/cheat.hpp Executable file
View File

@@ -0,0 +1,14 @@
struct CheatCode {
unsigned addr;
unsigned data;
unsigned comp;
};
struct Cheat : public linear_vector<CheatCode> {
static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp);
void synchronize();
bool override[65536];
};
extern Cheat cheat;

View File

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

View File

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

View File

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

View File

@@ -3,15 +3,15 @@
void CPU::mmio_joyp_poll() {
unsigned button = 0, dpad = 0;
button |= system.interface->input_poll((unsigned)Input::Start) << 3;
button |= system.interface->input_poll((unsigned)Input::Select) << 2;
button |= system.interface->input_poll((unsigned)Input::B) << 1;
button |= system.interface->input_poll((unsigned)Input::A) << 0;
button |= interface->inputPoll((unsigned)Input::Start) << 3;
button |= interface->inputPoll((unsigned)Input::Select) << 2;
button |= interface->inputPoll((unsigned)Input::B) << 1;
button |= interface->inputPoll((unsigned)Input::A) << 0;
dpad |= system.interface->input_poll((unsigned)Input::Down) << 3;
dpad |= system.interface->input_poll((unsigned)Input::Up) << 2;
dpad |= system.interface->input_poll((unsigned)Input::Left) << 1;
dpad |= system.interface->input_poll((unsigned)Input::Right) << 0;
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
status.joyp = 0x0f;
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
@@ -84,7 +84,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
if(addr == 0xff00) { //JOYP
status.p15 = data & 0x20;
status.p14 = data & 0x10;
system.interface->joyp_write(status.p15, status.p14);
interface->joypWrite(status.p15, status.p14);
mmio_joyp_poll();
return;
}

View File

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

View File

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

View File

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

View File

@@ -1,18 +1,22 @@
//bgameboy
//author: byuu
//project started: 2010-12-27
#ifndef GAMEBOY_HPP
#define GAMEBOY_HPP
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000.19";
static unsigned SerializerVersion = 1;
static const unsigned SerializerVersion = 2;
}
}
/*
bgameboy - Game Boy emulator
author: byuu
license: GPLv3
project started: 2010-12-27
*/
#include <libco/libco.h>
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
@@ -83,7 +87,7 @@ namespace GameBoy {
clock = 0;
}
inline Processor() : thread(0) {}
inline Processor() : thread(nullptr) {}
};
#include <gameboy/memory/memory.hpp>
@@ -93,4 +97,7 @@ namespace GameBoy {
#include <gameboy/cpu/cpu.hpp>
#include <gameboy/apu/apu.hpp>
#include <gameboy/lcd/lcd.hpp>
#include <gameboy/cheat/cheat.hpp>
};
#endif

View File

@@ -0,0 +1,89 @@
#include <gameboy/gameboy.hpp>
namespace GameBoy {
Interface *interface = 0;
void Interface::lcdScanline() {
}
void Interface::joypWrite(bool p15, bool p14) {
}
void Interface::videoRefresh(const uint8_t *data) {
}
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
}
bool Interface::inputPoll(unsigned id) {
return false;
}
void Interface::initialize(Interface *derived_interface) {
interface = derived_interface;
system.init();
}
bool Interface::cartridgeLoaded() {
return cartridge.loaded();
}
void Interface::loadCartridge(const string &markup, const uint8_t *data, unsigned size) {
cartridge.load(markup, data, size);
system.power();
}
void Interface::unloadCartridge() {
cartridge.unload();
}
unsigned Interface::memorySize(Memory memory) {
if(memory == Memory::RAM) return cartridge.ramsize;
return 0u;
}
uint8_t* Interface::memoryData(Memory memory) {
if(memory == Memory::RAM) return cartridge.ramdata;
return 0u;
}
void Interface::power() {
system.power();
}
void Interface::run() {
do {
system.run();
} while(scheduler.exit_reason() != Scheduler::ExitReason::FrameEvent);
}
serializer Interface::serialize() {
system.runtosave();
return system.serialize();
}
bool Interface::unserialize(serializer &s) {
return system.unserialize(s);
}
void Interface::setCheats(const lstring &list) {
cheat.reset();
for(auto &code : list) {
lstring codelist;
codelist.split("+", code);
for(auto &part : codelist) {
unsigned addr, data, comp;
if(Cheat::decode(part, addr, data, comp)) {
cheat.append({ addr, data, comp });
}
}
}
cheat.synchronize();
}
void Interface::message(const string &text) {
print(text, "\n");
}
}

View File

@@ -1,11 +1,34 @@
class Interface {
public:
virtual void joyp_write(bool p15, bool p14) {}
virtual void lcdScanline();
virtual void joypWrite(bool p15, bool p14);
virtual void video_refresh(const uint8_t *data) {}
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
virtual void input_poll() {}
virtual bool input_poll(unsigned id) {}
virtual void videoRefresh(const uint8_t *data);
virtual void audioSample(int16_t center, int16_t left, int16_t right);
virtual bool inputPoll(unsigned id);
virtual void message(const string &text) { print(text, "\n"); }
virtual void initialize(Interface*);
virtual bool cartridgeLoaded();
virtual void loadCartridge(const string &markup, const uint8_t *data, unsigned size);
virtual void unloadCartridge();
enum class Memory : unsigned {
RAM,
};
virtual unsigned memorySize(Memory);
virtual uint8_t* memoryData(Memory);
virtual void power();
virtual void run();
virtual serializer serialize();
virtual bool unserialize(serializer&);
virtual void setCheats(const lstring &list = lstring{});
virtual void message(const string &text);
};
extern Interface *interface;

View File

@@ -7,6 +7,8 @@ namespace GameBoy {
#include "serialization.cpp"
LCD lcd;
static unsigned linectr;
void LCD::Main() {
lcd.main();
}
@@ -18,21 +20,20 @@ void LCD::main() {
}
add_clocks(4);
status.lx += 4;
if(status.lx >= 456) scanline();
if(status.lx == 0) {
if(status.display_enable && status.lx == 0) {
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.lx == 252) {
if(status.display_enable && status.lx == 252) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
}
void LCD::add_clocks(unsigned clocks) {
status.lx += clocks;
if(status.lx >= 456) scanline();
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
@@ -43,29 +44,32 @@ void LCD::scanline() {
status.lx -= 456;
if(++status.ly == 154) frame();
if(status.interrupt_lyc == true) {
if(status.display_enable && status.interrupt_lyc == true) {
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly < 144) render();
if(status.ly == 144) {
if(status.display_enable && status.ly == 144) {
cpu.interrupt_raise(CPU::Interrupt::Vblank);
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
void LCD::frame() {
system.interface->video_refresh(screen);
system.interface->input_poll();
interface->videoRefresh(screen);
cpu.mmio_joyp_poll();
status.ly = 0;
status.wyc = 0;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
void LCD::render() {
for(unsigned n = 0; n < 160; n++) line[n] = 0x00;
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x00;
origin[n] = Origin::None;
}
if(status.display_enable == true) {
if(status.bg_enable == true) render_bg();
@@ -74,7 +78,8 @@ void LCD::render() {
}
uint8_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
interface->lcdScanline();
}
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
@@ -97,7 +102,9 @@ void LCD::render_bg() {
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = status.bgp[palette];
origin[ox] = Origin::BG;
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
@@ -107,15 +114,19 @@ void LCD::render_bg() {
}
void LCD::render_window() {
if(status.ly - status.wy >= 144U) return;
unsigned iy = status.ly - status.wy;
unsigned ix = (status.wx - 7) & 255, tx = ix & 7;
if(status.ly - status.wy >= 144u) return;
if(status.wx >= 167u) return;
unsigned iy = status.wyc++;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned data = read_tile(status.window_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160U) line[ox] = status.bgp[palette];
if(ox - (status.wx - 7) < 160U) {
line[ox] = status.bgp[palette];
origin[ox] = Origin::Window;
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
@@ -125,6 +136,8 @@ void LCD::render_window() {
}
void LCD::render_obj() {
enum : unsigned { Priority = 0x80, YFlip = 0x40, XFlip = 0x20, Palette = 0x10 };
unsigned obj_size = (status.obj_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
@@ -164,26 +177,29 @@ void LCD::render_obj() {
sy = status.ly - sy;
if(sy >= obj_size) continue;
if(attribute & 0x40) sy ^= (obj_size - 1);
if(attribute & YFlip) sy ^= (obj_size - 1);
unsigned tdaddr = (tile << 4) + (sy << 1);
uint8 d0 = vram[tdaddr + 0];
uint8 d1 = vram[tdaddr + 1];
unsigned xflip = attribute & 0x20 ? 7 : 0;
unsigned xflip = attribute & XFlip ? 7 : 0;
for(unsigned tx = 0; tx < 8; tx++) {
uint8 palette = ((d0 & (0x80 >> tx)) ? 1 : 0)
| ((d1 & (0x80 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = status.obp[(bool)(attribute & 0x10)][palette];
palette = status.obp[(bool)(attribute & Palette)][palette];
unsigned ox = sx + (tx ^ xflip);
if(ox <= 159) {
if(attribute & 0x80) {
if(line[ox] > 0) continue;
if(attribute & Priority) {
if(origin[ox] == Origin::BG || origin[ox] == Origin::Window) {
if(line[ox] > 0) continue;
}
}
line[ox] = palette;
origin[ox] = Origin::OBJ;
}
}
}
@@ -202,6 +218,7 @@ void LCD::power() {
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
status.lx = 0;
status.wyc = 0;
status.display_enable = 0;
status.window_tilemap_select = 0;

View File

@@ -3,6 +3,7 @@ struct LCD : Processor, MMIO {
struct Status {
unsigned lx;
unsigned wyc;
//$ff40 LCDC
bool display_enable;
@@ -51,6 +52,9 @@ struct LCD : Processor, MMIO {
uint8 oam[160];
uint8 line[160];
struct Origin { enum : unsigned { None, BG, Window, OBJ }; };
uint8 origin[160];
static void Main();
void main();
void add_clocks(unsigned clocks);

View File

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

View File

@@ -2,6 +2,7 @@
void LCD::serialize(serializer &s) {
s.integer(status.lx);
s.integer(status.wyc);
s.integer(status.display_enable);
s.integer(status.window_tilemap_select);
@@ -33,6 +34,7 @@ void LCD::serialize(serializer &s) {
s.array(vram);
s.array(oam);
s.array(line);
s.array(origin);
}
#endif

View File

@@ -42,7 +42,20 @@ Memory::~Memory() {
//
uint8 Bus::read(uint16 addr) {
return mmio[addr]->mmio_read(addr);
uint8 data = mmio[addr]->mmio_read(addr);
if(cheat.override[addr]) {
for(unsigned n = 0; n < cheat.size(); n++) {
if(cheat[n].addr == addr) {
if(cheat[n].comp > 255 || cheat[n].comp == data) {
data = cheat[n].data;
break;
}
}
}
}
return data;
}
void Bus::write(uint16 addr, uint8 data) {

View File

@@ -33,8 +33,8 @@ void System::runthreadtosave() {
}
}
void System::init(Interface *interface_) {
interface = interface_;
void System::init() {
assert(interface != 0);
}
void System::load() {

View File

@@ -14,11 +14,10 @@ struct System {
void runtosave();
void runthreadtosave();
void init(Interface*);
void init();
void load();
void power();
Interface *interface;
unsigned clocks_executed;
//serialization.cpp

View File

@@ -32,9 +32,9 @@ ifeq ($(compiler),)
ifeq ($(platform),win)
compiler := gcc
else ifeq ($(platform),osx)
compiler := gcc-mp-4.5
compiler := gcc-mp-4.6
else
compiler := gcc-4.5
compiler := gcc-4.6
endif
endif

View File

@@ -2,13 +2,12 @@
#define NALL_ARRAY_HPP
#include <stdlib.h>
#include <algorithm>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/concept.hpp>
#include <nall/foreach.hpp>
#include <nall/utility.hpp>
namespace nall {
@@ -26,7 +25,7 @@ namespace nall {
void reset() {
if(pool) free(pool);
pool = 0;
pool = nullptr;
poolsize = 0;
buffersize = 0;
}
@@ -54,15 +53,12 @@ namespace nall {
operator[](buffersize) = data;
}
template<typename U> void insert(unsigned index, const U list) {
unsigned listsize = container_size(list);
resize(buffersize + listsize);
memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T));
foreach(item, list) pool[index++] = item;
void append(const T data[], unsigned length) {
for(unsigned n = 0; n < length; n++) operator[](buffersize) = data[n];
}
void insert(unsigned index, const T item) {
insert(index, array<T>{ item });
void remove() {
if(size > 0) resize(size - 1); //remove last element only
}
void remove(unsigned index, unsigned count = 1) {
@@ -82,10 +78,10 @@ namespace nall {
memset(pool, 0, buffersize * sizeof(T));
}
array() : pool(0), poolsize(0), buffersize(0) {
array() : pool(nullptr), poolsize(0), buffersize(0) {
}
array(std::initializer_list<T> list) : pool(0), poolsize(0), buffersize(0) {
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), buffersize(0) {
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
}
@@ -103,7 +99,7 @@ namespace nall {
return *this;
}
array(const array &source) : pool(0), poolsize(0), buffersize(0) {
array(const array &source) : pool(nullptr), poolsize(0), buffersize(0) {
operator=(source);
}
@@ -113,12 +109,12 @@ namespace nall {
pool = source.pool;
poolsize = source.poolsize;
buffersize = source.buffersize;
source.pool = 0;
source.pool = nullptr;
source.reset();
return *this;
}
array(array &&source) : pool(0), poolsize(0), buffersize(0) {
array(array &&source) : pool(nullptr), poolsize(0), buffersize(0) {
operator=(std::move(source));
}
@@ -133,9 +129,13 @@ namespace nall {
if(index >= buffersize) throw "array[] out of bounds";
return pool[index];
}
};
template<typename T> struct has_size<array<T>> { enum { value = true }; };
//iteration
T* begin() { return &pool[0]; }
T* end() { return &pool[buffersize]; }
const T* begin() const { return &pool[0]; }
const T* end() const { return &pool[buffersize]; }
};
}
#endif

88
bsnes/nall/atoi.hpp Executable file
View File

@@ -0,0 +1,88 @@
#ifndef NALL_ATOI_HPP
#define NALL_ATOI_HPP
namespace nall {
//note: this header is intended to form the base for user-defined literals;
//once they are supported by GCC. eg:
//unsigned operator "" b(const char *s) { return binary(s); }
//-> signed data = 1001b;
//(0b1001 is nicer, but is not part of the C++ standard)
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
return (
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
sum
);
}
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
return (
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
sum
);
}
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
return (
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
sum
);
}
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
return (
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
sum
);
}
//
constexpr inline uintmax_t binary(const char *s) {
return (
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
*s == '%' ? binary_(s + 1) :
binary_(s)
);
}
constexpr inline uintmax_t octal(const char *s) {
return (
octal_(s)
);
}
constexpr inline intmax_t integer(const char *s) {
return (
*s == '+' ? +decimal_(s + 1) :
*s == '-' ? -decimal_(s + 1) :
decimal_(s)
);
}
constexpr inline uintmax_t decimal(const char *s) {
return (
decimal_(s)
);
}
constexpr inline uintmax_t hex(const char *s) {
return (
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
*s == '$' ? hex_(s + 1) :
hex_(s)
);
}
inline double fp(const char *s) {
return atof(s);
}
}
#endif

View File

@@ -5,8 +5,7 @@
#include <nall/stdint.hpp>
namespace nall {
class base64 {
public:
struct base64 {
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
output = new char[inlength * 8 / 6 + 6]();
@@ -72,6 +71,7 @@ namespace nall {
private:
static char enc(uint8_t n) {
//base64 for URL encodings
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}

View File

@@ -2,39 +2,39 @@
#define NALL_BIT_HPP
namespace nall {
template<int bits> inline unsigned uclamp(const unsigned x) {
template<int bits> constexpr inline unsigned uclamp(const unsigned x) {
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
return y + ((x - y) & -(x < y)); //min(x, y);
}
template<int bits> inline unsigned uclip(const unsigned x) {
template<int bits> constexpr inline unsigned uclip(const unsigned x) {
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
return (x & m);
}
template<int bits> inline signed sclamp(const signed x) {
template<int bits> constexpr inline signed sclamp(const signed x) {
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
return (x > m) ? m : (x < -b) ? -b : x;
}
template<int bits> inline signed sclip(const signed x) {
template<int bits> constexpr inline signed sclip(const signed x) {
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
return ((x & m) ^ b) - b;
}
namespace bit {
//lowest(0b1110) == 0b0010
template<typename T> inline T lowest(const T x) {
template<typename T> constexpr inline T lowest(const T x) {
return x & -x;
}
//clear_lowest(0b1110) == 0b1100
template<typename T> inline T clear_lowest(const T x) {
template<typename T> constexpr inline T clear_lowest(const T x) {
return x & (x - 1);
}
//set_lowest(0b0101) == 0b0111
template<typename T> inline T set_lowest(const T x) {
template<typename T> constexpr inline T set_lowest(const T x) {
return x | (x + 1);
}

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -1,34 +0,0 @@
#ifndef NALL_CONCEPT_HPP
#define NALL_CONCEPT_HPP
#include <nall/static.hpp>
#include <nall/utility.hpp>
namespace nall {
//unsigned count() const;
template<typename T> struct has_count { enum { value = false }; };
//unsigned length() const;
template<typename T> struct has_length { enum { value = false }; };
//unsigned size() const;
template<typename T> struct has_size { enum { value = false }; };
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_count<T>>::type = 0) {
return object.count();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_length<T>>::type = 0) {
return object.length();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_size<T>>::type = 0) {
return object.size();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<std::is_array<T>>::type = 0) {
return sizeof(T) / sizeof(typename std::remove_extent<T>::type);
}
}
#endif

View File

@@ -34,11 +34,11 @@ namespace nall {
string get() const {
switch(type) {
case boolean_t: return string() << *(bool*)data;
case signed_t: return string() << *(signed*)data;
case unsigned_t: return string() << *(unsigned*)data;
case double_t: return string() << *(double*)data;
case string_t: return string() << "\"" << *(string*)data << "\"";
case boolean_t: return { *(bool*)data };
case signed_t: return { *(signed*)data };
case unsigned_t: return { *(unsigned*)data };
case double_t: return { *(double*)data };
case string_t: return { "\"", *(string*)data, "\"" };
}
return "???";
}
@@ -70,7 +70,7 @@ namespace nall {
else list[n].type = unknown_t;
}
virtual bool load(const char *filename) {
virtual bool load(const string &filename) {
string data;
if(data.readfile(filename) == true) {
data.replace("\r", "");
@@ -100,14 +100,14 @@ namespace nall {
}
}
virtual bool save(const char *filename) const {
virtual bool save(const string &filename) const {
file fp;
if(fp.open(filename, file::mode::write)) {
for(unsigned i = 0; i < list.size(); i++) {
string output;
output << list[i].name << " = " << list[i].get();
if(list[i].desc != "") output << " # " << list[i].desc;
output << "\r\n";
output.append(list[i].name, " = ", list[i].get());
if(list[i].desc != "") output.append(" # ", list[i].desc);
output.append("\r\n");
fp.print(output);
}

View File

@@ -1,30 +0,0 @@
#ifndef NALL_DETECT_HPP
#define NALL_DETECT_HPP
/* Compiler detection */
#if defined(__GNUC__)
#define COMPILER_GCC
#elif defined(_MSC_VER)
#define COMPILER_VISUALC
#endif
/* Platform detection */
#if defined(_WIN32)
#define PLATFORM_WIN
#elif defined(__APPLE__)
#define PLATFORM_OSX
#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define PLATFORM_X
#endif
/* Endian detection */
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
#define ARCH_LSB
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
#define ARCH_MSB
#endif
#endif

View File

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

View File

@@ -1,12 +1,13 @@
#ifndef NALL_DIRECTORY_HPP
#define NALL_DIRECTORY_HPP
#include <nall/foreach.hpp>
#include <nall/intrinsics.hpp>
#include <nall/sort.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
#if defined(_WIN32)
#include <nall/utf8.hpp>
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/utf8.hpp>
#else
#include <dirent.h>
#include <stdio.h>
@@ -22,7 +23,7 @@ struct directory {
static lstring contents(const string &pathname, const string &pattern = "*");
};
#if defined(_WIN32)
#if defined(PLATFORM_WINDOWS)
inline bool directory::exists(const string &pathname) {
DWORD result = GetFileAttributes(utf16_t(pathname));
if(result == INVALID_FILE_ATTRIBUTES) return false;
@@ -42,20 +43,21 @@ struct directory {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(string(name, "/"));
if(wildcard(name, pattern)) list.append(name);
}
}
while(FindNextFile(handle, &data) != false) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(string(name, "/"));
if(wildcard(name, pattern)) list.append(name);
}
}
}
FindClose(handle);
}
if(list.size() > 0) sort(&list[0], list.size());
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
@@ -88,7 +90,7 @@ struct directory {
inline lstring directory::contents(const string &pathname, const string &pattern) {
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
lstring files = directory::files(pathname, pattern);
foreach(file, files) folders.append(file);
for(auto &file : files) folders.append(file);
return folders;
}
#else
@@ -109,14 +111,14 @@ struct directory {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if(ep->d_type & DT_DIR) {
if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/"));
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
}
}
closedir(dp);
}
if(list.size() > 0) sort(&list[0], list.size());
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::files(const string &pathname, const string &pattern) {
@@ -141,7 +143,7 @@ struct directory {
inline lstring directory::contents(const string &pathname, const string &pattern) {
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
lstring files = directory::files(pathname, pattern);
foreach(file, files) folders.append(file);
for(auto &file : files) folders.append(file);
return folders;
}
#endif

View File

@@ -3,16 +3,16 @@
//dynamic linking support
#include <nall/detect.hpp>
#include <nall/intrinsics.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
#include <dlfcn.h>
#elif defined(PLATFORM_WIN)
#elif defined(PLATFORM_WINDOWS)
#include <windows.h>
#include <nall/utf8.hpp>
#include <nall/windows/utf8.hpp>
#endif
namespace nall {
@@ -81,7 +81,7 @@ namespace nall {
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_WIN)
#elif defined(PLATFORM_WINDOWS)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");

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

@@ -0,0 +1,13 @@
#ifndef NALL_DSP_HPP
#define NALL_DSP_HPP
#include <algorithm>
#ifdef __SSE__
#include <xmmintrin.h>
#endif
#define NALL_DSP_INTERNAL_HPP
#include <nall/dsp/core.hpp>
#undef NALL_DSP_INTERNAL_HPP
#endif

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

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

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

@@ -0,0 +1,167 @@
#ifdef NALL_DSP_INTERNAL_HPP
#include <math.h>
#include <nall/stdint.hpp>
namespace nall {
//precision: can be float, double or long double
#define real float
struct DSP;
struct Resampler {
DSP &dsp;
real frequency;
virtual void setFrequency() = 0;
virtual void clear() = 0;
virtual void sample() = 0;
Resampler(DSP &dsp) : dsp(dsp) {}
};
struct DSP {
enum class ResampleEngine : unsigned {
Nearest,
Linear,
Cosine,
Cubic,
Hermite,
Average,
Sinc,
};
inline void setChannels(unsigned channels);
inline void setPrecision(unsigned precision);
inline void setFrequency(real frequency); //inputFrequency
inline void setVolume(real volume);
inline void setBalance(real balance);
inline void setResampler(ResampleEngine resamplingEngine);
inline void setResamplerFrequency(real frequency); //outputFrequency
inline void sample(signed channel[]);
inline bool pending();
inline void read(signed channel[]);
inline void clear();
inline DSP();
inline ~DSP();
protected:
friend class ResampleNearest;
friend class ResampleLinear;
friend class ResampleCosine;
friend class ResampleCubic;
friend class ResampleAverage;
friend class ResampleHermite;
friend class ResampleSinc;
struct Settings {
unsigned channels;
unsigned precision;
real frequency;
real volume;
real balance;
//internal
real intensity;
real intensityInverse;
} settings;
Resampler *resampler;
inline void write(real channel[]);
#include "buffer.hpp"
Buffer buffer;
Buffer output;
inline void adjustVolume();
inline void adjustBalance();
inline signed clamp(const unsigned bits, const signed x);
};
#include "resample/nearest.hpp"
#include "resample/linear.hpp"
#include "resample/cosine.hpp"
#include "resample/cubic.hpp"
#include "resample/hermite.hpp"
#include "resample/average.hpp"
#include "resample/sinc.hpp"
#include "settings.hpp"
void DSP::sample(signed channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
}
buffer.wroffset++;
resampler->sample();
}
bool DSP::pending() {
return output.rdoffset != output.wroffset;
}
void DSP::read(signed channel[]) {
adjustVolume();
adjustBalance();
for(unsigned c = 0; c < settings.channels; c++) {
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
}
output.rdoffset++;
}
void DSP::write(real channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
output.write(c) = channel[c];
}
output.wroffset++;
}
void DSP::adjustVolume() {
for(unsigned c = 0; c < settings.channels; c++) {
output.read(c) *= settings.volume;
}
}
void DSP::adjustBalance() {
if(settings.channels != 2) return; //TODO: support > 2 channels
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
}
signed DSP::clamp(const unsigned bits, const signed x) {
const signed b = 1U << (bits - 1);
const signed m = (1U << (bits - 1)) - 1;
return (x > m) ? m : (x < -b) ? -b : x;
}
void DSP::clear() {
buffer.clear();
output.clear();
resampler->clear();
}
DSP::DSP() {
setResampler(ResampleEngine::Hermite);
setResamplerFrequency(44100.0);
setChannels(2);
setPrecision(16);
setFrequency(44100.0);
setVolume(1.0);
setBalance(0.0);
clear();
}
DSP::~DSP() {
if(resampler) delete resampler;
}
#undef real
}
#endif

View File

@@ -0,0 +1,72 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleAverage : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
inline void sampleLinear();
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleAverage::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleAverage::clear() {
fraction = 0.0;
}
void ResampleAverage::sample() {
//can only average if input frequency >= output frequency
if(step < 1.0) return sampleLinear();
fraction += 1.0;
real scalar = 1.0;
if(fraction > step) scalar = 1.0 - (fraction - step);
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
}
if(fraction >= step) {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) /= step;
}
dsp.output.wroffset++;
fraction -= step;
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
}
}
dsp.buffer.rdoffset++;
}
void ResampleAverage::sampleLinear() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,44 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleCosine : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleCosine::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleCosine::clear() {
fraction = 0.0;
}
void ResampleCosine::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,50 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleCubic : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleCubic::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleCubic::clear() {
fraction = 0.0;
}
void ResampleCubic::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -3);
real b = dsp.buffer.read(n, -2);
real c = dsp.buffer.read(n, -1);
real d = dsp.buffer.read(n, -0);
real mu = fraction;
real A = d - c - a + b;
real B = a - b - A;
real C = c - a;
real D = b;
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,62 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleHermite : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleHermite::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleHermite::clear() {
fraction = 0.0;
}
void ResampleHermite::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -3);
real b = dsp.buffer.read(n, -2);
real c = dsp.buffer.read(n, -1);
real d = dsp.buffer.read(n, -0);
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
mu1 = fraction;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,600 @@
// If these types are changed to anything other than "float", you should comment out the SSE detection directives below
// so that the SSE code is not used.
typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16
typedef float resample_samp_t;
// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE.
#define RESAMPLE_SSEREGPARM
#if defined(__SSE__)
#define SINCRESAMPLE_USE_SSE 1
#ifndef __x86_64__
#undef RESAMPLE_SSEREGPARM
#define RESAMPLE_SSEREGPARM __attribute__((sseregparm))
#endif
#else
// TODO: altivec here
#endif
namespace ResampleUtility
{
inline void kaiser_window(double* io, int count, double beta);
inline void gen_sinc(double* out, int size, double cutoff, double kaiser);
inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser);
inline void normalize(double* io, int size, double gain = 1.0);
inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2
}
class SincResampleHR
{
private:
inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d);
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
inline bool output_avail(void);
private:
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count);
unsigned ratio;
unsigned num_convolutions;
resample_coeff_t *coeffs;
std::vector<unsigned char> coeffs_mem;
// second half of ringbuffer should be copy of first half.
resample_samp_t *rb;
std::vector<unsigned char> rb_mem;
signed rb_readpos;
signed rb_writepos;
signed rb_in;
signed rb_eff_size;
friend class SincResample;
};
class SincResample
{
public:
enum
{
QUALITY_LOW = 0,
QUALITY_MEDIUM = 2,
QUALITY_HIGH = 4
};
inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH);
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
inline bool output_avail(void);
private:
inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min);
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM;
unsigned num_convolutions;
unsigned num_phases;
unsigned step_int;
double step_fract;
double input_pos_fract;
std::vector<resample_coeff_t *> coeffs; // Pointers into coeff_mem.
std::vector<unsigned char> coeff_mem;
std::vector<resample_samp_t> rb; // second half should be copy of first half.
signed rb_readpos;
signed rb_writepos;
signed rb_in;
bool hr_used;
SincResampleHR hr;
};
//
// Code:
//
//#include "resample.hpp"
#if 0
namespace bit
{
inline unsigned round(unsigned x) {
if((x & (x - 1)) == 0) return x;
while(x & (x - 1)) x &= x - 1;
return x << 1;
}
}
#endif
void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d)
{
const unsigned align_boundary = 16;
std::vector<double> coeffs_tmp;
double cutoff; // 1.0 = f/2
ratio = ratio_arg;
//num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even
num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1);
cutoff = (1.0 / ratio) - (d / num_convolutions);
//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff);
assert(num_convolutions > ratio);
// Generate windowed sinc of POWER
coeffs_tmp.resize(num_convolutions);
//ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta);
ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta);
ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions);
// Copy from coeffs_tmp to coeffs~
// We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really
// exist are allocated, zero'd mem.
coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1));
coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary);
for(unsigned i = 0; i < num_convolutions; i++)
coeffs[i] = coeffs_tmp[i];
rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1;
rb_readpos = 0;
rb_writepos = 0;
rb_in = 0;
rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1));
rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary);
}
inline bool SincResampleHR::output_avail(void)
{
return(rb_in >= (signed)num_convolutions);
}
inline void SincResampleHR::write(resample_samp_t sample)
{
assert(!output_avail());
rb[rb_writepos] = sample;
rb[rb_writepos + rb_eff_size] = sample;
rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1);
rb_in++;
}
resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count)
{
#if SINCRESAMPLE_USE_SSE
__m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
resample_samp_t accum;
for(unsigned c = 0; c < count; c += 8)
{
for(unsigned i = 0; i < 2; i++)
{
__m128 co[2];
__m128 w[2];
co[i] = _mm_load_ps(&coeff[c + i * 4]);
w[i] = _mm_load_ps(&wave[c + i * 4]);
w[i] = _mm_mul_ps(w[i], co[i]);
accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]);
}
}
__m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3]));
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
_mm_store_ss(&accum, accum_vec);
return accum;
#else
resample_samp_t accum[4] = { 0, 0, 0, 0 };
for(unsigned c = 0; c < count; c+= 4)
{
accum[0] += wave[c + 0] * coeff[c + 0];
accum[1] += wave[c + 1] * coeff[c + 1];
accum[2] += wave[c + 2] * coeff[c + 2];
accum[3] += wave[c + 3] * coeff[c + 3];
}
return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may...
#endif
}
resample_samp_t SincResampleHR::read(void)
{
assert(output_avail());
resample_samp_t ret;
ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions);
rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1);
rb_in -= ratio;
return ret;
}
SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality)
{
const struct
{
double beta;
double d;
unsigned pn_nume;
unsigned phases_min;
} qtab[5] =
{
{ 5.658, 3.62, 4096, 4 },
{ 6.764, 4.32, 8192, 4 },
{ 7.865, 5.0, 16384, 8 },
{ 8.960, 5.7, 32768, 16 },
{ 10.056, 6.4, 65536, 32 }
};
// Sanity checks
assert(ceil(input_rate) > 0);
assert(ceil(output_rate) > 0);
assert(ceil(input_rate / output_rate) <= 1024);
assert(ceil(output_rate / input_rate) <= 1024);
// The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when
// upsampling.
assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96);
assert(quality >= 0 && quality <= 4);
hr_used = false;
#if 1
// Round down to the nearest multiple of 4(so wave buffer remains aligned)
// It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2
// aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately
// to virtually eliminate over-attenuation.
unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3;
if(ioratio_rd >= 8)
{
hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4);
hr_used = true;
input_rate /= ioratio_rd;
}
#endif
Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min);
}
void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min)
{
const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2.
const unsigned max_mult_minus1 = (max_mult_atatime - 1);
const unsigned conv_alignment_bytes = 16; // must be power of 2
const double input_to_output_ratio = input_rate / output_rate;
const double output_to_input_ratio = output_rate / input_rate;
double cutoff; // 1.0 = input_rate / 2
std::vector<double> coeff_init_buffer;
// Round up num_convolutions to be even.
if(output_rate > input_rate)
num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1;
else
num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1;
if(output_rate > input_rate) // Upsampling
cutoff = desired_bandwidth;
else // Downsampling
cutoff = output_to_input_ratio * desired_bandwidth;
// Round up to be even.
num_phases = (std::max<unsigned>(pn_nume / num_convolutions, phases_min) + 1) &~1;
// Adjust cutoff to account for the multiple phases.
cutoff = cutoff / num_phases;
assert((num_convolutions & 1) == 0);
assert((num_phases & 1) == 0);
// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases,
// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes));
coeff_init_buffer.resize(num_phases * num_convolutions);
coeffs.resize(num_phases + 1 + 1);
coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes);
// Assign aligned pointers into coeff_mem
{
resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes);
for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++)
{
coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase);
}
}
ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta);
ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases);
// Reorder coefficients to allow for more efficient convolution.
for(int phase = -1; phase < ((int)num_phases + 1); phase++)
{
for(int conv = 0; conv < (int)num_convolutions; conv++)
{
double coeff;
if(phase == -1 && conv == 0)
coeff = 0;
else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1))
coeff = 0;
else
coeff = coeff_init_buffer[conv * num_phases + phase];
coeffs[phase + 1][conv] = coeff;
}
}
// Free a bit of mem
coeff_init_buffer.resize(0);
step_int = floor(input_to_output_ratio);
step_fract = input_to_output_ratio - step_int;
input_pos_fract = 0;
// Do NOT use rb.size() later in the code, since it'll include the padding.
// We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time
// rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half,
// since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to
// prevent illegal memory accesses.
rb.resize(num_convolutions * 2 + max_mult_minus1);
rb_readpos = 0;
rb_writepos = 0;
rb_in = 0;
}
resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count)
{
resample_samp_t accum = 0;
#if SINCRESAMPLE_USE_SSE
__m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
__m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
for(unsigned c = 0; c < count; c += 8) //8) //4)
{
__m128 coeff_a[2];
__m128 coeff_b[2];
__m128 w[2];
__m128 result_a[2], result_b[2];
for(unsigned i = 0; i < 2; i++)
{
coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]);
coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]);
w[i] = _mm_loadu_ps(&wave[c + (i * 4)]);
result_a[i] = _mm_mul_ps(coeff_a[i], w[i]);
result_b[i] = _mm_mul_ps(coeff_b[i], w[i]);
accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]);
accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]);
}
}
__m128 accum_vec, av_a, av_b;
__m128 mult_a_vec = _mm_set1_ps(1.0 - ffract);
__m128 mult_b_vec = _mm_set1_ps(ffract);
av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1]));
av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1]));
accum_vec = _mm_add_ps(av_a, av_b);
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
_mm_store_ss(&accum, accum_vec);
#else
resample_coeff_t mult_a = 1.0 - ffract;
resample_coeff_t mult_b = ffract;
for(unsigned c = 0; c < count; c += 4)
{
accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b);
accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b);
accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b);
accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b);
}
#endif
return accum;
}
inline bool SincResample::output_avail(void)
{
return(rb_in >= (int)num_convolutions);
}
resample_samp_t SincResample::read(void)
{
assert(output_avail());
double phase = input_pos_fract * num_phases - 0.5;
signed phase_int = (signed)floor(phase);
double phase_fract = phase - phase_int;
unsigned phase_a = num_phases - 1 - phase_int;
unsigned phase_b = phase_a - 1;
resample_samp_t ret;
ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions);
unsigned int_increment = step_int;
input_pos_fract += step_fract;
int_increment += floor(input_pos_fract);
input_pos_fract -= floor(input_pos_fract);
rb_readpos = (rb_readpos + int_increment) % num_convolutions;
rb_in -= int_increment;
return ret;
}
inline void SincResample::write(resample_samp_t sample)
{
assert(!output_avail());
if(hr_used)
{
hr.write(sample);
if(hr.output_avail())
{
sample = hr.read();
}
else
{
return;
}
}
rb[rb_writepos + 0 * num_convolutions] = sample;
rb[rb_writepos + 1 * num_convolutions] = sample;
rb_writepos = (rb_writepos + 1) % num_convolutions;
rb_in++;
}
void ResampleUtility::kaiser_window( double* io, int count, double beta)
{
int const accuracy = 24; //16; //12;
double* end = io + count;
double beta2 = beta * beta * (double) -0.25;
double to_fract = beta2 / ((double) count * count);
double i = 0;
double rescale = 0; // Doesn't need an initializer, to shut up gcc
for ( ; io < end; ++io, i += 1 )
{
double x = i * i * to_fract - beta2;
double u = x;
double k = x + 1;
double n = 2;
do
{
u *= x / (n * n);
n += 1;
k += u;
}
while ( k <= u * (1 << accuracy) );
if ( !i )
rescale = 1 / k; // otherwise values get large
*io *= k * rescale;
}
}
void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser)
{
assert( size % 2 == 0 ); // size must be even
int const half_size = size / 2;
double* const mid = &out [half_size];
// Generate right half of sinc
for ( int i = 0; i < half_size; i++ )
{
double angle = (i * 2 + 1) * (M_PI / 2);
mid [i] = sin( angle * cutoff ) / angle;
}
kaiser_window( mid, half_size, kaiser );
// Mirror for left half
for ( int i = 0; i < half_size; i++ )
out [i] = mid [half_size - 1 - i];
}
void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser)
{
assert( size % 2 == 1); // size must be odd
for(int i = 0; i < size; i++)
{
if(i == (size / 2))
out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
else
out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1));
//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1));
// printf("%d %f\n", i, out[i]);
}
kaiser_window(&out[size / 2], size / 2 + 1, kaiser);
// Mirror for left half
for ( int i = 0; i < size / 2; i++ )
out [i] = out [size - 1 - i];
}
void ResampleUtility::normalize(double* io, int size, double gain)
{
double sum = 0;
for ( int i = 0; i < size; i++ )
sum += io [i];
double scale = gain / sum;
for ( int i = 0; i < size; i++ )
io [i] *= scale;
}
void* ResampleUtility::make_aligned(void* ptr, unsigned boundary)
{
unsigned char* null_ptr = (unsigned char *)NULL;
unsigned char* uc_ptr = (unsigned char *)ptr;
uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1);
//while((uc_ptr - null_ptr) & (boundary - 1))
// uc_ptr++;
//printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr);
assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr));
return uc_ptr;
}

View File

@@ -0,0 +1,43 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleLinear : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleLinear::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleLinear::clear() {
fraction = 0.0;
}
void ResampleLinear::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,43 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleNearest : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleNearest(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleNearest::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleNearest::clear() {
fraction = 0.0;
}
void ResampleNearest::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
channel[n] = mu < 0.5 ? a : b;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -0,0 +1,54 @@
#ifdef NALL_DSP_INTERNAL_HPP
#include "lib/sinc.hpp"
struct ResampleSinc : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
inline ResampleSinc(DSP &dsp);
private:
inline void remakeSinc();
SincResample *sinc_resampler[8];
};
void ResampleSinc::setFrequency() {
remakeSinc();
}
void ResampleSinc::clear() {
remakeSinc();
}
void ResampleSinc::sample() {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
sinc_resampler[c]->write(dsp.buffer.read(c));
}
if(sinc_resampler[0]->output_avail()) {
do {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) = sinc_resampler[c]->read();
}
dsp.output.wroffset++;
} while(sinc_resampler[0]->output_avail());
}
dsp.buffer.rdoffset++;
}
ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) {
for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0;
}
void ResampleSinc::remakeSinc() {
assert(dsp.settings.channels < 8);
for(unsigned c = 0; c < dsp.settings.channels; c++) {
if(sinc_resampler[c]) delete sinc_resampler[c];
sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH);
}
}
#endif

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

@@ -0,0 +1,50 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::setChannels(unsigned channels) {
assert(channels > 0);
buffer.setChannels(channels);
output.setChannels(channels);
settings.channels = channels;
}
void DSP::setPrecision(unsigned precision) {
settings.precision = precision;
settings.intensity = 1 << (settings.precision - 1);
settings.intensityInverse = 1.0 / settings.intensity;
}
void DSP::setFrequency(real frequency) {
settings.frequency = frequency;
resampler->setFrequency();
}
void DSP::setVolume(real volume) {
settings.volume = volume;
}
void DSP::setBalance(real balance) {
settings.balance = balance;
}
void DSP::setResampler(ResampleEngine engine) {
if(resampler) delete resampler;
switch(engine) {
case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return;
case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return;
case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return;
case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return;
case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return;
case ResampleEngine::Average: resampler = new ResampleAverage(*this); return;
case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return;
}
throw;
}
void DSP::setResamplerFrequency(real frequency) {
resampler->frequency = frequency;
resampler->setFrequency();
}
#endif

View File

@@ -1,7 +1,9 @@
#ifndef NALL_ENDIAN_HPP
#define NALL_ENDIAN_HPP
#if !defined(ARCH_MSB)
#include <nall/intrinsics.hpp>
#if defined(ENDIAN_LSB)
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
#define order_lsb2(a,b) a,b
#define order_lsb3(a,b,c) a,b,c
@@ -17,7 +19,7 @@
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
#else
#elif defined(ENDIAN_MSB)
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
#define order_lsb2(a,b) b,a
#define order_lsb3(a,b,c) c,b,a
@@ -33,6 +35,8 @@
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
#else
#error "Unknown endian. Please specify in nall/intrinsics.hpp"
#endif
#endif

View File

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

View File

@@ -2,7 +2,7 @@
#define NALL_FILEMAP_HPP
#include <nall/stdint.hpp>
#include <nall/utf8.hpp>
#include <nall/windows/utf8.hpp>
#include <stdio.h>
#include <stdlib.h>
@@ -21,7 +21,7 @@ namespace nall {
public:
enum class mode : unsigned { read, write, readwrite, writeread };
bool opened() const { return p_opened(); }
bool open() const { return p_open(); }
bool open(const char *filename, mode mode_) { return p_open(filename, mode_); }
void close() { return p_close(); }
unsigned size() const { return p_size; }
@@ -42,11 +42,17 @@ namespace nall {
HANDLE p_filehandle, p_maphandle;
bool p_opened() const {
bool p_open() const {
return p_handle;
}
bool p_open(const char *filename, mode mode_) {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = 0;
p_size = 0;
return true;
}
int desired_access, creation_disposition, flprotect, map_access;
switch(mode_) {
@@ -128,11 +134,17 @@ namespace nall {
int p_fd;
bool p_opened() const {
bool p_open() const {
return p_handle;
}
bool p_open(const char *filename, mode mode_) {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = 0;
p_size = 0;
return true;
}
int open_flags, mmap_flags;
switch(mode_) {

View File

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

View File

@@ -36,19 +36,19 @@ namespace nall {
public:
operator bool() const { return callback; }
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
void reset() { if(callback) { delete callback; callback = 0; } }
void reset() { if(callback) { delete callback; callback = nullptr; } }
function& operator=(const function &source) {
if(this != &source) {
if(callback) { delete callback; callback = 0; }
if(callback) { delete callback; callback = nullptr; }
if(source.callback) callback = source.callback->copy();
}
return *this;
}
function(const function &source) : callback(0) { operator=(source); }
function() : callback(0) {}
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
function(const function &source) : callback(nullptr) { operator=(source); }
function() : callback(nullptr) {}
function(void *function) : callback(nullptr) { if(function) callback = new global((R (*)(P...))function); }
function(R (*function)(P...)) { callback = new global(function); }
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }

View File

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

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

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

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

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

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

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

View File

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

60
bsnes/nall/intrinsics.hpp Executable file
View File

@@ -0,0 +1,60 @@
#ifndef NALL_INTRINSICS_HPP
#define NALL_INTRINSICS_HPP
struct Intrinsics {
enum class Compiler : unsigned { GCC, VisualC, Unknown };
enum class Platform : unsigned { X, OSX, Windows, Unknown };
enum class Endian : unsigned { LSB, MSB, Unknown };
static inline Compiler compiler();
static inline Platform platform();
static inline Endian endian();
};
/* Compiler detection */
#if defined(__GNUC__)
#define COMPILER_GCC
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::GCC; }
#elif defined(_MSC_VER)
#define COMPILER_VISUALC
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::VisualC; }
#else
#define COMPILER_UNKNOWN
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; }
#endif
/* Platform detection */
#if defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define PLATFORM_X
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::X; }
#elif defined(__APPLE__)
#define PLATFORM_OSX
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::OSX; }
#elif defined(_WIN32)
#define PLATFORM_WINDOWS
#define PLATFORM_WIN
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; }
#else
#define PLATFORM_UNKNOWN
#endif
/* Endian detection */
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
#define ENDIAN_LSB
#define ARCH_LSB
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::LSB; }
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
#define ENDIAN_MSB
#define ARCH_MSB
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::MSB; }
#else
#define ENDIAN_UNKNOWN
#define ARCH_UNKNOWN
Intrinsics::Endian Intrinsics::endia() { return Intrinsics::Endian::Unknown; }
#endif
#endif

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

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

View File

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

View File

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

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

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

View File

@@ -12,7 +12,7 @@ namespace nall {
//priority queue implementation using binary min-heap array;
//does not require normalize() function.
//O(1) find (tick)
//O(log n) insert (enqueue)
//O(log n) append (enqueue)
//O(log n) remove (dequeue)
template<typename type_t> class priority_queue {
public:

View File

@@ -22,7 +22,7 @@
// readwrite<int> y;
//};
//return types are const T& (byref) instead fo T (byval) to avoid major speed
//return types are const T& (byref) instead of T (byval) to avoid major speed
//penalties for objects with expensive copy constructors
//operator-> provides access to underlying object type:

View File

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

View File

@@ -1,9 +1,9 @@
#ifndef NALL_REFERENCE_ARRAY_HPP
#define NALL_REFERENCE_ARRAY_HPP
#include <algorithm>
#include <type_traits>
#include <nall/bit.hpp>
#include <nall/concept.hpp>
namespace nall {
template<typename T> struct reference_array {
@@ -18,7 +18,7 @@ namespace nall {
void reset() {
if(pool) free(pool);
pool = 0;
pool = nullptr;
poolsize = 0;
buffersize = 0;
}
@@ -36,13 +36,29 @@ namespace nall {
buffersize = newsize;
}
void append(const T data) {
bool append(const T data) {
for(unsigned index = 0; index < buffersize; index++) {
if(pool[index] == &data) return false;
}
unsigned index = buffersize++;
if(index >= poolsize) resize(index + 1);
pool[index] = &data;
return true;
}
template<typename... Args> reference_array(Args&... args) : pool(0), poolsize(0), buffersize(0) {
bool remove(const T data) {
for(unsigned index = 0; index < buffersize; index++) {
if(pool[index] == &data) {
for(unsigned i = index; i < buffersize - 1; i++) pool[i] = pool[i + 1];
resize(buffersize - 1);
return true;
}
}
return false;
}
template<typename... Args> reference_array(Args&... args) : pool(nullptr), poolsize(0), buffersize(0) {
construct(args...);
}
@@ -64,7 +80,7 @@ namespace nall {
pool = source.pool;
poolsize = source.poolsize;
buffersize = source.buffersize;
source.pool = 0;
source.pool = nullptr;
source.reset();
return *this;
}
@@ -79,6 +95,22 @@ namespace nall {
return *pool[index];
}
//iteration
struct iterator {
bool operator!=(const iterator &source) const { return index != source.index; }
T& operator*() { return array.operator[](index); }
iterator& operator++() { index++; return *this; }
iterator(const reference_array &array, unsigned index) : array(array), index(index) {}
private:
const reference_array &array;
unsigned index;
};
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, buffersize); }
const iterator begin() const { return iterator(*this, 0); }
const iterator end() const { return iterator(*this, buffersize); }
private:
void construct() {
}
@@ -96,8 +128,6 @@ namespace nall {
construct(args...);
}
};
template<typename T> struct has_size<reference_array<T>> { enum { value = true }; };
}
#endif

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

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

View File

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

View File

@@ -3,10 +3,10 @@
namespace nall {
class SNESCartridge {
class SnesCartridge {
public:
string xmlMemoryMap;
inline SNESCartridge(const uint8_t *data, unsigned size);
string markup;
inline SnesCartridge(const uint8_t *data, unsigned size);
//private:
inline void read_header(const uint8_t *data, unsigned size);
@@ -105,432 +105,346 @@ public:
bool has_st018;
};
SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
#define T "\t"
SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
read_header(data, size);
string xml = "<?xml version='1.0' encoding='UTF-8'?>\n";
string xml;
markup = "";
if(type == TypeBsx) {
xml << "<cartridge/>";
xmlMemoryMap = xml.transform("'", "\"");
markup.append("cartridge");
return;
}
if(type == TypeSufamiTurbo) {
xml << "<cartridge/>";
xmlMemoryMap = xml.transform("'", "\"");
markup.append("cartridge");
return;
}
if(type == TypeGameBoy) {
xml << "<cartridge rtc='" << gameboy_has_rtc(data, size) << "'>\n";
markup.append("cartridge rtc=", gameboy_has_rtc(data, size), "\n");
if(gameboy_ram_size(data, size) > 0) {
xml << " <ram size='" << hex(gameboy_ram_size(data, size)) << "'/>\n";
markup.append(T "ram size=0x", hex(gameboy_ram_size(data, size)), "\n");
}
xml << "</cartridge>\n";
xmlMemoryMap = xml.transform("'", "\"");
return;
}
xml << "<cartridge";
if(region == NTSC) {
xml << " region='NTSC'";
} else {
xml << " region='PAL'";
}
xml << ">\n";
markup.append("cartridge region=", region == NTSC ? "NTSC\n" : "PAL\n");
if(type == TypeSuperGameBoy1Bios) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <icd2 revision='1'>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </icd2>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
markup.append(T "icd2 revision=1\n");
markup.append(T T "map address=00-3f:6000-7fff\n");
markup.append(T T "map address=80-bf:6000-7fff\n");
} else if(type == TypeSuperGameBoy2Bios) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <icd2 revision='2'>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </icd2>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
markup.append(T "icd2 revision=1\n");
markup.append(T T "map address=00-3f:6000-7fff\n");
markup.append(T T "map address=80-bf:6000-7fff\n");
} else if(has_cx4) {
markup.append(T "hitachidsp model=HG51B169 frequency=20000000 firmware=cx4.bin sha256=ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18\n");
markup.append(T T "rom\n");
markup.append(T T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T T "map mode=linear address=80-ff:8000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:6000-7fff\n");
markup.append(T T T "map address=80-bf:6000-7fff\n");
} else if(has_spc7110) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-0f:8000-ffff'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-cf:0000-ffff'/>\n";
xml << " </rom>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-0f:8000-ffff\n");
markup.append(T T "map mode=shadow address=80-bf:8000-ffff\n");
markup.append(T T "map mode=linear address=c0-cf:0000-ffff\n");
xml << " <spc7110>\n";
xml << " <mcu>\n";
xml << " <map address='d0-ff:0000-ffff' offset='100000' size='" << hex(size - 0x100000) << "'/>\n";
xml << " </mcu>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='00:6000-7fff'/>\n";
xml << " <map mode='linear' address='30:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:4800-483f'/>\n";
xml << " <map address='80-bf:4800-483f'/>\n";
xml << " </mmio>\n";
markup.append(T "spc7110\n");
markup.append(T T "mcu\n");
markup.append(T T T "map address=d0-ff:0000-ffff offset=0x100000 size=0x", hex(size - 0x100000), "\n");
markup.append(T T "ram size=0x", hex(ram_size), "\n");
markup.append(T T T "map mode=linear address=00:6000-7fff\n");
markup.append(T T T "map mode=linear address=30:6000-7fff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:4800-483f\n");
markup.append(T T T "map address=80-bf:4800-483f\n");
if(has_spc7110rtc) {
xml << " <rtc>\n";
xml << " <map address='00-3f:4840-4842'/>\n";
xml << " <map address='80-bf:4840-4842'/>\n";
xml << " </rtc>\n";
markup.append(T T "rtc\n");
markup.append(T T T "map address=00-3f:4840-4842\n");
markup.append(T T T "map address=80-bf:4840-4842\n");
}
xml << " <dcu>\n";
xml << " <map address='50:0000-ffff'/>\n";
xml << " </dcu>\n";
xml << " </spc7110>\n";
markup.append(T T "dcu\n");
markup.append(T T T "map address=50:0000-ffff\n");
} else if(mapper == LoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
xml << " </rom>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
markup.append(T T "map mode=linear address=f0-ff:0000-7fff\n");
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-ffff'/>\n";
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
markup.append(T T "map mode=linear address=f0-ff:0000-ffff\n");
}
xml << " </ram>\n";
}
} else if(mapper == HiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-3f:8000-ffff\n");
markup.append(T T "map mode=linear address=40-7f:0000-ffff\n");
markup.append(T T "map mode=shadow address=80-bf:8000-ffff\n");
markup.append(T T "map mode=linear address=c0-ff:0000-ffff\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
}
xml << " </ram>\n";
}
} else if(mapper == ExLoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " </rom>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-3f:8000-ffff\n");
markup.append(T T "map mode=linear address=40-7f:0000-ffff\n");
markup.append(T T "map mode=linear address=80-bf:8000-ffff\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " </ram>\n";
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
}
} else if(mapper == ExHiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-3f:8000-ffff' offset='400000'/>\n";
xml << " <map mode='linear' address='40-7f:0000-ffff' offset='400000'/>\n";
xml << " <map mode='shadow' address='80-bf:8000-ffff' offset='000000'/>\n";
xml << " <map mode='linear' address='c0-ff:0000-ffff' offset='000000'/>\n";
xml << " </rom>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-3f:8000-ffff offset=0x400000\n");
markup.append(T T "map mode=linear address=40-7f:0000-ffff offset=0x400000\n");
markup.append(T T "map mode=shadow address=80-bf:8000-ffff offset=0x000000\n");
markup.append(T T "map mode=linear address=c0-ff:0000-ffff offset=0x000000\n");
if(ram_size > 0) {
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
} else {
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
}
xml << " </ram>\n";
}
} else if(mapper == SuperFXROM) {
xml << " <superfx revision='2'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='00-3f:6000-7fff' size='2000'/>\n";
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
xml << " <map mode='linear' address='80-bf:6000-7fff' size='2000'/>\n";
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
xml << " </ram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:3000-32ff'/>\n";
xml << " <map address='80-bf:3000-32ff'/>\n";
xml << " </mmio>\n";
xml << " </superfx>\n";
markup.append(T "superfx revision=2\n");
markup.append(T T "rom\n");
markup.append(T T T "map mode=linear address=00-3f:8000-ffff\n");
markup.append(T T T "map mode=linear address=40-5f:0000-ffff\n");
markup.append(T T T "map mode=linear address=80-bf:8000-ffff\n");
markup.append(T T T "map mode=linear address=c0-df:0000-ffff\n");
markup.append(T T "ram size=0x", hex(ram_size), "\n");
markup.append(T T T "map mode=linear address=00-3f:6000-7fff size=0x2000\n");
markup.append(T T T "map mode=linear address=60-7f:0000-ffff\n");
markup.append(T T T "map mode=linear address=80-bf:6000-7fff size=0x2000\n");
markup.append(T T T "map mode=linear address=e0-ff:0000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:3000-32ff\n");
markup.append(T T T "map address=80-bf:3000-32ff\n");
} else if(mapper == SA1ROM) {
xml << " <sa1>\n";
xml << " <mcu>\n";
xml << " <rom>\n";
xml << " <map mode='direct' address='00-3f:8000-ffff'/>\n";
xml << " <map mode='direct' address='80-bf:8000-ffff'/>\n";
xml << " <map mode='direct' address='c0-ff:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram>\n";
xml << " <map mode='direct' address='00-3f:6000-7fff'/>\n";
xml << " <map mode='direct' address='80-bf:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " </mcu>\n";
xml << " <iram size='800'>\n";
xml << " <map mode='linear' address='00-3f:3000-37ff'/>\n";
xml << " <map mode='linear' address='80-bf:3000-37ff'/>\n";
xml << " </iram>\n";
xml << " <bwram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='40-4f:0000-ffff'/>\n";
xml << " </bwram>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:2200-23ff'/>\n";
xml << " <map address='80-bf:2200-23ff'/>\n";
xml << " </mmio>\n";
xml << " </sa1>\n";
markup.append(T "sa1\n");
markup.append(T T "mcu\n");
markup.append(T T T "rom\n");
markup.append(T T T T "map mode=direct address=00-3f:8000-ffff\n");
markup.append(T T T T "map mode=direct address=80-bf:8000-ffff\n");
markup.append(T T T T "map mode=direct address=c0-ff:0000-ffff\n");
markup.append(T T T "ram\n");
markup.append(T T T T "map mode=direct address=00-3f:6000-7fff\n");
markup.append(T T T T "map mode=direct address=80-bf:6000-7fff\n");
markup.append(T T "iram size=0x800\n");
markup.append(T T T "map mode=linear address=00-3f:3000-37ff\n");
markup.append(T T T "map mode=linear address=80-bf:3000-37ff\n");
markup.append(T T "bwram size=0x", hex(ram_size), "\n");
markup.append(T T T "map mode=linear address=40-4f:0000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:2200-23ff\n");
markup.append(T T T "map address=80-bf:2200-23ff\n");
} else if(mapper == BSCLoROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-1f:8000-ffff' offset='000000'/>\n";
xml << " <map mode='linear' address='20-3f:8000-ffff' offset='100000'/>\n";
xml << " <map mode='linear' address='80-9f:8000-ffff' offset='200000'/>\n";
xml << " <map mode='linear' address='a0-bf:8000-ffff' offset='100000'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
xml << " </ram>\n";
xml << " <bsx>\n";
xml << " <slot>\n";
xml << " <map mode='linear' address='c0-ef:0000-ffff'/>\n";
xml << " </slot>\n";
xml << " </bsx>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-1f:8000-ffff offset=0x000000\n");
markup.append(T T "map mode=linear address=20-3f:8000-ffff offset=0x100000\n");
markup.append(T T "map mode=linear address=80-9f:8000-ffff offset=0x200000\n");
markup.append(T T "map mode=linear address=a0-bf:8000-ffff offset=0x100000\n");
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
markup.append(T T "map mode=linear address=f0-ff:0000-7fff\n");
markup.append(T "bsx\n");
markup.append(T T "slot\n");
markup.append(T T T "map mode=linear address=c0-ef:0000-ffff\n");
} else if(mapper == BSCHiROM) {
xml << " <rom>\n";
xml << " <map mode='shadow' address='00-1f:8000-ffff'/>\n";
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='80-9f:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='" << hex(ram_size) << "'>\n";
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
xml << " </ram>\n";
xml << " <bsx>\n";
xml << " <slot>\n";
xml << " <map mode='shadow' address='20-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
xml << " <map mode='shadow' address='a0-bf:8000-ffff'/>\n";
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
xml << " </slot>\n";
xml << " </bsx>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=shadow address=00-1f:8000-ffff\n");
markup.append(T T "map mode=linear address=40-5f:0000-ffff\n");
markup.append(T T "map mode=shadow address=80-9f:8000-ffff\n");
markup.append(T T "map mode=linear address=c0-df:0000-ffff\n");
markup.append(T "ram size=0x", hex(ram_size), "\n");
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
markup.append(T "bsx\n");
markup.append(T T "slot\n");
markup.append(T T T "map mode=shadow address=20-3f:8000-ffff\n");
markup.append(T T T "map mode=linear address=60-7f:0000-ffff\n");
markup.append(T T T "map mode=shadow address=a0-bf:8000-ffff\n");
markup.append(T T T "map mode=linear address=e0-ff:0000-ffff\n");
} else if(mapper == BSXROM) {
xml << " <bsx>\n";
xml << " <mcu>\n";
xml << " <map address='00-3f:8000-ffff'/>\n";
xml << " <map address='80-bf:8000-ffff'/>\n";
xml << " <map address='40-7f:0000-ffff'/>\n";
xml << " <map address='c0-ff:0000-ffff'/>\n";
xml << " <map address='20-3f:6000-7fff'/>\n";
xml << " </mcu>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:5000-5fff'/>\n";
xml << " <map address='80-bf:5000-5fff'/>\n";
xml << " </mmio>\n";
xml << " </bsx>\n";
markup.append(T "bsx\n");
markup.append(T T "mcu\n");
markup.append(T T T "map address=00-3f:8000-ffff\n");
markup.append(T T T "map address=80-bf:8000-ffff\n");
markup.append(T T T "map address=40-7f:0000-ffff\n");
markup.append(T T T "map address=c0-ff:0000-ffff\n");
markup.append(T T T "map address=20-3f:6000-7fff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:5000-5fff\n");
markup.append(T T T "map address=80-bf:5000-5fff\n");
} else if(mapper == STROM) {
xml << " <rom>\n";
xml << " <map mode='linear' address='00-1f:8000-ffff'/>\n";
xml << " <map mode='linear' address='80-9f:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <sufamiturbo>\n";
xml << " <slot id='A'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='20-3f:8000-ffff'/>\n";
xml << " <map mode='linear' address='a0-bf:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='20000'>\n";
xml << " <map mode='linear' address='60-63:8000-ffff'/>\n";
xml << " <map mode='linear' address='e0-e3:8000-ffff'/>\n";
xml << " </ram>\n";
xml << " </slot>\n";
xml << " <slot id='B'>\n";
xml << " <rom>\n";
xml << " <map mode='linear' address='40-5f:8000-ffff'/>\n";
xml << " <map mode='linear' address='c0-df:8000-ffff'/>\n";
xml << " </rom>\n";
xml << " <ram size='20000'>\n";
xml << " <map mode='linear' address='70-73:8000-ffff'/>\n";
xml << " <map mode='linear' address='f0-f3:8000-ffff'/>\n";
xml << " </ram>\n";
xml << " </slot>\n";
xml << " </sufamiturbo>\n";
markup.append(T "rom\n");
markup.append(T T "map mode=linear address=00-1f:8000-ffff\n");
markup.append(T T "map mode=linear address=80-9f:8000-ffff\n");
markup.append(T "sufamiturbo\n");
markup.append(T T "slot id=A\n");
markup.append(T T T "rom\n");
markup.append(T T T T "map mode=linear address=20-3f:8000-ffff\n");
markup.append(T T T T "map mode=linear address=a0-bf:8000-ffff\n");
markup.append(T T T "ram size=0x20000\n");
markup.append(T T T T "map mode=linear address=60-63:8000-ffff\n");
markup.append(T T T T "map mode=linear address=e0-e3:8000-ffff\n");
markup.append(T T "slot id=B\n");
markup.append(T T T "rom\n");
markup.append(T T T T "map mode=linear address=40-5f:8000-ffff\n");
markup.append(T T T T "map mode=linear address=c0-df:8000-ffff\n");
markup.append(T T T "ram size=0x20000\n");
markup.append(T T T T "map mode=linear address=70-73:8000-ffff\n");
markup.append(T T T T "map mode=linear address=f0-f3:8000-ffff\n");
}
if(has_srtc) {
xml << " <srtc>\n";
xml << " <map address='00-3f:2800-2801'/>\n";
xml << " <map address='80-bf:2800-2801'/>\n";
xml << " </srtc>\n";
markup.append(T "srtc\n");
markup.append(T T "map address=00-3f:2800-2801\n");
markup.append(T T "map address=80-bf:2800-2801\n");
}
if(has_sdd1) {
xml << " <sdd1>\n";
xml << " <mcu>\n";
xml << " <map address='c0-ff:0000-ffff'/>\n";
xml << " </mcu>\n";
xml << " <mmio>\n";
xml << " <map address='00-3f:4800-4807'/>\n";
xml << " <map address='80-bf:4800-4807'/>\n";
xml << " </mmio>\n";
xml << " </sdd1>\n";
}
if(has_cx4) {
xml << " <cx4>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </cx4>\n";
markup.append(T "sdd1\n");
markup.append(T T "mcu\n");
markup.append(T T T "map address=c0-ff:0000-ffff\n");
markup.append(T T "mmio\n");
markup.append(T T T "map address=00-3f:4800-4807\n");
markup.append(T T T "map address=80-bf:4800-4807\n");
}
if(has_dsp1) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp1b.bin sha256=4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c\n");
if(dsp1_mapper == DSP1LoROM1MB) {
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
markup.append(T T "dr\n");
markup.append(T T T "map address=20-3f:8000-bfff\n");
markup.append(T T T "map address=a0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=20-3f:c000-ffff\n");
markup.append(T T T "map address=a0-bf:c000-ffff\n");
} else if(dsp1_mapper == DSP1LoROM2MB) {
xml << " <dr>\n";
xml << " <map address='60-6f:0000-3fff'/>\n";
xml << " <map address='e0-ef:0000-3fff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60-6f:4000-7fff'/>\n";
xml << " <map address='e0-ef:4000-7fff'/>\n";
xml << " </sr>\n";
markup.append(T T "dr\n");
markup.append(T T T "map address=60-6f:0000-3fff\n");
markup.append(T T T "map address=e0-ef:0000-3fff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=60-6f:4000-7fff\n");
markup.append(T T T "map address=e0-ef:4000-7fff\n");
} else if(dsp1_mapper == DSP1HiROM) {
xml << " <dr>\n";
xml << " <map address='00-1f:6000-6fff'/>\n";
xml << " <map address='80-9f:6000-6fff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='00-1f:7000-7fff'/>\n";
xml << " <map address='80-9f:7000-7fff'/>\n";
xml << " </sr>\n";
markup.append(T T "dr\n");
markup.append(T T T "map address=00-1f:6000-6fff\n");
markup.append(T T T "map address=80-9f:6000-6fff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=00-1f:7000-7fff\n");
markup.append(T T T "map address=80-9f:7000-7fff\n");
}
xml << " </necdsp>\n";
}
if(has_dsp2) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </necdsp>\n";
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp2.bin sha256=5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=20-3f:8000-bfff\n");
markup.append(T T T "map address=a0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=20-3f:c000-ffff\n");
markup.append(T T T "map address=a0-bf:c000-ffff\n");
}
if(has_dsp3) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
xml << " <dr>\n";
xml << " <map address='20-3f:8000-bfff'/>\n";
xml << " <map address='a0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='20-3f:c000-ffff'/>\n";
xml << " <map address='a0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </necdsp>\n";
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp3.bin sha256=2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=20-3f:8000-bfff\n");
markup.append(T T T "map address=a0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=20-3f:c000-ffff\n");
markup.append(T T T "map address=a0-bf:c000-ffff\n");
}
if(has_dsp4) {
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
xml << " <dr>\n";
xml << " <map address='30-3f:8000-bfff'/>\n";
xml << " <map address='b0-bf:8000-bfff'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='30-3f:c000-ffff'/>\n";
xml << " <map address='b0-bf:c000-ffff'/>\n";
xml << " </sr>\n";
xml << " </necdsp>\n";
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp4.bin sha256=63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=30-3f:8000-bfff\n");
markup.append(T T T "map address=b0-bf:8000-bfff\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=30-3f:c000-ffff\n");
markup.append(T T T "map address=b0-bf:c000-ffff\n");
}
if(has_obc1) {
xml << " <obc1>\n";
xml << " <map address='00-3f:6000-7fff'/>\n";
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </obc1>\n";
markup.append(T "obc1\n");
markup.append(T T "map address=00-3f:6000-7fff\n");
markup.append(T T "map address=80-bf:6000-7fff\n");
}
if(has_st010) {
xml << " <necdsp revision='upd96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
xml << " <dr>\n";
xml << " <map address='60:0000'/>\n";
xml << " <map address='e0:0000'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60:0001'/>\n";
xml << " <map address='e0:0001'/>\n";
xml << " </sr>\n";
xml << " <dp>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </dp>\n";
xml << " </necdsp>\n";
markup.append(T "necdsp model=uPD96050 frequency=10000000 firmware=st0010.bin sha256=55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=60:0000\n");
markup.append(T T T "map address=e0:0000\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=60:0001\n");
markup.append(T T T "map address=e0:0001\n");
markup.append(T T "dp\n");
markup.append(T T T "map address=68-6f:0000-0fff\n");
markup.append(T T T "map address=e8-ef:0000-0fff\n");
}
if(has_st011) {
xml << " <necdsp revision='upd96050' frequency='15000000' program='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n";
xml << " <dr>\n";
xml << " <map address='60:0000'/>\n";
xml << " <map address='e0:0000'/>\n";
xml << " </dr>\n";
xml << " <sr>\n";
xml << " <map address='60:0001'/>\n";
xml << " <map address='e0:0001'/>\n";
xml << " </sr>\n";
xml << " <dp>\n";
xml << " <map address='68-6f:0000-0fff'/>\n";
xml << " <map address='e8-ef:0000-0fff'/>\n";
xml << " </dp>\n";
xml << " </necdsp>\n";
markup.append(T "necdsp model=uPD96050 frequency=15000000 firmware=st0011.bin sha256=651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e\n");
markup.append(T T "dr\n");
markup.append(T T T "map address=60:0000\n");
markup.append(T T T "map address=e0:0000\n");
markup.append(T T "sr\n");
markup.append(T T T "map address=60:0001\n");
markup.append(T T T "map address=e0:0001\n");
markup.append(T T "dp\n");
markup.append(T T T "map address=68-6f:0000-0fff\n");
markup.append(T T T "map address=e8-ef:0000-0fff\n");
}
if(has_st018) {
xml << " <setarisc program='ST-0018'>\n";
xml << " <map address='00-3f:3800-38ff'/>\n";
xml << " <map address='80-bf:3800-38ff'/>\n";
xml << " </setarisc>\n";
markup.append(T "setarisc firmware=ST-0018\n");
markup.append(T T "map address=00-3f:3800-38ff\n");
markup.append(T T "map address=80-bf:3800-38ff\n");
}
xml << "</cartridge>\n";
xmlMemoryMap = xml.transform("'", "\"");
}
void SNESCartridge::read_header(const uint8_t *data, unsigned size) {
#undef T
void SnesCartridge::read_header(const uint8_t *data, unsigned size) {
type = TypeUnknown;
mapper = LoROM;
dsp1_mapper = DSP1Unmapped;
@@ -758,7 +672,7 @@ void SNESCartridge::read_header(const uint8_t *data, unsigned size) {
}
}
unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) {
unsigned SnesCartridge::find_header(const uint8_t *data, unsigned size) {
unsigned score_lo = score_header(data, size, 0x007fc0);
unsigned score_hi = score_header(data, size, 0x00ffc0);
unsigned score_ex = score_header(data, size, 0x40ffc0);
@@ -773,7 +687,7 @@ unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) {
}
}
unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
unsigned SnesCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
if(size < addr + 64) return 0; //image too small to contain header at this location?
int score = 0;
@@ -854,7 +768,7 @@ unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigne
return score;
}
unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
unsigned SnesCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
if(size < 512) return 0;
switch(data[0x0149]) {
case 0x00: return 0 * 1024;
@@ -867,7 +781,7 @@ unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
}
}
bool SNESCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
bool SnesCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
if(size < 512) return false;
if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true;
return false;

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

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

View File

@@ -1,16 +1,34 @@
#ifndef NALL_STRING_HPP
#define NALL_STRING_HPP
#include <initializer_list>
#include <nall/platform.hpp>
#include <nall/utility.hpp>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <initializer_list>
#include <nall/array.hpp>
#include <nall/atoi.hpp>
#include <nall/function.hpp>
#include <nall/platform.hpp>
#include <nall/sha256.hpp>
#include <nall/stdint.hpp>
#include <nall/utility.hpp>
#include <nall/vector.hpp>
#include <nall/windows/utf8.hpp>
#define NALL_STRING_INTERNAL_HPP
#include <nall/string/base.hpp>
#include <nall/string/bml.hpp>
#include <nall/string/bsv.hpp>
#include <nall/string/core.hpp>
#include <nall/string/cast.hpp>
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/cstring.hpp>
#include <nall/string/filename.hpp>
#include <nall/string/math.hpp>
#include <nall/string/platform.hpp>
@@ -23,10 +41,6 @@
#include <nall/string/variadic.hpp>
#include <nall/string/wrapper.hpp>
#include <nall/string/xml.hpp>
namespace nall {
template<> struct has_length<string> { enum { value = true }; };
template<> struct has_size<lstring> { enum { value = true }; };
}
#undef NALL_STRING_INTERNAL_HPP
#endif

View File

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

151
bsnes/nall/string/bml.hpp Executable file
View File

@@ -0,0 +1,151 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BML v1.0 parser
//revision 0.05
namespace nall {
namespace BML {
inline static string indent(const char *s, unsigned depth) {
array<char> output;
do {
for(unsigned n = 0; n < depth; n++) output.append('\t');
do output.append(*s); while(*s && *s++ != '\n');
} while(*s);
return output.get();
}
struct Node {
cstring name;
cstring value;
private:
linear_vector<Node> children;
inline bool valid(char p) const { //A-Za-z0-9-.
return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u;
}
inline unsigned parseDepth(char *&p) {
while(*p == '\n' || *p == '#') {
while(*p != '\n') *p++ = 0;
*p++ = 0; //'\n'
}
unsigned depth = 0;
while(p[depth] == '\t') depth++;
return depth;
}
inline void parseName(char *&p) {
if(valid(*p) == false) throw "Missing node name";
name = p;
while(valid(*p)) p++;
}
inline void parseValue(char *&p) {
char terminal = *p == ':' ? '\n' : ' '; //':' or '='
*p++ = 0;
value = p;
while(*p && *p != terminal && *p != '\n') p++;
}
inline void parseBlock(char *&p, unsigned depth) {
value = p;
char *w = p;
while(parseDepth(p) > depth) {
p += depth + 1;
while(*p && *p != '\n') *w++ = *p++;
if(*p && *p != '\n') throw "Multi-line value missing line feed";
*w++ = *p;
}
*(w - 1) = 0; //'\n'
}
inline void parseLine(char *&p) {
unsigned depth = parseDepth(p);
while(*p == '\t') p++;
parseName(p);
bool multiLine = *p == '~';
if(multiLine) *p++ = 0;
else if(*p == ':' || *p == '=') parseValue(p);
if(*p && *p != ' ' && *p != '\n') throw "Invalid character encountered";
while(*p == ' ') {
*p++ = 0;
Node node;
node.parseName(p);
if(*p == ':' || *p == '=') node.parseValue(p);
if(*p && *p != ' ' && *p != '\n') throw "Invalid character after node";
if(*p == '\n') *p++ = 0;
children.append(node);
}
if(multiLine) return parseBlock(p, depth);
while(parseDepth(p) > depth) {
Node node;
node.parseLine(p);
children.append(node);
}
}
inline void parse(char *&p) {
while(*p) {
Node node;
node.parseLine(p);
children.append(node);
}
}
public:
inline Node& operator[](const char *name) {
for(auto &node : children) {
if(node.name == name) return node;
}
static Node node;
node.name = nullptr;
return node;
}
inline bool exists() const { return name; }
unsigned size() const { return children.size(); }
Node* begin() { return children.begin(); }
Node* end() { return children.end(); }
const Node* begin() const { return children.begin(); }
const Node* end() const { return children.end(); }
inline Node() : name(""), value("") {}
friend class Document;
};
struct Document : Node {
cstring error;
inline bool load(const char *document) {
if(document == nullptr) return false;
this->document = strdup(document);
char *p = this->document;
try {
this->error = nullptr;
parse(p);
} catch(const char *error) {
this->error = error;
free(this->document);
this->document = nullptr;
children.reset();
return false;
}
return true;
}
inline Document(const char *document = "") : document(nullptr), error(nullptr) { if(*document) load(document); }
inline ~Document() { if(document) free(document); }
private:
char *document;
};
}
}
#endif

View File

@@ -1,74 +1,75 @@
#ifndef NALL_STRING_BSV_HPP
#define NALL_STRING_BSV_HPP
#ifdef NALL_STRING_INTERNAL_HPP
//BSV parser
//version 0.01
//BSV v1.0 parser
//revision 0.02
namespace nall {
inline string bsv_decode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '}' ) return "";
if(*input == '\r') return "";
if(*input == '\n') return "";
struct BSV {
static inline string decode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '}' ) return "";
if(*input == '\r') return "";
if(*input == '\n') return "";
//normal characters
if(*input != '{') { output[offset++] = *input++; continue; }
//normal characters
if(*input != '{') { output[offset++] = *input++; continue; }
//entities
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
//entities
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
//illegal entities
return "";
//illegal entities
return "";
}
output[offset] = 0;
return output;
}
output[offset] = 0;
return output;
}
inline string bsv_encode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '\r') return "";
static inline string encode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '\r') return "";
if(*input == '\n') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'f';
output[offset++] = '}';
input++;
continue;
if(*input == '\n') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'f';
output[offset++] = '}';
input++;
continue;
}
if(*input == '{') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
if(*input == '}') {
output[offset++] = '{';
output[offset++] = 'r';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
output[offset++] = *input++;
}
if(*input == '{') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
if(*input == '}') {
output[offset++] = '{';
output[offset++] = 'r';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
output[offset++] = *input++;
output[offset] = 0;
return output;
}
output[offset] = 0;
return output;
}
};
}

View File

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

View File

@@ -1,5 +1,4 @@
#ifndef NALL_STRING_COMPARE_HPP
#define NALL_STRING_COMPARE_HPP
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
@@ -11,7 +10,7 @@ char chrupper(char c) {
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
}
int stricmp(const char *str1, const char *str2) {
int istrcmp(const char *str1, const char *str2) {
while(*str1) {
if(chrlower(*str1) != chrlower(*str2)) break;
str1++, str2++;
@@ -66,7 +65,7 @@ bool strbegin(const char *str, const char *key) {
return (!memcmp(str, key, ksl));
}
bool stribegin(const char *str, const char *key) {
bool istrbegin(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
@@ -89,7 +88,7 @@ bool strend(const char *str, const char *key) {
return (!memcmp(str + ssl - ksl, key, ksl));
}
bool striend(const char *str, const char *key) {
bool istrend(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;

View File

@@ -1,5 +1,4 @@
#ifndef NALL_STRING_CONVERT_HPP
#define NALL_STRING_CONVERT_HPP
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
@@ -23,6 +22,26 @@ char* strupper(char *str) {
return str;
}
char* qstrlower(char *s) {
if(!s) return 0;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
s++;
}
}
char* qstrupper(char *s) {
if(!s) return 0;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
s++;
}
}
char* strtr(char *dest, const char *before, const char *after) {
if(!dest || !before || !after) return dest;
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
@@ -40,86 +59,6 @@ char* strtr(char *dest, const char *before, const char *after) {
return dest;
}
uintmax_t hex(const char *str) {
if(!str) return 0;
uintmax_t result = 0;
//skip hex identifiers 0x and $, if present
if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2;
else if(*str == '$') str++;
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else if(x >= 'A' && x <= 'F') x -= 'A' - 10;
else if(x >= 'a' && x <= 'f') x -= 'a' - 10;
else break; //stop at first invalid character
result = result * 16 + x;
}
return result;
}
intmax_t integer(const char *str) {
if(!str) return 0;
intmax_t result = 0;
bool negate = false;
//check for sign
if(*str == '+') {
negate = false;
str++;
} else if(*str == '-') {
negate = true;
str++;
}
while(*str) {
uint8_t x = *str++;
if(x >= '0' && x <= '9') x -= '0';
else break; //stop at first invalid character
result = result * 10 + x;
}
return !negate ? result : -result;
}
uintmax_t decimal(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 binary(const char *str) {
if(!str) return 0;
uintmax_t result = 0;
//skip bin identifiers 0b and %, if present
if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2;
else if(*str == '%') str++;
while(*str) {
uint8_t x = *str++;
if(x == '0' || x == '1') x -= '0';
else break; //stop at first invalid character
result = result * 2 + x;
}
return result;
}
double fp(const char *str) {
return atof(str);
}
}
#endif

View File

@@ -1,5 +1,4 @@
#ifndef NALL_STRING_CORE_HPP
#define NALL_STRING_CORE_HPP
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
@@ -66,15 +65,17 @@ bool string::operator> (const char *str) const { return strcmp(data, str) > 0;
bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; }
string& string::operator=(const string &value) {
if(&value == this) return *this;
assign(value);
return *this;
}
string& string::operator=(string &&source) {
if(&source == this) return *this;
if(data) free(data);
size = source.size;
data = source.data;
source.data = 0;
source.data = nullptr;
source.size = 0;
return *this;
}
@@ -87,14 +88,16 @@ template<typename... Args> string::string(Args&&... args) {
}
string::string(const string &value) {
if(&value == this) return;
size = strlen(value);
data = strdup(value);
}
string::string(string &&source) {
if(&source == this) return;
size = source.size;
data = source.data;
source.data = 0;
source.data = nullptr;
}
string::~string() {
@@ -131,6 +134,19 @@ optional<unsigned> lstring::find(const char *key) const {
return { false, 0 };
}
bool lstring::operator==(const lstring &source) const {
if(this == &source) return true;
if(size() != source.size()) return false;
for(unsigned n = 0; n < size(); n++) {
if(operator[](n) != source[n]) return false;
}
return true;
}
bool lstring::operator!=(const lstring &source) const {
return !operator==(source);
}
inline lstring::lstring() {
}

21
bsnes/nall/string/cstring.hpp Executable file
View File

@@ -0,0 +1,21 @@
#ifdef NALL_STRING_INTERNAL_HPP
//const string:
//bind a const char* pointer to an object that has various testing functionality;
//yet lacks the memory allocation and modification functionality of the string class
namespace nall {
cstring::operator const char*() const { return data; }
unsigned cstring::length() const { return strlen(data); }
bool cstring::operator==(const char *s) const { return !strcmp(data, s); }
bool cstring::operator!=(const char *s) const { return strcmp(data, s); }
optional<unsigned> cstring::position (const char *key) const { return strpos(data, key); }
optional<unsigned> cstring::iposition(const char *key) const { return istrpos(data, key); }
cstring& cstring::operator=(const char *data) { this->data = data; return *this; }
cstring::cstring(const char *data) : data(data) {}
cstring::cstring() : data("") {}
}
#endif

View File

@@ -1,5 +1,4 @@
#ifndef NALL_FILENAME_HPP
#define NALL_FILENAME_HPP
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {

View File

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

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