2016-01-07 08:14:33 +00:00
|
|
|
#pragma once
|
2014-01-13 09:35:46 +00:00
|
|
|
|
|
|
|
namespace nall {
|
|
|
|
|
Update to bsnes v107.1 release.
byuu says:
Don't let the point release fool you, there are many significant changes in this
release. I will be keeping bsnes releases using a point system until the new
higan release is ready.
Changelog:
- GUI: added high DPI support
- GUI: fixed the state manager image preview
- Windows: added a new waveOut driver with support for dynamic rate control
- Windows: corrected the XAudio 2.1 dynamic rate control support [BearOso]
- Windows: corrected the Direct3D 9.0 fullscreen exclusive window centering
- Windows: fixed XInput controller support on Windows 10
- SFC: added high-level emulation for the DSP1, DSP2, DSP4, ST010, and Cx4
coprocessors
- SFC: fixed a slight rendering glitch in the intro to Megalomania
If the coprocessor firmware is missing, bsnes will fallback on HLE where it is
supported, which is everything other than SD Gundam GX and the two Hayazashi
Nidan Morita Shougi games.
The Windows dynamic rate control works best with Direct3D in fullscreen
exclusive mode. I recommend the waveOut driver over the XAudio 2.1 driver, as it
is not possible to target a single XAudio2 version on all Windows OS releases.
The waveOut driver should work everywhere out of the box.
Note that with DRC, the synchronization source is your monitor, so you will
want to be running at 60hz (NTSC) or 50hz (PAL). If you have an adaptive sync
monitor, you should instead use the WASAPI (exclusive) or ASIO audio driver.
2019-04-09 01:16:30 +00:00
|
|
|
//scan all four sides of the image for fully transparent pixels, and then crop them
|
|
|
|
//imagine an icon centered on a transparent background: this function removes the bordering
|
|
|
|
//this certainly won't win any speed awards, but nall::image is meant to be correct and simple, not fast
|
|
|
|
auto image::shrink(uint64_t transparentColor) -> void {
|
|
|
|
//top
|
|
|
|
{ uint padding = 0;
|
|
|
|
for(uint y : range(_height)) {
|
|
|
|
const uint8_t* sp = _data + pitch() * y;
|
|
|
|
bool found = false;
|
|
|
|
for(uint x : range(_width)) {
|
|
|
|
if(read(sp) != transparentColor) { found = true; break; }
|
|
|
|
sp += stride();
|
|
|
|
}
|
|
|
|
if(found) break;
|
|
|
|
padding++;
|
|
|
|
}
|
|
|
|
crop(0, padding, _width, _height - padding);
|
|
|
|
}
|
|
|
|
|
|
|
|
//bottom
|
|
|
|
{ uint padding = 0;
|
|
|
|
for(uint y : reverse(range(_height))) {
|
|
|
|
const uint8_t* sp = _data + pitch() * y;
|
|
|
|
bool found = false;
|
|
|
|
for(uint x : range(_width)) {
|
|
|
|
if(read(sp) != transparentColor) { found = true; break; }
|
|
|
|
sp += stride();
|
|
|
|
}
|
|
|
|
if(found) break;
|
|
|
|
padding++;
|
|
|
|
}
|
|
|
|
crop(0, 0, _width, _height - padding);
|
|
|
|
}
|
|
|
|
|
|
|
|
//left
|
|
|
|
{ uint padding = 0;
|
|
|
|
for(uint x : range(_width)) {
|
|
|
|
const uint8_t* sp = _data + stride() * x;
|
|
|
|
bool found = false;
|
|
|
|
for(uint y : range(_height)) {
|
|
|
|
if(read(sp) != transparentColor) { found = true; break; }
|
|
|
|
sp += pitch();
|
|
|
|
}
|
|
|
|
if(found) break;
|
|
|
|
padding++;
|
|
|
|
}
|
|
|
|
crop(padding, 0, _width - padding, _height);
|
|
|
|
}
|
|
|
|
|
|
|
|
//right
|
|
|
|
{ uint padding = 0;
|
|
|
|
for(uint x : reverse(range(_width))) {
|
|
|
|
const uint8_t* sp = _data + stride() * x;
|
|
|
|
bool found = false;
|
|
|
|
for(uint y : range(_height)) {
|
|
|
|
if(read(sp) != transparentColor) { found = true; break; }
|
|
|
|
sp += pitch();
|
|
|
|
}
|
|
|
|
if(found) break;
|
|
|
|
padding++;
|
|
|
|
}
|
|
|
|
crop(0, 0, _width - padding, _height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
auto image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsigned outputHeight) -> bool {
|
|
|
|
if(outputX + outputWidth > _width) return false;
|
|
|
|
if(outputY + outputHeight > _height) return false;
|
2014-01-13 09:35:46 +00:00
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
uint8_t* outputData = allocate(outputWidth, outputHeight, stride());
|
|
|
|
unsigned outputPitch = outputWidth * stride();
|
2014-01-13 09:35:46 +00:00
|
|
|
|
|
|
|
for(unsigned y = 0; y < outputHeight; y++) {
|
2015-06-18 10:48:53 +00:00
|
|
|
const uint8_t* sp = _data + pitch() * (outputY + y) + stride() * outputX;
|
2014-01-13 09:35:46 +00:00
|
|
|
uint8_t* dp = outputData + outputPitch * y;
|
|
|
|
for(unsigned x = 0; x < outputWidth; x++) {
|
|
|
|
write(dp, read(sp));
|
2015-06-18 10:48:53 +00:00
|
|
|
sp += stride();
|
|
|
|
dp += stride();
|
2014-01-13 09:35:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
delete[] _data;
|
|
|
|
_data = outputData;
|
|
|
|
_width = outputWidth;
|
|
|
|
_height = outputHeight;
|
2014-01-13 09:35:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
auto image::alphaBlend(uint64_t alphaColor) -> void {
|
|
|
|
uint64_t alphaR = (alphaColor & _red.mask() ) >> _red.shift();
|
|
|
|
uint64_t alphaG = (alphaColor & _green.mask()) >> _green.shift();
|
|
|
|
uint64_t alphaB = (alphaColor & _blue.mask() ) >> _blue.shift();
|
2014-01-13 09:35:46 +00:00
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
for(unsigned y = 0; y < _height; y++) {
|
|
|
|
uint8_t* dp = _data + pitch() * y;
|
|
|
|
for(unsigned x = 0; x < _width; x++) {
|
2014-01-13 09:35:46 +00:00
|
|
|
uint64_t color = read(dp);
|
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
uint64_t colorA = (color & _alpha.mask()) >> _alpha.shift();
|
|
|
|
uint64_t colorR = (color & _red.mask() ) >> _red.shift();
|
|
|
|
uint64_t colorG = (color & _green.mask()) >> _green.shift();
|
|
|
|
uint64_t colorB = (color & _blue.mask() ) >> _blue.shift();
|
|
|
|
double alphaScale = (double)colorA / (double)((1 << _alpha.depth()) - 1);
|
2014-01-13 09:35:46 +00:00
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
colorA = (1 << _alpha.depth()) - 1;
|
2014-01-13 09:35:46 +00:00
|
|
|
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
|
|
|
|
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
|
|
|
|
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
|
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
write(dp, (colorA << _alpha.shift()) | (colorR << _red.shift()) | (colorG << _green.shift()) | (colorB << _blue.shift()));
|
|
|
|
dp += stride();
|
2014-01-13 09:35:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Update to v094r43 release.
byuu says:
Updated to compile with all of the new hiro changes. My next step is to
write up hiro API documentation, and move the API from alpha (constantly
changing) to beta (rarely changing), in preparation for the first stable
release (backward-compatible changes only.)
Added "--fullscreen" command-line option. I like this over
a configuration file option. Lets you use the emulator in both modes
without having to modify the config file each time.
Also enhanced the command-line game loading. You can now use any of
these methods:
higan /path/to/game-folder.sfc
higan /path/to/game-folder.sfc/
higan /path/to/game-folder.sfc/program.rom
The idea is to support launchers that insist on loading files only.
Technically, the file can be any name (manifest.bml also works); the
only criteria is that the file actually exists and is a file, and not
a directory. This is a requirement to support the first version (a
directory lacking the trailing / identifier), because I don't want my
nall::string class to query the file system to determine if the string
is an actual existing file or directory for its pathname() / dirname()
functions.
Anyway, every game folder I've made so far has program.rom, and that's
very unlikely to change, so this should be fine.
Now, of course, if you drop a regular "game.sfc" file on the emulator,
it won't even try to load it, unless it's in a folder that ends in .fc,
.sfc, etc. In which case, it'll bail out immediately by being unable to
produce a manifest for what is obviously not really a game folder.
2015-08-30 02:08:26 +00:00
|
|
|
auto image::alphaMultiply() -> void {
|
|
|
|
unsigned divisor = (1 << _alpha.depth()) - 1;
|
|
|
|
|
|
|
|
for(unsigned y = 0; y < _height; y++) {
|
|
|
|
uint8_t* dp = _data + pitch() * y;
|
|
|
|
for(unsigned x = 0; x < _width; x++) {
|
|
|
|
uint64_t color = read(dp);
|
|
|
|
|
|
|
|
uint64_t colorA = (color & _alpha.mask()) >> _alpha.shift();
|
|
|
|
uint64_t colorR = (color & _red.mask() ) >> _red.shift();
|
|
|
|
uint64_t colorG = (color & _green.mask()) >> _green.shift();
|
|
|
|
uint64_t colorB = (color & _blue.mask() ) >> _blue.shift();
|
|
|
|
|
|
|
|
colorR = (colorR * colorA) / divisor;
|
|
|
|
colorG = (colorG * colorA) / divisor;
|
|
|
|
colorB = (colorB * colorA) / divisor;
|
|
|
|
|
|
|
|
write(dp, (colorA << _alpha.shift()) | (colorR << _red.shift()) | (colorG << _green.shift()) | (colorB << _blue.shift()));
|
|
|
|
dp += stride();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
auto image::transform(const image& source) -> void {
|
|
|
|
return transform(source._endian, source._depth, source._alpha.mask(), source._red.mask(), source._green.mask(), source._blue.mask());
|
|
|
|
}
|
|
|
|
|
|
|
|
auto image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) -> void {
|
|
|
|
if(_endian == outputEndian && _depth == outputDepth && _alpha.mask() == outputAlphaMask && _red.mask() == outputRedMask && _green.mask() == outputGreenMask && _blue.mask() == outputBlueMask) return;
|
2014-01-13 09:35:46 +00:00
|
|
|
|
|
|
|
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
|
2015-06-18 10:48:53 +00:00
|
|
|
output.allocate(_width, _height);
|
2014-01-13 09:35:46 +00:00
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
for(unsigned y = 0; y < _height; y++) {
|
|
|
|
const uint8_t* sp = _data + pitch() * y;
|
|
|
|
uint8_t* dp = output._data + output.pitch() * y;
|
|
|
|
for(unsigned x = 0; x < _width; x++) {
|
2014-01-13 09:35:46 +00:00
|
|
|
uint64_t color = read(sp);
|
2015-06-18 10:48:53 +00:00
|
|
|
sp += stride();
|
2014-01-13 09:35:46 +00:00
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
uint64_t a = (color & _alpha.mask()) >> _alpha.shift();
|
|
|
|
uint64_t r = (color & _red.mask() ) >> _red.shift();
|
|
|
|
uint64_t g = (color & _green.mask()) >> _green.shift();
|
|
|
|
uint64_t b = (color & _blue.mask() ) >> _blue.shift();
|
2014-01-13 09:35:46 +00:00
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
a = normalize(a, _alpha.depth(), output._alpha.depth());
|
|
|
|
r = normalize(r, _red.depth(), output._red.depth());
|
|
|
|
g = normalize(g, _green.depth(), output._green.depth());
|
|
|
|
b = normalize(b, _blue.depth(), output._blue.depth());
|
2014-01-13 09:35:46 +00:00
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
output.write(dp, (a << output._alpha.shift()) | (r << output._red.shift()) | (g << output._green.shift()) | (b << output._blue.shift()));
|
|
|
|
dp += output.stride();
|
2014-01-13 09:35:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:48:53 +00:00
|
|
|
operator=(move(output));
|
2014-01-13 09:35:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|