mirror of https://github.com/bsnes-emu/bsnes.git
Update to v097r01 release.
byuu says: A minor WIP to get us started. Changelog: - System::Video merged to PPU::Video - System::Audio merged to DSP::Audio - System::Configuration merged to Interface::Settings - created emulator/emulator.cpp and accompanying object file for shared code between all cores Currently, emulator.cpp just holds a videoColor() function that takes R16G16B16, performs gamma/saturation/luma adjust, and outputs (currently) A8R8G8B8. It's basically an internal function call for cores to use when generating palette entries. This code used to exist inside ui-tomoko/program/interface.cpp, but we have to move it internal for software display emulation. But in the future, we could add other useful cross-core functionality here.
This commit is contained in:
parent
1fdd0582fc
commit
f1ebef2ea8
|
@ -0,0 +1,3 @@
|
|||
objects += emulator
|
||||
|
||||
obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator/)
|
|
@ -0,0 +1,34 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
auto Interface::videoColor(uint16 r, uint16 g, uint16 b) -> uint32 {
|
||||
double saturation = 1.0;
|
||||
double gamma = 1.0;
|
||||
double luminance = 1.0;
|
||||
|
||||
if(saturation != 1.0) {
|
||||
uint16 grayscale = uclamp<16>((r + g + b) / 3);
|
||||
double inverse = max(0.0, 1.0 - saturation);
|
||||
r = uclamp<16>(r * saturation + grayscale * inverse);
|
||||
g = uclamp<16>(g * saturation + grayscale * inverse);
|
||||
b = uclamp<16>(b * saturation + grayscale * inverse);
|
||||
}
|
||||
|
||||
if(gamma != 1.0) {
|
||||
double reciprocal = 1.0 / 32767.0;
|
||||
r = r > 32767 ? r : 32767 * pow(r * reciprocal, gamma);
|
||||
g = g > 32767 ? g : 32767 * pow(g * reciprocal, gamma);
|
||||
b = b > 32767 ? b : 32767 * pow(b * reciprocal, gamma);
|
||||
}
|
||||
|
||||
if(luminance != 1.0) {
|
||||
r = uclamp<16>(r * luminance);
|
||||
g = uclamp<16>(g * luminance);
|
||||
b = uclamp<16>(b * luminance);
|
||||
}
|
||||
|
||||
return 255 << 24 | (r >> 8) << 16 | (g >> 8) << 8 | (b >> 8) << 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "097";
|
||||
static const string Version = "097.01";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -108,6 +108,9 @@ struct Interface {
|
|||
virtual auto cap(const string& name) -> bool { return false; }
|
||||
virtual auto get(const string& name) -> any { return {}; }
|
||||
virtual auto set(const string& name, const any& value) -> bool { return false; }
|
||||
|
||||
//shared functions
|
||||
auto videoColor(uint16 r, uint16 g, uint16 b) -> uint32;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
#ifndef PROCESSOR_HPP
|
||||
#define PROCESSOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,7 +21,7 @@ auto ICD2::enter() -> void {
|
|||
step(GameBoy::system.clocks_executed);
|
||||
GameBoy::system.clocks_executed = 0;
|
||||
} else { //DMG halted
|
||||
audio.coprocessor_sample(0x0000, 0x0000);
|
||||
dsp.audio.coprocessorSample(0, 0);
|
||||
step(1);
|
||||
}
|
||||
synchronizeCPU();
|
||||
|
@ -44,8 +44,8 @@ auto ICD2::unload() -> void {
|
|||
}
|
||||
|
||||
auto ICD2::power() -> void {
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(2 * 1024 * 1024);
|
||||
dsp.audio.coprocessorEnable(true);
|
||||
dsp.audio.coprocessorFrequency(2 * 1024 * 1024);
|
||||
}
|
||||
|
||||
auto ICD2::reset() -> void {
|
||||
|
|
|
@ -93,7 +93,7 @@ auto ICD2::videoRefresh(const uint32* data, uint pitch, uint width, uint height)
|
|||
}
|
||||
|
||||
auto ICD2::audioSample(int16 left, int16 right) -> void {
|
||||
audio.coprocessor_sample(left, right);
|
||||
dsp.audio.coprocessorSample(left, right);
|
||||
}
|
||||
|
||||
auto ICD2::inputPoll(uint port, uint device, uint id) -> int16 {
|
||||
|
|
|
@ -41,7 +41,7 @@ auto MSU1::enter() -> void {
|
|||
right = sclamp<16>(rchannel);
|
||||
if(dsp.mute()) left = 0, right = 0;
|
||||
|
||||
audio.coprocessor_sample(left, right);
|
||||
dsp.audio.coprocessorSample(left, right);
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
@ -59,8 +59,8 @@ auto MSU1::unload() -> void {
|
|||
}
|
||||
|
||||
auto MSU1::power() -> void {
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(44100.0);
|
||||
dsp.audio.coprocessorEnable(true);
|
||||
dsp.audio.coprocessorFrequency(44100.0);
|
||||
}
|
||||
|
||||
auto MSU1::reset() -> void {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
auto DSP::Audio::coprocessorEnable(bool enable) -> void {
|
||||
mixer.clear();
|
||||
mixerEnable = enable;
|
||||
dsp.read = dsp.write = 0;
|
||||
mix.read = mix.write = 0;
|
||||
}
|
||||
|
||||
auto DSP::Audio::coprocessorFrequency(double frequency) -> void {
|
||||
mixer.setFrequency(frequency);
|
||||
mixer.setResampler(nall::DSP::ResampleEngine::Sinc);
|
||||
mixer.setResamplerFrequency(system.apuFrequency() / 768.0);
|
||||
}
|
||||
|
||||
auto DSP::Audio::sample(int16 left, int16 right) -> void {
|
||||
if(!mixerEnable) return interface->audioSample(left, right);
|
||||
|
||||
dsp.left[dsp.write] = left;
|
||||
dsp.right[dsp.write] = right;
|
||||
dsp.write++;
|
||||
flush();
|
||||
}
|
||||
|
||||
auto DSP::Audio::coprocessorSample(int16 left, int16 right) -> void {
|
||||
int samples[] = {left, right};
|
||||
mixer.sample(samples);
|
||||
while(mixer.pending()) {
|
||||
mixer.read(samples);
|
||||
mix.left[mix.write] = samples[0];
|
||||
mix.right[mix.write] = samples[1];
|
||||
mix.write++;
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
auto DSP::Audio::flush() -> void {
|
||||
while(dsp.read != dsp.write && mix.read != mix.write) {
|
||||
interface->audioSample(
|
||||
sclamp<16>(dsp.left[dsp.read] + mix.left[mix.read]),
|
||||
sclamp<16>(dsp.right[dsp.read] + mix.right[mix.read])
|
||||
);
|
||||
dsp.read++;
|
||||
mix.read++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
struct Audio {
|
||||
auto sample(int16 left, int16 right) -> void;
|
||||
|
||||
auto coprocessorEnable(bool enable) -> void;
|
||||
auto coprocessorFrequency(double frequency) -> void;
|
||||
auto coprocessorSample(int16 left, int16 right) -> void;
|
||||
auto flush() -> void;
|
||||
|
||||
private:
|
||||
nall::DSP mixer;
|
||||
bool mixerEnable = false;
|
||||
struct Buffer {
|
||||
int16 left[256];
|
||||
int16 right[256];
|
||||
uint8 read;
|
||||
uint8 write;
|
||||
} dsp, mix;
|
||||
};
|
|
@ -7,7 +7,6 @@ DSP dsp;
|
|||
#define REG(n) state.regs[n]
|
||||
#define VREG(n) state.regs[v.vidx + n]
|
||||
|
||||
#include "serialization.cpp"
|
||||
#include "gaussian.cpp"
|
||||
#include "counter.cpp"
|
||||
#include "envelope.cpp"
|
||||
|
@ -15,6 +14,8 @@ DSP dsp;
|
|||
#include "misc.cpp"
|
||||
#include "voice.cpp"
|
||||
#include "echo.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "audio.cpp"
|
||||
|
||||
DSP::DSP() {
|
||||
static_assert(sizeof(signed) >= 32 / 8, "signed >= 32-bits");
|
||||
|
@ -283,6 +284,8 @@ auto DSP::power() -> void {
|
|||
voice[n].hiddenEnvelope = 0;
|
||||
voice[n]._envxOut = 0;
|
||||
}
|
||||
|
||||
audio.coprocessorEnable(false);
|
||||
}
|
||||
|
||||
auto DSP::reset() -> void {
|
||||
|
|
|
@ -5,7 +5,7 @@ struct DSP : Thread {
|
|||
|
||||
DSP();
|
||||
|
||||
alwaysinline auto step(unsigned clocks) -> void;
|
||||
alwaysinline auto step(uint clocks) -> void;
|
||||
alwaysinline auto synchronizeSMP() -> void;
|
||||
|
||||
auto mute() const -> bool;
|
||||
|
@ -18,10 +18,13 @@ struct DSP : Thread {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
#include "audio.hpp"
|
||||
Audio audio;
|
||||
|
||||
privileged:
|
||||
#include "modulo-array.hpp"
|
||||
|
||||
enum GlobalRegister : unsigned {
|
||||
enum GlobalRegister : uint {
|
||||
MVOLL = 0x0c, MVOLR = 0x1c,
|
||||
EVOLL = 0x2c, EVOLR = 0x3c,
|
||||
KON = 0x4c, KOFF = 0x5c,
|
||||
|
@ -32,7 +35,7 @@ privileged:
|
|||
EDL = 0x7d, FIR = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f
|
||||
};
|
||||
|
||||
enum VoiceRegister : unsigned {
|
||||
enum VoiceRegister : uint {
|
||||
VOLL = 0x00, VOLR = 0x01,
|
||||
PITCHL = 0x02, PITCHH = 0x03,
|
||||
SRCN = 0x04, ADSR0 = 0x05,
|
||||
|
@ -40,14 +43,14 @@ privileged:
|
|||
ENVX = 0x08, OUTX = 0x09,
|
||||
};
|
||||
|
||||
enum EnvelopeMode : unsigned {
|
||||
enum EnvelopeMode : uint {
|
||||
EnvelopeRelease,
|
||||
EnvelopeAttack,
|
||||
EnvelopeDecay,
|
||||
EnvelopeSustain,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
enum : uint {
|
||||
EchoHistorySize = 8,
|
||||
BrrBufferSize = 12,
|
||||
BrrBlockSize = 9,
|
||||
|
@ -57,77 +60,77 @@ privileged:
|
|||
struct State {
|
||||
uint8 regs[128];
|
||||
|
||||
ModuloArray<signed, EchoHistorySize> echoHistory[2]; //echo history keeps most recent 8 samples
|
||||
signed echoHistoryOffset;
|
||||
ModuloArray<int, EchoHistorySize> echoHistory[2]; //echo history keeps most recent 8 samples
|
||||
int echoHistoryOffset;
|
||||
|
||||
bool everyOtherSample; //toggles every sample
|
||||
signed kon; //KON value when last checked
|
||||
signed noise;
|
||||
signed counter;
|
||||
signed echoOffset; //offset from ESA in echo buffer
|
||||
signed echoLength; //number of bytes that echo_offset will stop at
|
||||
int kon; //KON value when last checked
|
||||
int noise;
|
||||
int counter;
|
||||
int echoOffset; //offset from ESA in echo buffer
|
||||
int echoLength; //number of bytes that echo_offset will stop at
|
||||
|
||||
//hidden registers also written to when main register is written to
|
||||
signed konBuffer;
|
||||
signed endxBuffer;
|
||||
signed envxBuffer;
|
||||
signed outxBuffer;
|
||||
int konBuffer;
|
||||
int endxBuffer;
|
||||
int envxBuffer;
|
||||
int outxBuffer;
|
||||
|
||||
//temporary state between clocks (prefixed with _)
|
||||
|
||||
//read once per sample
|
||||
signed _pmon;
|
||||
signed _non;
|
||||
signed _eon;
|
||||
signed _dir;
|
||||
signed _koff;
|
||||
int _pmon;
|
||||
int _non;
|
||||
int _eon;
|
||||
int _dir;
|
||||
int _koff;
|
||||
|
||||
//read a few clocks ahead before used
|
||||
signed _brrNextAddress;
|
||||
signed _adsr0;
|
||||
signed _brrHeader;
|
||||
signed _brrByte;
|
||||
signed _srcn;
|
||||
signed _esa;
|
||||
signed _echoDisabled;
|
||||
int _brrNextAddress;
|
||||
int _adsr0;
|
||||
int _brrHeader;
|
||||
int _brrByte;
|
||||
int _srcn;
|
||||
int _esa;
|
||||
int _echoDisabled;
|
||||
|
||||
//internal state that is recalculated every sample
|
||||
signed _dirAddress;
|
||||
signed _pitch;
|
||||
signed _output;
|
||||
signed _looped;
|
||||
signed _echoPointer;
|
||||
int _dirAddress;
|
||||
int _pitch;
|
||||
int _output;
|
||||
int _looped;
|
||||
int _echoPointer;
|
||||
|
||||
//left/right sums
|
||||
signed _mainOut[2];
|
||||
signed _echoOut[2];
|
||||
signed _echoIn [2];
|
||||
int _mainOut[2];
|
||||
int _echoOut[2];
|
||||
int _echoIn [2];
|
||||
} state;
|
||||
|
||||
struct Voice {
|
||||
ModuloArray<signed, BrrBufferSize> buffer; //decoded samples
|
||||
signed bufferOffset; //place in buffer where next samples will be decoded
|
||||
signed gaussianOffset; //relative fractional position in sample (0x1000 = 1.0)
|
||||
signed brrAddress; //address of current BRR block
|
||||
signed brrOffset; //current decoding offset in BRR block
|
||||
signed vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc
|
||||
signed vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc
|
||||
signed konDelay; //KON delay/current setup phase
|
||||
signed envelopeMode;
|
||||
signed envelope; //current envelope level
|
||||
signed hiddenEnvelope; //used by GAIN mode 7, very obscure quirk
|
||||
signed _envxOut;
|
||||
ModuloArray<int, BrrBufferSize> buffer; //decoded samples
|
||||
int bufferOffset; //place in buffer where next samples will be decoded
|
||||
int gaussianOffset; //relative fractional position in sample (0x1000 = 1.0)
|
||||
int brrAddress; //address of current BRR block
|
||||
int brrOffset; //current decoding offset in BRR block
|
||||
int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc
|
||||
int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc
|
||||
int konDelay; //KON delay/current setup phase
|
||||
int envelopeMode;
|
||||
int envelope; //current envelope level
|
||||
int hiddenEnvelope; //used by GAIN mode 7, very obscure quirk
|
||||
int _envxOut;
|
||||
} voice[8];
|
||||
|
||||
//gaussian
|
||||
static const int16 GaussianTable[512];
|
||||
auto gaussianInterpolate(const Voice& v) -> signed;
|
||||
auto gaussianInterpolate(const Voice& v) -> int;
|
||||
|
||||
//counter
|
||||
static const uint16 CounterRate[32];
|
||||
static const uint16 CounterOffset[32];
|
||||
auto counterTick() -> void;
|
||||
auto counterPoll(unsigned rate) -> bool;
|
||||
auto counterPoll(uint rate) -> bool;
|
||||
|
||||
//envelope
|
||||
auto envelopeRun(Voice& v) -> void;
|
||||
|
@ -157,8 +160,8 @@ privileged:
|
|||
auto voice9 (Voice& v) -> void;
|
||||
|
||||
//echo
|
||||
auto calculateFIR(signed i, bool channel) -> signed;
|
||||
auto echoOutput(bool channel) -> signed;
|
||||
auto calculateFIR(int i, bool channel) -> int;
|
||||
auto echoOutput(bool channel) -> int;
|
||||
auto echoRead(bool channel) -> void;
|
||||
auto echoWrite(bool channel) -> void;
|
||||
auto echo22() -> void;
|
||||
|
|
|
@ -128,6 +128,11 @@ struct Settings {
|
|||
bool blurEmulation = true;
|
||||
bool colorEmulation = true;
|
||||
bool scanlineEmulation = true;
|
||||
|
||||
uint controllerPort1 = 0;
|
||||
uint controllerPort2 = 0;
|
||||
uint expansionPort = 0;
|
||||
bool random = true;
|
||||
};
|
||||
|
||||
extern Interface* interface;
|
||||
|
|
|
@ -10,6 +10,7 @@ PPU ppu;
|
|||
#include "sprite/sprite.cpp"
|
||||
#include "window/window.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "video.cpp"
|
||||
|
||||
PPU::PPU() :
|
||||
bg1(*this, Background::ID::BG1),
|
||||
|
@ -119,6 +120,7 @@ auto PPU::reset() -> void {
|
|||
sprite.reset();
|
||||
window.reset();
|
||||
screen.reset();
|
||||
video.reset();
|
||||
|
||||
frame();
|
||||
}
|
||||
|
@ -141,7 +143,7 @@ auto PPU::scanline() -> void {
|
|||
screen.scanline();
|
||||
|
||||
if(vcounter() == 241) {
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
video.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ privileged:
|
|||
#include "screen/screen.hpp"
|
||||
#include "sprite/sprite.hpp"
|
||||
#include "window/window.hpp"
|
||||
#include "video.hpp"
|
||||
|
||||
Background bg1;
|
||||
Background bg2;
|
||||
|
@ -46,6 +47,7 @@ privileged:
|
|||
Sprite sprite;
|
||||
Window window;
|
||||
Screen screen;
|
||||
Video video;
|
||||
|
||||
static auto Enter() -> void;
|
||||
alwaysinline auto add_clocks(uint) -> void;
|
||||
|
@ -57,7 +59,6 @@ privileged:
|
|||
friend class PPU::Sprite;
|
||||
friend class PPU::Window;
|
||||
friend class PPU::Screen;
|
||||
friend class Video;
|
||||
|
||||
struct Debugger {
|
||||
hook<void (uint16, uint8)> vram_read;
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
PPU::Video::Video() {
|
||||
output = new uint32[512 * 512]();
|
||||
output += 16 * 512; //overscan padding
|
||||
|
||||
paletteLiteral = new uint32[1 << 19];
|
||||
paletteStandard = new uint32[1 << 19];
|
||||
paletteEmulation = new uint32[1 << 19];
|
||||
|
||||
output += 16 * 512; //overscan padding
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
PPU::Video::~Video() {
|
||||
output -= 16 * 512;
|
||||
delete[] output;
|
||||
|
||||
delete[] paletteLiteral;
|
||||
delete[] paletteStandard;
|
||||
delete[] paletteEmulation;
|
||||
}
|
||||
|
||||
auto Video::reset() -> void {
|
||||
memory::fill(output, 512 * 480 * sizeof(uint32)); //padding area already cleared
|
||||
auto PPU::Video::reset() -> void {
|
||||
memory::fill(output, 512 * 480 * sizeof(uint32));
|
||||
|
||||
for(auto color : range(1 << 19)) {
|
||||
uint l = (uint4)(color >> 15);
|
||||
|
@ -26,28 +27,31 @@ auto Video::reset() -> void {
|
|||
|
||||
double L = (1.0 + l) / 16.0 * (l ? 1.0 : 0.5);
|
||||
|
||||
{ uint R = L * image::normalize(r, 5, 8);
|
||||
uint G = L * image::normalize(g, 5, 8);
|
||||
uint B = L * image::normalize(b, 5, 8);
|
||||
paletteStandard[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0);
|
||||
{ paletteLiteral[color] = color;
|
||||
}
|
||||
|
||||
{ uint R = L * gammaRamp[r];
|
||||
uint G = L * gammaRamp[g];
|
||||
uint B = L * gammaRamp[b];
|
||||
paletteEmulation[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0);
|
||||
{ uint R = L * image::normalize(r, 5, 16);
|
||||
uint G = L * image::normalize(g, 5, 16);
|
||||
uint B = L * image::normalize(b, 5, 16);
|
||||
paletteStandard[color] = interface->videoColor(R, G, B);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto color : range(1 << 19)) {
|
||||
uint l = (uint4)(color >> 15);
|
||||
uint b = (uint5)(color >> 10);
|
||||
uint g = (uint5)(color >> 5);
|
||||
uint r = (uint5)(color >> 0);
|
||||
{ static const uint8 gammaRamp[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
|
||||
uint R = L * gammaRamp[r] * 0x0101;
|
||||
uint G = L * gammaRamp[g] * 0x0101;
|
||||
uint B = L * gammaRamp[b] * 0x0101;
|
||||
paletteEmulation[color] = interface->videoColor(R, G, B);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Video::refresh() -> void {
|
||||
auto PPU::Video::refresh() -> void {
|
||||
auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard;
|
||||
|
||||
if(settings.scanlineEmulation) {
|
||||
|
@ -109,13 +113,29 @@ auto Video::refresh() -> void {
|
|||
}
|
||||
|
||||
drawCursors();
|
||||
|
||||
interface->videoRefresh(output - (ppu.overscan() ? 0 : 7 * 1024), 512 * sizeof(uint32), 512, 480);
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
//internal
|
||||
auto PPU::Video::drawCursor(uint32 color, int x, int y) -> void {
|
||||
static const uint8 cursor[15 * 15] = {
|
||||
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
|
||||
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
|
||||
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
|
||||
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
|
||||
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
|
||||
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
|
||||
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
|
||||
1,2,2,1,1,2,2,2,2,2,1,1,2,2,1,
|
||||
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
|
||||
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
|
||||
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
|
||||
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
|
||||
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
|
||||
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
|
||||
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
|
||||
};
|
||||
|
||||
auto Video::drawCursor(uint32 color, int x, int y) -> void {
|
||||
auto data = (uint32*)output;
|
||||
if(ppu.interlace() && ppu.field()) data += 512;
|
||||
|
||||
|
@ -138,8 +158,8 @@ auto Video::drawCursor(uint32 color, int x, int y) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto Video::drawCursors() -> void {
|
||||
switch(configuration.controllerPort2) {
|
||||
auto PPU::Video::drawCursors() -> void {
|
||||
switch((Device::ID)settings.controllerPort2) {
|
||||
case Device::ID::SuperScope:
|
||||
if(dynamic_cast<SuperScope*>(device.controllerPort2)) {
|
||||
auto& controller = (SuperScope&)*device.controllerPort2;
|
||||
|
@ -157,28 +177,3 @@ auto Video::drawCursors() -> void {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8 Video::gammaRamp[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
|
||||
const uint8 Video::cursor[15 * 15] = {
|
||||
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
|
||||
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
|
||||
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
|
||||
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
|
||||
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
|
||||
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
|
||||
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
|
||||
1,2,2,1,1,2,2,2,2,2,1,1,2,2,1,
|
||||
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
|
||||
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
|
||||
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
|
||||
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
|
||||
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
|
||||
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
|
||||
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
|
||||
};
|
|
@ -5,18 +5,12 @@ struct Video {
|
|||
auto reset() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
uint32* output = nullptr;
|
||||
uint32* paletteStandard = nullptr;
|
||||
uint32* paletteEmulation = nullptr;
|
||||
|
||||
private:
|
||||
auto drawCursor(uint32 color, int x, int y) -> void;
|
||||
auto drawCursors() -> void;
|
||||
|
||||
static const uint8 gammaRamp[32];
|
||||
static const uint8 cursor[15 * 15];
|
||||
|
||||
friend class System;
|
||||
uint32* output = nullptr;
|
||||
uint32* paletteLiteral = nullptr;
|
||||
uint32* paletteStandard = nullptr;
|
||||
uint32* paletteEmulation = nullptr;
|
||||
};
|
||||
|
||||
extern Video video;
|
|
@ -1,62 +0,0 @@
|
|||
Audio audio;
|
||||
|
||||
auto Audio::coprocessor_enable(bool state) -> void {
|
||||
coprocessor = state;
|
||||
dspaudio.clear();
|
||||
|
||||
dsp_rdoffset = cop_rdoffset = 0;
|
||||
dsp_wroffset = cop_wroffset = 0;
|
||||
dsp_length = cop_length = 0;
|
||||
}
|
||||
|
||||
auto Audio::coprocessor_frequency(double input_frequency) -> void {
|
||||
dspaudio.setFrequency(input_frequency);
|
||||
dspaudio.setResampler(nall::DSP::ResampleEngine::Sinc);
|
||||
dspaudio.setResamplerFrequency(system.apuFrequency() / 768.0);
|
||||
}
|
||||
|
||||
auto Audio::sample(int16 lsample, int16 rsample) -> void {
|
||||
if(coprocessor == false) return interface->audioSample(lsample, rsample);
|
||||
|
||||
dsp_buffer[dsp_wroffset] = ((uint16)lsample << 0) + ((uint16)rsample << 16);
|
||||
dsp_wroffset = (dsp_wroffset + 1) & buffer_mask;
|
||||
dsp_length = (dsp_length + 1) & buffer_mask;
|
||||
flush();
|
||||
}
|
||||
|
||||
auto Audio::coprocessor_sample(int16 lsample, int16 rsample) -> void {
|
||||
int samples[] = {lsample, rsample};
|
||||
dspaudio.sample(samples);
|
||||
while(dspaudio.pending()) {
|
||||
dspaudio.read(samples);
|
||||
|
||||
cop_buffer[cop_wroffset] = ((uint16)samples[0] << 0) + ((uint16)samples[1] << 16);
|
||||
cop_wroffset = (cop_wroffset + 1) & buffer_mask;
|
||||
cop_length = (cop_length + 1) & buffer_mask;
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
auto Audio::flush() -> void {
|
||||
while(dsp_length > 0 && cop_length > 0) {
|
||||
uint32 dsp_sample = dsp_buffer[dsp_rdoffset];
|
||||
uint32 cop_sample = cop_buffer[cop_rdoffset];
|
||||
|
||||
dsp_rdoffset = (dsp_rdoffset + 1) & buffer_mask;
|
||||
cop_rdoffset = (cop_rdoffset + 1) & buffer_mask;
|
||||
|
||||
dsp_length--;
|
||||
cop_length--;
|
||||
|
||||
int dsp_left = (int16)(dsp_sample >> 0);
|
||||
int dsp_right = (int16)(dsp_sample >> 16);
|
||||
|
||||
int cop_left = (int16)(cop_sample >> 0);
|
||||
int cop_right = (int16)(cop_sample >> 16);
|
||||
|
||||
interface->audioSample(
|
||||
sclamp<16>(dsp_left + cop_left ),
|
||||
sclamp<16>(dsp_right + cop_right)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
struct Audio {
|
||||
auto coprocessor_enable(bool state) -> void;
|
||||
auto coprocessor_frequency(double frequency) -> void;
|
||||
auto sample(int16 lsample, int16 rsample) -> void;
|
||||
auto coprocessor_sample(int16 lsample, int16 rsample) -> void;
|
||||
|
||||
private:
|
||||
auto flush() -> void;
|
||||
|
||||
nall::DSP dspaudio;
|
||||
bool coprocessor;
|
||||
enum : uint { buffer_size = 256, buffer_mask = buffer_size - 1 };
|
||||
uint32 dsp_buffer[buffer_size], cop_buffer[buffer_size];
|
||||
uint dsp_rdoffset, cop_rdoffset;
|
||||
uint dsp_wroffset, cop_wroffset;
|
||||
uint dsp_length, cop_length;
|
||||
};
|
||||
|
||||
extern Audio audio;
|
|
@ -32,12 +32,12 @@ auto Device::connect(uint port, Device::ID id) -> void {
|
|||
}
|
||||
|
||||
switch(port) {
|
||||
case 0: configuration.controllerPort1 = id; break;
|
||||
case 1: configuration.controllerPort2 = id; break;
|
||||
case 0: settings.controllerPort1 = (uint)id; break;
|
||||
case 1: settings.controllerPort2 = (uint)id; break;
|
||||
}
|
||||
}
|
||||
|
||||
if(port == 2) {
|
||||
configuration.expansionPort = id;
|
||||
settings.expansionPort = (uint)id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
Random random;
|
||||
|
||||
auto Random::seed(uint seed) -> void {
|
||||
iter = seed;
|
||||
}
|
||||
|
||||
auto Random::operator()(uint result) -> uint {
|
||||
if(!settings.random) return result;
|
||||
return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
|
||||
auto Random::serialize(serializer& s) -> void {
|
||||
s.integer(iter);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
auto System::serialize() -> serializer {
|
||||
serializer s(serializeSize);
|
||||
serializer s(_serializeSize);
|
||||
|
||||
uint signature = 0x31545342, version = Info::SerializerVersion;
|
||||
char hash[64], description[512], profile[16];
|
||||
|
@ -37,13 +37,11 @@ auto System::unserialize(serializer& s) -> bool {
|
|||
return true;
|
||||
}
|
||||
|
||||
//========
|
||||
//internal
|
||||
//========
|
||||
|
||||
auto System::serialize(serializer& s) -> void {
|
||||
s.integer((uint&)region);
|
||||
s.integer((uint&)expansionPort);
|
||||
s.integer((uint&)_region);
|
||||
s.integer((uint&)_expansionPort);
|
||||
}
|
||||
|
||||
auto System::serializeAll(serializer& s) -> void {
|
||||
|
@ -89,5 +87,5 @@ auto System::serializeInit() -> void {
|
|||
s.array(description);
|
||||
|
||||
serializeAll(s);
|
||||
serializeSize = s.size();
|
||||
_serializeSize = s.size();
|
||||
}
|
||||
|
|
|
@ -3,53 +3,46 @@
|
|||
namespace SuperFamicom {
|
||||
|
||||
System system;
|
||||
Configuration configuration;
|
||||
Random random;
|
||||
|
||||
#include "video.cpp"
|
||||
#include "audio.cpp"
|
||||
#include "device.cpp"
|
||||
#include "random.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
#include <sfc/scheduler/scheduler.cpp>
|
||||
|
||||
System::System() {
|
||||
region = Region::Autodetect;
|
||||
expansionPort = Device::ID::eBoot;
|
||||
}
|
||||
auto System::region() const -> Region { return _region; }
|
||||
auto System::expansionPort() const -> Device::ID { return _expansionPort; }
|
||||
auto System::cpuFrequency() const -> uint { return _cpuFrequency; }
|
||||
auto System::apuFrequency() const -> uint { return _apuFrequency; }
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
|
||||
video.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
if(CPU::Threaded == true) {
|
||||
if(CPU::Threaded) {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
||||
runThreadToSave();
|
||||
}
|
||||
|
||||
if(SMP::Threaded == true) {
|
||||
if(SMP::Threaded) {
|
||||
scheduler.thread = smp.thread;
|
||||
runThreadToSave();
|
||||
}
|
||||
|
||||
if(PPU::Threaded == true) {
|
||||
if(PPU::Threaded) {
|
||||
scheduler.thread = ppu.thread;
|
||||
runThreadToSave();
|
||||
}
|
||||
|
||||
if(DSP::Threaded == true) {
|
||||
if(DSP::Threaded) {
|
||||
scheduler.thread = dsp.thread;
|
||||
runThreadToSave();
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < cpu.coprocessors.size(); i++) {
|
||||
auto& chip = *cpu.coprocessors[i];
|
||||
for(uint n = 0; n < cpu.coprocessors.size(); n++) {
|
||||
auto& chip = *cpu.coprocessors[n];
|
||||
scheduler.thread = chip.thread;
|
||||
runThreadToSave();
|
||||
}
|
||||
|
@ -59,9 +52,6 @@ auto System::runThreadToSave() -> void {
|
|||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
|
||||
video.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,8 +79,9 @@ auto System::init() -> void {
|
|||
|
||||
bsmemory.init();
|
||||
|
||||
device.connect(0, configuration.controllerPort1);
|
||||
device.connect(1, configuration.controllerPort2);
|
||||
device.connect(0, (Device::ID)settings.controllerPort1);
|
||||
device.connect(1, (Device::ID)settings.controllerPort2);
|
||||
device.connect(2, (Device::ID)settings.expansionPort);
|
||||
}
|
||||
|
||||
auto System::term() -> void {
|
||||
|
@ -104,15 +95,10 @@ auto System::load() -> void {
|
|||
interface->loadRequest(ID::IPLROM, iplrom, true);
|
||||
}
|
||||
|
||||
region = configuration.region;
|
||||
if(region == Region::Autodetect) {
|
||||
region = (cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL);
|
||||
}
|
||||
expansionPort = configuration.expansionPort;
|
||||
cpuFrequency = region() == Region::NTSC ? 21477272 : 21281370;
|
||||
apuFrequency = 24606720;
|
||||
|
||||
audio.coprocessor_enable(false);
|
||||
_region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL;
|
||||
_expansionPort = (Device::ID)settings.expansionPort;
|
||||
_cpuFrequency = region() == Region::NTSC ? 21477272 : 21281370;
|
||||
_apuFrequency = 24606720;
|
||||
|
||||
bus.reset();
|
||||
bus.map();
|
||||
|
@ -240,10 +226,10 @@ auto System::reset() -> void {
|
|||
if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110);
|
||||
if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1);
|
||||
|
||||
video.reset();
|
||||
scheduler.init();
|
||||
device.connect(0, configuration.controllerPort1);
|
||||
device.connect(1, configuration.controllerPort2);
|
||||
device.connect(0, (Device::ID)settings.controllerPort1);
|
||||
device.connect(1, (Device::ID)settings.controllerPort2);
|
||||
device.connect(2, (Device::ID)settings.expansionPort);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
struct Interface;
|
||||
|
||||
#include "video.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
struct System : property<System> {
|
||||
enum class Region : uint { NTSC = 0, PAL = 1, Autodetect = 2 };
|
||||
struct System {
|
||||
enum class Region : bool { NTSC = 0, PAL = 1 };
|
||||
|
||||
System();
|
||||
auto region() const -> Region;
|
||||
auto expansionPort() const -> Device::ID;
|
||||
auto cpuFrequency() const -> uint;
|
||||
auto apuFrequency() const -> uint;
|
||||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
|
@ -19,14 +20,6 @@ struct System : property<System> {
|
|||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
//return *active* system information (settings are cached upon power-on)
|
||||
readonly<Region> region;
|
||||
readonly<Device::ID> expansionPort;
|
||||
|
||||
readonly<uint> cpuFrequency;
|
||||
readonly<uint> apuFrequency;
|
||||
readonly<uint> serializeSize;
|
||||
|
||||
auto serialize() -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
|
||||
|
@ -41,9 +34,13 @@ private:
|
|||
auto serializeAll(serializer&) -> void;
|
||||
auto serializeInit() -> void;
|
||||
|
||||
Region _region = Region::NTSC;
|
||||
Device::ID _expansionPort = Device::ID::None;
|
||||
uint _cpuFrequency = 0;
|
||||
uint _apuFrequency = 0;
|
||||
uint _serializeSize = 0;
|
||||
|
||||
friend class Cartridge;
|
||||
friend class Video;
|
||||
friend class Audio;
|
||||
friend class Device;
|
||||
};
|
||||
|
||||
|
@ -51,29 +48,10 @@ extern System system;
|
|||
|
||||
#include <sfc/scheduler/scheduler.hpp>
|
||||
|
||||
struct Configuration {
|
||||
Device::ID controllerPort1 = Device::ID::None;
|
||||
Device::ID controllerPort2 = Device::ID::None;
|
||||
Device::ID expansionPort = Device::ID::None;
|
||||
System::Region region = System::Region::Autodetect;
|
||||
bool random = true;
|
||||
};
|
||||
|
||||
extern Configuration configuration;
|
||||
|
||||
struct Random {
|
||||
auto seed(uint seed) -> void {
|
||||
iter = seed;
|
||||
}
|
||||
|
||||
auto operator()(uint result) -> uint {
|
||||
if(configuration.random == false) return result;
|
||||
return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(iter);
|
||||
}
|
||||
auto seed(uint seed) -> void;
|
||||
auto operator()(uint result) -> uint;
|
||||
auto serialize(serializer& s) -> void;
|
||||
|
||||
private:
|
||||
uint iter = 0;
|
||||
|
|
|
@ -3,6 +3,7 @@ name := higan
|
|||
processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050
|
||||
include processor/GNUmakefile
|
||||
|
||||
include emulator/GNUmakefile
|
||||
include fc/GNUmakefile
|
||||
include sfc/GNUmakefile
|
||||
include gb/GNUmakefile
|
||||
|
|
|
@ -47,7 +47,11 @@ auto Icarus::famicomImport(vector<uint8>& buffer, string location) -> string {
|
|||
uint chrrom = 0;
|
||||
auto markup = famicomManifest(buffer, location, &prgrom, &chrrom);
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
|
||||
file::copy({source, name, ".sav"}, {target, "save.ram"});
|
||||
}
|
||||
|
||||
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
|
||||
file::write({target, "ines.rom"}, buffer.data(), 16);
|
||||
|
|
|
@ -39,7 +39,11 @@ auto Icarus::gameBoyAdvanceImport(vector<uint8>& buffer, string location) -> str
|
|||
|
||||
auto markup = gameBoyAdvanceManifest(buffer, location);
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
|
||||
file::copy({source, name, ".sav"}, {target, "save.ram"});
|
||||
}
|
||||
|
||||
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
|
||||
file::write({target, "program.rom"}, buffer);
|
||||
|
|
|
@ -39,7 +39,11 @@ auto Icarus::gameBoyColorImport(vector<uint8>& buffer, string location) -> strin
|
|||
|
||||
auto markup = gameBoyColorManifest(buffer, location);
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
|
||||
file::copy({source, name, ".sav"}, {target, "save.ram"});
|
||||
}
|
||||
|
||||
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
|
||||
file::write({target, "program.rom"}, buffer);
|
||||
|
|
|
@ -39,7 +39,11 @@ auto Icarus::gameBoyImport(vector<uint8>& buffer, string location) -> string {
|
|||
|
||||
auto markup = gameBoyManifest(buffer, location);
|
||||
if(!markup) return failure("failed to parse ROM image");
|
||||
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
|
||||
file::copy({source, name, ".sav"}, {target, "save.ram"});
|
||||
}
|
||||
|
||||
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
|
||||
file::write({target, "program.rom"}, buffer);
|
||||
|
|
|
@ -66,6 +66,9 @@ auto Icarus::superFamicomImport(vector<uint8>& buffer, string location) -> strin
|
|||
}
|
||||
|
||||
if(!directory::create(target)) return failure("library path unwritable");
|
||||
if(file::exists({source, name, ".srm"}) && !file::exists({target, "save.ram"})) {
|
||||
file::copy({source, name, ".srm"}, {target, "save.ram"});
|
||||
}
|
||||
|
||||
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
|
||||
uint offset = (buffer.size() & 0x7fff) == 512 ? 512 : 0; //skip header if present
|
||||
|
|
|
@ -114,12 +114,9 @@ template<typename... P> inline auto execute(const string& name, P&&... p) -> str
|
|||
nullptr, nullptr, &si, &pi
|
||||
)) return "";
|
||||
|
||||
while(true) {
|
||||
DWORD exitCode;
|
||||
GetExitCodeProcess(pi.hProcess, &exitCode);
|
||||
Sleep(1);
|
||||
if(exitCode != STILL_ACTIVE) break;
|
||||
}
|
||||
if(WaitForSingleObject(pi.hProcess, INFINITE)) return "";
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
string result;
|
||||
while(true) {
|
||||
|
|
|
@ -6,7 +6,7 @@ auto activepath() -> string {
|
|||
char path[PATH_MAX] = "";
|
||||
auto unused = getcwd(path, PATH_MAX);
|
||||
string result = path;
|
||||
if(result.empty()) result = ".";
|
||||
if(!result) result = ".";
|
||||
result.transform("\\", "/");
|
||||
if(result.endsWith("/") == false) result.append("/");
|
||||
return result;
|
||||
|
@ -16,7 +16,7 @@ auto realpath(rstring name) -> string {
|
|||
string result;
|
||||
char path[PATH_MAX] = "";
|
||||
if(::realpath(name, path)) result = pathname(string{path}.transform("\\", "/"));
|
||||
if(result.empty()) return activepath();
|
||||
if(!result) return activepath();
|
||||
result.transform("\\", "/");
|
||||
if(result.endsWith("/") == false) result.append("/");
|
||||
return result;
|
||||
|
@ -48,7 +48,7 @@ auto userpath() -> string {
|
|||
struct passwd* userinfo = getpwuid(getuid());
|
||||
string result = userinfo->pw_dir;
|
||||
#endif
|
||||
if(result.empty()) result = ".";
|
||||
if(!result) result = ".";
|
||||
if(result.endsWith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
|
@ -66,12 +66,12 @@ auto configpath() -> string {
|
|||
#else
|
||||
string result = {userpath(), ".config/"};
|
||||
#endif
|
||||
if(result.empty()) result = ".";
|
||||
if(!result) result = ".";
|
||||
if(result.endsWith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
|
||||
// /home/username/.local/
|
||||
// /home/username/.local/share/
|
||||
// c:/users/username/appdata/local/
|
||||
auto localpath() -> string {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
@ -82,9 +82,9 @@ auto localpath() -> string {
|
|||
#elif defined(PLATFORM_MACOSX)
|
||||
string result = {userpath(), "Library/Application Support/"};
|
||||
#else
|
||||
string result = {userpath(), ".local/"};
|
||||
string result = {userpath(), ".local/share/"};
|
||||
#endif
|
||||
if(result.empty()) result = ".";
|
||||
if(!result) result = ".";
|
||||
if(result.endsWith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ auto sharedpath() -> string {
|
|||
#else
|
||||
string result = "/usr/share/";
|
||||
#endif
|
||||
if(result.empty()) result = ".";
|
||||
if(!result) result = ".";
|
||||
if(result.endsWith("/") == false) result.append("/");
|
||||
return result;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue