Sources: Run clang-format on everything.

This commit is contained in:
Emmanuel Gil Peyrot 2016-09-18 09:38:01 +09:00
parent fe948af095
commit dc8479928c
386 changed files with 19560 additions and 18080 deletions

View file

@ -42,10 +42,18 @@ void Init() {
} }
void AddAddressSpace(Kernel::VMManager& address_space) { void AddAddressSpace(Kernel::VMManager& address_space) {
auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); auto r0_vma = address_space
.MapBackingMemory(DSP::HLE::region0_base,
reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]),
sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
.MoveFrom();
address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); auto r1_vma = address_space
.MapBackingMemory(DSP::HLE::region1_base,
reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]),
sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
.MoveFrom();
address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite);
} }
@ -58,9 +66,9 @@ void SelectSink(std::string sink_id) {
return; return;
} }
auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { auto iter =
return sink_detail.id == sink_id; std::find_if(g_sink_details.begin(), g_sink_details.end(),
}); [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
if (iter == g_sink_details.end()) { if (iter == g_sink_details.end()) {
LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id");

View file

@ -12,7 +12,7 @@ class VMManager;
namespace AudioCore { namespace AudioCore {
constexpr int native_sample_rate = 32728; ///< 32kHz constexpr int native_sample_rate = 32728; ///< 32kHz
/// Initialise Audio Core /// Initialise Audio Core
void Init(); void Init();

View file

@ -15,22 +15,25 @@
namespace Codec { namespace Codec {
StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients. // GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each. // Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long. // Samples are 4 bits (one nibble) long.
constexpr size_t FRAME_LEN = 8; constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14; constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }}; constexpr std::array<int, 16> SIGNED_NIBBLES{
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
StereoBuffer16 ret(ret_size); StereoBuffer16 ret(ret_size);
int yn1 = state.yn1, int yn1 = state.yn1, yn2 = state.yn2;
yn2 = state.yn2;
const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (size_t framei = 0; framei < NUM_FRAMES; framei++) { for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN]; const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF); const int scale = 1 << (frame_header & 0xF);
@ -43,7 +46,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
// Decodes an audio sample. One nibble produces one sample. // Decodes an audio sample. One nibble produces one sample.
const auto decode_sample = [&](const int nibble) -> s16 { const auto decode_sample = [&](const int nibble) -> s16 {
const int xn = nibble * scale; const int xn = nibble * scale;
// We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back. // We first transform everything into 11 bit fixed point, perform the second order
// digital filter, then transform back.
// 0x400 == 0.5 in 11 bit fixed point. // 0x400 == 0.5 in 11 bit fixed point.
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
@ -82,7 +86,8 @@ static s16 SignExtendS8(u8 x) {
return static_cast<s16>(static_cast<s8>(x)); return static_cast<s16>(static_cast<s8>(x));
} }
StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) { StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
const size_t sample_count) {
ASSERT(num_channels == 1 || num_channels == 2); ASSERT(num_channels == 1 || num_channels == 2);
StereoBuffer16 ret(sample_count); StereoBuffer16 ret(sample_count);
@ -101,7 +106,8 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
return ret; return ret;
} }
StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) { StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
const size_t sample_count) {
ASSERT(num_channels == 1 || num_channels == 2); ASSERT(num_channels == 1 || num_channels == 2);
StereoBuffer16 ret(sample_count); StereoBuffer16 ret(sample_count);
@ -118,5 +124,4 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, co
return ret; return ret;
} }
}; };

View file

@ -29,7 +29,8 @@ struct ADPCMState {
* @param state ADPCM state, this is updated with new state * @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
const std::array<s16, 16>& adpcm_coeff, ADPCMState& state);
/** /**
* @param num_channels Number of channels * @param num_channels Number of channels
@ -37,7 +38,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
* @param sample_count Length of buffer in terms of number of samples * @param sample_count Length of buffer in terms of number of samples
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count); StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
const size_t sample_count);
/** /**
* @param num_channels Number of channels * @param num_channels Number of channels
@ -45,6 +47,6 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
* @param sample_count Length of buffer in terms of number of samples * @param sample_count Length of buffer in terms of number of samples
* @return Decoded stereo signed PCM16 data, sample_count in length * @return Decoded stereo signed PCM16 data, sample_count in length
*/ */
StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count); StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
const size_t sample_count);
}; };

View file

@ -13,23 +13,22 @@ namespace DSP {
namespace HLE { namespace HLE {
constexpr int num_sources = 24; constexpr int num_sources = 24;
constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>;
/// The DSP is quadraphonic internally. /// The DSP is quadraphonic internally.
using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
/** /**
* This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
* FilterT::ProcessSample is called sequentially on the samples. * FilterT::ProcessSample is called sequentially on the samples.
*/ */
template<typename FrameT, typename FilterT> template <typename FrameT, typename FilterT>
void FilterFrame(FrameT& frame, FilterT& filter) { void FilterFrame(FrameT& frame, FilterT& filter) {
std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) { std::transform(frame.begin(), frame.end(), frame.begin(),
return filter.ProcessSample(sample); [&filter](const auto& sample) { return filter.ProcessSample(sample); });
});
} }
} // namespace HLE } // namespace HLE

View file

@ -47,11 +47,9 @@ static SharedMemory& WriteRegion() {
// Audio processing and mixing // Audio processing and mixing
static std::array<Source, num_sources> sources = { static std::array<Source, num_sources> sources = {
Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(6), Source(7),
Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), Source(8), Source(9), Source(10), Source(11), Source(12), Source(13), Source(14), Source(15),
Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)};
Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
};
static Mixers mixers; static Mixers mixers;
static StereoFrame16 GenerateCurrentFrame() { static StereoFrame16 GenerateCurrentFrame() {
@ -62,14 +60,16 @@ static StereoFrame16 GenerateCurrentFrame() {
// Generate intermediate mixes // Generate intermediate mixes
for (size_t i = 0; i < num_sources; i++) { for (size_t i = 0; i < num_sources; i++) {
write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); write.source_statuses.status[i] =
sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
for (size_t mix = 0; mix < 3; mix++) { for (size_t mix = 0; mix < 3; mix++) {
sources[i].MixInto(intermediate_mixes[mix], mix); sources[i].MixInto(intermediate_mixes[mix], mix);
} }
} }
// Generate final mix // Generate final mix
write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes); write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples,
write.intermediate_mix_samples, intermediate_mixes);
StereoFrame16 output_frame = mixers.GetOutput(); StereoFrame16 output_frame = mixers.GetOutput();
@ -152,7 +152,8 @@ void Shutdown() {
bool Tick() { bool Tick() {
StereoFrame16 current_frame = {}; StereoFrame16 current_frame = {};
// TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region) // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
// shared memory region)
current_frame = GenerateCurrentFrame(); current_frame = GenerateCurrentFrame();
OutputCurrentFrame(current_frame); OutputCurrentFrame(current_frame);

View file

@ -30,7 +30,8 @@ namespace HLE {
// Second Region: 0x1FF70000 (Size: 0x8000) // Second Region: 0x1FF70000 (Size: 0x8000)
// //
// The DSP reads from each region alternately based on the frame counter for each region much like a // The DSP reads from each region alternately based on the frame counter for each region much like a
// double-buffer. The frame counter is located as the very last u16 of each region and is incremented // double-buffer. The frame counter is located as the very last u16 of each region and is
// incremented
// each audio tick. // each audio tick.
constexpr VAddr region0_base = 0x1FF50000; constexpr VAddr region0_base = 0x1FF50000;
@ -56,6 +57,7 @@ struct u32_dsp {
void operator=(u32 new_value) { void operator=(u32 new_value) {
storage = Convert(new_value); storage = Convert(new_value);
} }
private: private:
static constexpr u32 Convert(u32 value) { static constexpr u32 Convert(u32 value) {
return (value << 16) | (value >> 16); return (value << 16) | (value >> 16);
@ -89,11 +91,13 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe. // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
// See also: DSP::HLE::PipeRead. // See also: DSP::HLE::PipeRead.
// //
// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are // Note that the above addresses do vary slightly between audio firmwares observed; the addresses
// are
// not fixed in stone. The addresses above are only an examplar; they're what this implementation // not fixed in stone. The addresses above are only an examplar; they're what this implementation
// does and provides to applications. // does and provides to applications.
// //
// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the // Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
// the
// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the // ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the
// second region via: // second region via:
// second_region_dsp_addr = first_region_dsp_addr | 0x10000 // second_region_dsp_addr = first_region_dsp_addr | 0x10000
@ -110,14 +114,17 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
// GCC versions < 5.0 do not implement std::is_trivially_copyable. // GCC versions < 5.0 do not implement std::is_trivially_copyable.
// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
#if (__GNUC__ >= 5) || defined(__clang__) #if (__GNUC__ >= 5) || defined(__clang__)
#define ASSERT_DSP_STRUCT(name, size) \ #define ASSERT_DSP_STRUCT(name, size) \
static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ static_assert(std::is_standard_layout<name>::value, \
static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \ "DSP structure " #name " doesn't use standard layout"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) static_assert(std::is_trivially_copyable<name>::value, \
"DSP structure " #name " isn't trivially copyable"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
#else #else
#define ASSERT_DSP_STRUCT(name, size) \ #define ASSERT_DSP_STRUCT(name, size) \
static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ static_assert(std::is_standard_layout<name>::value, \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) "DSP structure " #name " doesn't use standard layout"); \
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
#endif #endif
struct SourceConfiguration { struct SourceConfiguration {
@ -130,7 +137,8 @@ struct SourceConfiguration {
BitField<0, 1, u32_le> format_dirty; BitField<0, 1, u32_le> format_dirty;
BitField<1, 1, u32_le> mono_or_stereo_dirty; BitField<1, 1, u32_le> mono_or_stereo_dirty;
BitField<2, 1, u32_le> adpcm_coefficients_dirty; BitField<2, 1, u32_le> adpcm_coefficients_dirty;
BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. BitField<3, 1, u32_le>
partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
BitField<4, 1, u32_le> partial_reset_flag; BitField<4, 1, u32_le> partial_reset_flag;
BitField<16, 1, u32_le> enable_dirty; BitField<16, 1, u32_le> enable_dirty;
@ -138,7 +146,8 @@ struct SourceConfiguration {
BitField<18, 1, u32_le> rate_multiplier_dirty; BitField<18, 1, u32_le> rate_multiplier_dirty;
BitField<19, 1, u32_le> buffer_queue_dirty; BitField<19, 1, u32_le> buffer_queue_dirty;
BitField<20, 1, u32_le> loop_related_dirty; BitField<20, 1, u32_le> loop_related_dirty;
BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated. BitField<21, 1, u32_le>
play_position_dirty; ///< Tends to also be set when embedded buffer is updated.
BitField<22, 1, u32_le> filters_enabled_dirty; BitField<22, 1, u32_le> filters_enabled_dirty;
BitField<23, 1, u32_le> simple_filter_dirty; BitField<23, 1, u32_le> simple_filter_dirty;
BitField<24, 1, u32_le> biquad_filter_dirty; BitField<24, 1, u32_le> biquad_filter_dirty;
@ -164,11 +173,7 @@ struct SourceConfiguration {
/// Multiplier for sample rate. Resampling occurs with the selected interpolation method. /// Multiplier for sample rate. Resampling occurs with the selected interpolation method.
float_le rate_multiplier; float_le rate_multiplier;
enum class InterpolationMode : u8 { enum class InterpolationMode : u8 { Polyphase = 0, Linear = 1, None = 2 };
Polyphase = 0,
Linear = 1,
None = 2
};
InterpolationMode interpolation_mode; InterpolationMode interpolation_mode;
INSERT_PADDING_BYTES(1); ///< Interpolation related INSERT_PADDING_BYTES(1); ///< Interpolation related
@ -191,7 +196,8 @@ struct SourceConfiguration {
* This is a normalised biquad filter (second-order). * This is a normalised biquad filter (second-order).
* The transfer function of this filter is: * The transfer function of this filter is:
* H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2) * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
* Nintendo chose to negate the feedbackward coefficients. This differs from standard notation * Nintendo chose to negate the feedbackward coefficients. This differs from standard
* notation
* as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
* Values are signed fixed point with 14 fractional bits. * Values are signed fixed point with 14 fractional bits.
*/ */
@ -239,23 +245,24 @@ struct SourceConfiguration {
/// Is a looping buffer. /// Is a looping buffer.
u8 is_looping; u8 is_looping;
/// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished. /// This value is shown in SourceStatus::previous_buffer_id when this buffer has
/// finished.
/// This allows the emulated application to tell what buffer is currently playing /// This allows the emulated application to tell what buffer is currently playing
u16_le buffer_id; u16_le buffer_id;
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
}; };
u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i]) u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
Buffer buffers[4]; ///< Queued Buffers Buffer buffers[4]; ///< Queued Buffers
// Playback controls // Playback controls
u32_dsp loop_related; u32_dsp loop_related;
u8 enable; u8 enable;
INSERT_PADDING_BYTES(1); INSERT_PADDING_BYTES(1);
u16_le sync; ///< Application-side sync (See also: SourceStatus::sync) u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
u32_dsp play_position; ///< Position. (Units: number of samples) u32_dsp play_position; ///< Position. (Units: number of samples)
INSERT_PADDING_DSPWORDS(2); INSERT_PADDING_DSPWORDS(2);
// Embedded Buffer // Embedded Buffer
@ -268,16 +275,9 @@ struct SourceConfiguration {
/// Note a sample takes up different number of bytes in different buffer formats. /// Note a sample takes up different number of bytes in different buffer formats.
u32_dsp length; u32_dsp length;
enum class MonoOrStereo : u16_le { enum class MonoOrStereo : u16_le { Mono = 1, Stereo = 2 };
Mono = 1,
Stereo = 2
};
enum class Format : u16_le { enum class Format : u16_le { PCM8 = 0, PCM16 = 1, ADPCM = 2 };
PCM8 = 0,
PCM16 = 1,
ADPCM = 2
};
union { union {
u16_le flags1_raw; u16_le flags1_raw;
@ -299,10 +299,11 @@ struct SourceConfiguration {
union { union {
u16_le flags2_raw; u16_le flags2_raw;
BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed? BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer? BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
}; };
/// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer). /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this
/// buffer).
u16_le buffer_id; u16_le buffer_id;
}; };
@ -313,11 +314,11 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
struct SourceStatus { struct SourceStatus {
struct Status { struct Status {
u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.) u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
u32_dsp buffer_position; ///< Number of samples into the current buffer u32_dsp buffer_position; ///< Number of samples into the current buffer
u16_le current_buffer_id; ///< Updated when a buffer finishes playing u16_le current_buffer_id; ///< Updated when a buffer finishes playing
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
}; };
@ -347,16 +348,13 @@ struct DspConfiguration {
BitField<28, 1, u32_le> headphones_connected_dirty; BitField<28, 1, u32_le> headphones_connected_dirty;
}; };
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
/// each at the final mixer
float_le volume[3]; float_le volume[3];
INSERT_PADDING_DSPWORDS(3); INSERT_PADDING_DSPWORDS(3);
enum class OutputFormat : u16_le { enum class OutputFormat : u16_le { Mono = 0, Stereo = 1, Surround = 2 };
Mono = 0,
Stereo = 1,
Surround = 2
};
OutputFormat output_format; OutputFormat output_format;
@ -388,8 +386,9 @@ struct DspConfiguration {
u16_le enable; u16_le enable;
INSERT_PADDING_DSPWORDS(1); INSERT_PADDING_DSPWORDS(1);
u16_le outputs; u16_le outputs;
u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer. u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to
u16_le frame_count; ///< Frames to delay by /// use as a work buffer.
u16_le frame_count; ///< Frames to delay by
// Coefficients // Coefficients
s16_le g; ///< Fixed point with 7 fractional bits s16_le g; ///< Fixed point with 7 fractional bits
@ -506,21 +505,36 @@ ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
extern std::array<SharedMemory, 2> g_regions; extern std::array<SharedMemory, 2> g_regions;
// Structures must have an offset that is a multiple of two. // Structures must have an offset that is a multiple of two.
static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0,
static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0,
static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0,
static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0,
static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0,
static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0,
static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, final_samples) % 2 == 0,
static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, compressor) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown10) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown11) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown12) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown13) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, unknown14) % 2 == 0,
"Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
#undef INSERT_PADDING_DSPWORDS #undef INSERT_PADDING_DSPWORDS
#undef ASSERT_DSP_STRUCT #undef ASSERT_DSP_STRUCT

View file

@ -59,7 +59,8 @@ void SourceFilters::SimpleFilter::Reset() {
b0 = 1 << 15; b0 = 1 << 15;
} }
void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { void SourceFilters::SimpleFilter::Configure(
SourceConfiguration::Configuration::SimpleFilter config) {
a1 = config.a1; a1 = config.a1;
b0 = config.b0; b0 = config.b0;
} }
@ -88,7 +89,8 @@ void SourceFilters::BiquadFilter::Reset() {
b0 = 1 << 14; b0 = 1 << 14;
} }
void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { void SourceFilters::BiquadFilter::Configure(
SourceConfiguration::Configuration::BiquadFilter config) {
a1 = config.a1; a1 = config.a1;
a2 = config.a2; a2 = config.a2;
b0 = config.b0; b0 = config.b0;

View file

@ -17,7 +17,9 @@ namespace HLE {
/// Preprocessing filters. There is an independent set of filters for each Source. /// Preprocessing filters. There is an independent set of filters for each Source.
class SourceFilters final { class SourceFilters final {
public: public:
SourceFilters() { Reset(); } SourceFilters() {
Reset();
}
/// Reset internal state. /// Reset internal state.
void Reset(); void Reset();
@ -54,7 +56,9 @@ private:
bool biquad_filter_enabled; bool biquad_filter_enabled;
struct SimpleFilter { struct SimpleFilter {
SimpleFilter() { Reset(); } SimpleFilter() {
Reset();
}
/// Resets internal state. /// Resets internal state.
void Reset(); void Reset();
@ -80,7 +84,9 @@ private:
} simple_filter; } simple_filter;
struct BiquadFilter { struct BiquadFilter {
BiquadFilter() { Reset(); } BiquadFilter() {
Reset();
}
/// Resets internal state. /// Resets internal state.
void Reset(); void Reset();

View file

@ -20,11 +20,9 @@ void Mixers::Reset() {
state = {}; state = {};
} }
DspStatus Mixers::Tick(DspConfiguration& config, DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
const IntermediateMixSamples& read_samples, IntermediateMixSamples& write_samples,
IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) {
const std::array<QuadFrame32, 3>& input)
{
ParseConfig(config); ParseConfig(config);
AuxReturn(read_samples); AuxReturn(read_samples);
@ -73,13 +71,15 @@ void Mixers::ParseConfig(DspConfiguration& config) {
if (config.output_format_dirty) { if (config.output_format_dirty) {
config.output_format_dirty.Assign(0); config.output_format_dirty.Assign(0);
state.output_format = config.output_format; state.output_format = config.output_format;
LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format)); LOG_TRACE(Audio_DSP, "mixers output_format = %zu",
static_cast<size_t>(config.output_format));
} }
if (config.headphones_connected_dirty) { if (config.headphones_connected_dirty) {
config.headphones_connected_dirty.Assign(0); config.headphones_connected_dirty.Assign(0);
// Do nothing. // Do nothing.
// (Note: Whether headphones are connected does affect coefficients used for surround sound.) // (Note: Whether headphones are connected does affect coefficients used for surround
// sound.)
LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
} }
@ -94,11 +94,10 @@ static s16 ClampToS16(s32 value) {
return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
} }
static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) { static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a,
return { const std::array<s16, 2>& b) {
ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1])) ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))};
};
} }
void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
@ -106,27 +105,33 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
switch (state.output_format) { switch (state.output_format) {
case OutputFormat::Mono: case OutputFormat::Mono:
std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), std::transform(
[gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
[gain](const std::array<s16, 2>& accumulator,
const std::array<s32, 4>& sample) -> std::array<s16, 2> {
// Downmix to mono // Downmix to mono
s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2)); s16 mono = ClampToS16(static_cast<s32>(
(gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) /
2));
// Mix into current frame // Mix into current frame
return AddAndClampToS16(accumulator, { mono, mono }); return AddAndClampToS16(accumulator, {mono, mono});
}); });
return; return;
case OutputFormat::Surround: case OutputFormat::Surround:
// TODO(merry): Implement surround sound. // TODO(merry): Implement surround sound.
// fallthrough // fallthrough
case OutputFormat::Stereo: case OutputFormat::Stereo:
std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), std::transform(
[gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
[gain](const std::array<s16, 2>& accumulator,
const std::array<s32, 4>& sample) -> std::array<s16, 2> {
// Downmix to stereo // Downmix to stereo
s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
// Mix into current frame // Mix into current frame
return AddAndClampToS16(accumulator, { left, right }); return AddAndClampToS16(accumulator, {left, right});
}); });
return; return;
} }
@ -135,12 +140,14 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
} }
void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
// QuadFrame32.
if (state.mixer1_enabled) { if (state.mixer1_enabled) {
for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t sample = 0; sample < samples_per_frame; sample++) {
for (size_t channel = 0; channel < 4; channel++) { for (size_t channel = 0; channel < 4; channel++) {
state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample]; state.intermediate_mix_buffer[1][sample][channel] =
read_samples.mix1.pcm32[channel][sample];
} }
} }
} }
@ -148,14 +155,17 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
if (state.mixer2_enabled) { if (state.mixer2_enabled) {
for (size_t sample = 0; sample < samples_per_frame; sample++) { for (size_t sample = 0; sample < samples_per_frame; sample++) {
for (size_t channel = 0; channel < 4; channel++) { for (size_t channel = 0; channel < 4; channel++) {
state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample]; state.intermediate_mix_buffer[2][sample][channel] =
read_samples.mix2.pcm32[channel][sample];
} }
} }
} }
} }
void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) { void Mixers::AuxSend(IntermediateMixSamples& write_samples,
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. const std::array<QuadFrame32, 3>& input) {
// NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
// QuadFrame32.
state.intermediate_mix_buffer[0] = input[0]; state.intermediate_mix_buffer[0] = input[0];
@ -184,7 +194,8 @@ void Mixers::MixCurrentFrame() {
current_frame.fill({}); current_frame.fill({});
for (size_t mix = 0; mix < 3; mix++) { for (size_t mix = 0; mix < 3; mix++) {
DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]); DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix],
state.intermediate_mix_buffer[mix]);
} }
// TODO(merry): Compressor. (We currently assume a disabled compressor.) // TODO(merry): Compressor. (We currently assume a disabled compressor.)

View file

@ -20,10 +20,8 @@ public:
void Reset(); void Reset();
DspStatus Tick(DspConfiguration& config, DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
const IntermediateMixSamples& read_samples, IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
IntermediateMixSamples& write_samples,
const std::array<QuadFrame32, 3>& input);
StereoFrame16 GetOutput() const { StereoFrame16 GetOutput() const {
return current_frame; return current_frame;
@ -53,7 +51,8 @@ private:
void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
/// INTERNAL: Mix current_frame. /// INTERNAL: Mix current_frame.
void MixCurrentFrame(); void MixCurrentFrame();
/// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame. /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate
/// into current_frame.
void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
/// INTERNAL: Generate DspStatus based on internal state. /// INTERNAL: Generate DspStatus based on internal state.
DspStatus GetCurrentStatus() const; DspStatus GetCurrentStatus() const;

View file

@ -44,8 +44,10 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
std::vector<u8>& data = pipe_data[pipe_index]; std::vector<u8>& data = pipe_data[pipe_index];
if (length > data.size()) { if (length > data.size()) {
LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", LOG_WARNING(
pipe_index, length, data.size()); Audio_DSP,
"pipe_number = %zu is out of data, application requested read of %u but %zu remain",
pipe_index, length, data.size());
length = static_cast<u32>(data.size()); length = static_cast<u32>(data.size());
} }
@ -95,8 +97,7 @@ static void AudioPipeWriteStructAddresses() {
0x8000 + offsetof(SharedMemory, unknown11) / 2, 0x8000 + offsetof(SharedMemory, unknown11) / 2,
0x8000 + offsetof(SharedMemory, unknown12) / 2, 0x8000 + offsetof(SharedMemory, unknown12) / 2,
0x8000 + offsetof(SharedMemory, unknown13) / 2, 0x8000 + offsetof(SharedMemory, unknown13) / 2,
0x8000 + offsetof(SharedMemory, unknown14) / 2 0x8000 + offsetof(SharedMemory, unknown14) / 2};
};
// Begin with a u16 denoting the number of structs. // Begin with a u16 denoting the number of structs.
WriteU16(DspPipe::Audio, static_cast<u16>(struct_addresses.size())); WriteU16(DspPipe::Audio, static_cast<u16>(struct_addresses.size()));
@ -112,16 +113,12 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
switch (pipe_number) { switch (pipe_number) {
case DspPipe::Audio: { case DspPipe::Audio: {
if (buffer.size() != 4) { if (buffer.size() != 4) {
LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size()); LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written",
buffer.size());
return; return;
} }
enum class StateChange { enum class StateChange { Initalize = 0, Shutdown = 1, Wakeup = 2, Sleep = 3 };
Initalize = 0,
Shutdown = 1,
Wakeup = 2,
Sleep = 3
};
// The difference between Initialize and Wakeup is that Input state is maintained // The difference between Initialize and Wakeup is that Input state is maintained
// when sleeping but isn't when turning it off and on again. (TODO: Implement this.) // when sleeping but isn't when turning it off and on again. (TODO: Implement this.)
@ -152,7 +149,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
dsp_state = DspState::Sleeping; dsp_state = DspState::Sleeping;
break; break;
default: default:
LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]); LOG_ERROR(Audio_DSP,
"Application has requested unknown state transition of DSP hardware %hhu",
buffer[0]);
dsp_state = DspState::Off; dsp_state = DspState::Off;
break; break;
} }
@ -160,7 +159,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
return; return;
} }
default: default:
LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number)); LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented",
static_cast<size_t>(pipe_number));
UNIMPLEMENTED(); UNIMPLEMENTED();
return; return;
} }

View file

@ -15,20 +15,17 @@ namespace HLE {
/// Reset the pipes by setting pipe positions back to the beginning. /// Reset the pipes by setting pipe positions back to the beginning.
void ResetPipes(); void ResetPipes();
enum class DspPipe { enum class DspPipe { Debug = 0, Dma = 1, Audio = 2, Binary = 3 };
Debug = 0,
Dma = 1,
Audio = 2,
Binary = 3
};
constexpr size_t NUM_DSP_PIPE = 8; constexpr size_t NUM_DSP_PIPE = 8;
/** /**
* Reads `length` bytes from the DSP pipe identified with `pipe_number`. * Reads `length` bytes from the DSP pipe identified with `pipe_number`.
* @note Can read up to the maximum value of a u16 in bytes (65,535). * @note Can read up to the maximum value of a u16 in bytes (65,535).
* @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned. * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty
* vector will be returned.
* @note IF `length` is set to 0, an empty vector will be returned. * @note IF `length` is set to 0, an empty vector will be returned.
* @note IF `length` is greater than the amount of data available, this function will only read the available amount. * @note IF `length` is greater than the amount of data available, this function will only read the
* available amount.
* @param pipe_number a `DspPipe` * @param pipe_number a `DspPipe`
* @param length the number of bytes to read. The max is 65,535 (max of u16). * @param length the number of bytes to read. The max is 65,535 (max of u16).
* @returns a vector of bytes from the specified pipe. On error, will be empty. * @returns a vector of bytes from the specified pipe. On error, will be empty.
@ -49,11 +46,7 @@ size_t GetPipeReadableSize(DspPipe pipe_number);
*/ */
void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer); void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer);
enum class DspState { enum class DspState { Off, On, Sleeping };
Off,
On,
Sleeping
};
/// Get the state of the DSP /// Get the state of the DSP
DspState GetDspState(); DspState GetDspState();

View file

@ -18,7 +18,8 @@
namespace DSP { namespace DSP {
namespace HLE { namespace HLE {
SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]) {
ParseConfig(config, adpcm_coeffs); ParseConfig(config, adpcm_coeffs);
if (state.enabled) { if (state.enabled) {
@ -47,7 +48,8 @@ void Source::Reset() {
state = {}; state = {};
} }
void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { void Source::ParseConfig(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]) {
if (!config.dirty_raw) { if (!config.dirty_raw) {
return; return;
} }
@ -82,7 +84,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier);
if (state.rate_multiplier <= 0) { if (state.rate_multiplier <= 0) {
LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier); LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f",
source_id, state.rate_multiplier);
state.rate_multiplier = 1.0f; state.rate_multiplier = 1.0f;
// Note: Actual firmware starts producing garbage if this occurs. // Note: Actual firmware starts producing garbage if this occurs.
} }
@ -90,37 +93,39 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.adpcm_coefficients_dirty) { if (config.adpcm_coefficients_dirty) {
config.adpcm_coefficients_dirty.Assign(0); config.adpcm_coefficients_dirty.Assign(0);
std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(), std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(),
[](const auto& coeff) { return static_cast<s16>(coeff); }); state.adpcm_coeffs.begin(),
[](const auto& coeff) { return static_cast<s16>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id);
} }
if (config.gain_0_dirty) { if (config.gain_0_dirty) {
config.gain_0_dirty.Assign(0); config.gain_0_dirty.Assign(0);
std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(), std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(),
[](const auto& coeff) { return static_cast<float>(coeff); }); [](const auto& coeff) { return static_cast<float>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id);
} }
if (config.gain_1_dirty) { if (config.gain_1_dirty) {
config.gain_1_dirty.Assign(0); config.gain_1_dirty.Assign(0);
std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(), std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(),
[](const auto& coeff) { return static_cast<float>(coeff); }); [](const auto& coeff) { return static_cast<float>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id);
} }
if (config.gain_2_dirty) { if (config.gain_2_dirty) {
config.gain_2_dirty.Assign(0); config.gain_2_dirty.Assign(0);
std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(), std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(),
[](const auto& coeff) { return static_cast<float>(coeff); }); [](const auto& coeff) { return static_cast<float>(coeff); });
LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id); LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id);
} }
if (config.filters_enabled_dirty) { if (config.filters_enabled_dirty) {
config.filters_enabled_dirty.Assign(0); config.filters_enabled_dirty.Assign(0);
state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool()); state.filters.Enable(config.simple_filter_enabled.ToBool(),
LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", config.biquad_filter_enabled.ToBool());
source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", source_id,
config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value());
} }
if (config.simple_filter_dirty) { if (config.simple_filter_dirty) {
@ -138,36 +143,38 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.interpolation_dirty) { if (config.interpolation_dirty) {
config.interpolation_dirty.Assign(0); config.interpolation_dirty.Assign(0);
state.interpolation_mode = config.interpolation_mode; state.interpolation_mode = config.interpolation_mode;
LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode)); LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id,
static_cast<size_t>(state.interpolation_mode));
} }
if (config.format_dirty || config.embedded_buffer_dirty) { if (config.format_dirty || config.embedded_buffer_dirty) {
config.format_dirty.Assign(0); config.format_dirty.Assign(0);
state.format = config.format; state.format = config.format;
LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format)); LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id,
static_cast<size_t>(state.format));
} }
if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) {
config.mono_or_stereo_dirty.Assign(0); config.mono_or_stereo_dirty.Assign(0);
state.mono_or_stereo = config.mono_or_stereo; state.mono_or_stereo = config.mono_or_stereo;
LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo)); LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id,
static_cast<size_t>(state.mono_or_stereo));
} }
if (config.embedded_buffer_dirty) { if (config.embedded_buffer_dirty) {
config.embedded_buffer_dirty.Assign(0); config.embedded_buffer_dirty.Assign(0);
state.input_queue.emplace(Buffer{ state.input_queue.emplace(Buffer{config.physical_address,
config.physical_address, config.length,
config.length, static_cast<u8>(config.adpcm_ps),
static_cast<u8>(config.adpcm_ps), {config.adpcm_yn[0], config.adpcm_yn[1]},
{ config.adpcm_yn[0], config.adpcm_yn[1] }, config.adpcm_dirty.ToBool(),
config.adpcm_dirty.ToBool(), config.is_looping.ToBool(),
config.is_looping.ToBool(), config.buffer_id,
config.buffer_id, state.mono_or_stereo,
state.mono_or_stereo, state.format,
state.format, false});
false LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu",
}); config.physical_address, config.length, config.buffer_id);
LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id);
} }
if (config.buffer_queue_dirty) { if (config.buffer_queue_dirty) {
@ -175,19 +182,18 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
for (size_t i = 0; i < 4; i++) { for (size_t i = 0; i < 4; i++) {
if (config.buffers_dirty & (1 << i)) { if (config.buffers_dirty & (1 << i)) {
const auto& b = config.buffers[i]; const auto& b = config.buffers[i];
state.input_queue.emplace(Buffer{ state.input_queue.emplace(Buffer{b.physical_address,
b.physical_address, b.length,
b.length, static_cast<u8>(b.adpcm_ps),
static_cast<u8>(b.adpcm_ps), {b.adpcm_yn[0], b.adpcm_yn[1]},
{ b.adpcm_yn[0], b.adpcm_yn[1] }, b.adpcm_dirty != 0,
b.adpcm_dirty != 0, b.is_looping != 0,
b.is_looping != 0, b.buffer_id,
b.buffer_id, state.mono_or_stereo,
state.mono_or_stereo, state.format,
state.format, true});
true LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i,
}); b.physical_address, b.length, b.buffer_id);
LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id);
} }
} }
config.buffers_dirty = 0; config.buffers_dirty = 0;
@ -218,10 +224,13 @@ void Source::GenerateFrame() {
break; break;
} }
const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position); const size_t size_to_copy =
std::min(state.current_buffer.size(), current_frame.size() - frame_position);
std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position); std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy,
state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy); current_frame.begin() + frame_position);
state.current_buffer.erase(state.current_buffer.begin(),
state.current_buffer.begin() + size_to_copy);
frame_position += size_to_copy; frame_position += size_to_copy;
state.next_sample_number += static_cast<u32>(size_to_copy); state.next_sample_number += static_cast<u32>(size_to_copy);
@ -230,9 +239,9 @@ void Source::GenerateFrame() {
state.filters.ProcessFrame(current_frame); state.filters.ProcessFrame(current_frame);
} }
bool Source::DequeueBuffer() { bool Source::DequeueBuffer() {
ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer"); ASSERT_MSG(state.current_buffer.empty(),
"Shouldn't dequeue; we still have data in current_buffer");
if (state.input_queue.empty()) if (state.input_queue.empty())
return false; return false;
@ -261,29 +270,34 @@ bool Source::DequeueBuffer() {
break; break;
case Format::ADPCM: case Format::ADPCM:
DEBUG_ASSERT(num_channels == 1); DEBUG_ASSERT(num_channels == 1);
state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); state.current_buffer =
Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state);
break; break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
} }
} else { } else {
LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", LOG_WARNING(Audio_DSP,
source_id, buf.buffer_id, buf.length, buf.physical_address); "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X",
source_id, buf.buffer_id, buf.length, buf.physical_address);
state.current_buffer.clear(); state.current_buffer.clear();
return true; return true;
} }
switch (state.interpolation_mode) { switch (state.interpolation_mode) {
case InterpolationMode::None: case InterpolationMode::None:
state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
case InterpolationMode::Linear: case InterpolationMode::Linear:
state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
case InterpolationMode::Polyphase: case InterpolationMode::Polyphase:
// TODO(merry): Implement polyphase interpolation // TODO(merry): Implement polyphase interpolation
state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); state.current_buffer =
AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
break; break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
@ -296,7 +310,8 @@ bool Source::DequeueBuffer() {
state.buffer_update = buf.from_queue; state.buffer_update = buf.from_queue;
LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu",
source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size()); source_id, buf.buffer_id, buf.from_queue ? "true" : "false",
state.current_buffer.size());
return true; return true;
} }

View file

@ -40,13 +40,17 @@ public:
/** /**
* This is called once every audio frame. This performs per-source processing every frame. * This is called once every audio frame. This performs per-source processing every frame.
* @param config The new configuration we've got for this Source from the application. * @param config The new configuration we've got for this Source from the application.
* @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise). * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain
* @return The current status of this Source. This is given back to the emulated application via SharedMemory. * invalid values otherwise).
* @return The current status of this Source. This is given back to the emulated application via
* SharedMemory.
*/ */
SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); SourceStatus::Status Tick(SourceConfiguration::Configuration& config,
const s16_le (&adpcm_coeffs)[16]);
/** /**
* Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer. * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th
* intermediate mixer.
* @param dest The QuadFrame32 to mix into. * @param dest The QuadFrame32 to mix into.
* @param intermediate_mix_id The id of the intermediate mix whose gains we are using. * @param intermediate_mix_id The id of the intermediate mix whose gains we are using.
*/ */
@ -77,7 +81,7 @@ private:
}; };
struct BufferOrder { struct BufferOrder {
bool operator() (const Buffer& a, const Buffer& b) const { bool operator()(const Buffer& a, const Buffer& b) const {
// Lower buffer_id comes first. // Lower buffer_id comes first.
return a.buffer_id > b.buffer_id; return a.buffer_id > b.buffer_id;
} }
@ -134,7 +138,8 @@ private:
void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
/// INTERNAL: Generate the current audio output for this frame based on our internal state. /// INTERNAL: Generate the current audio output for this frame based on our internal state.
void GenerateFrame(); void GenerateFrame();
/// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer. /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it
/// into current_buffer.
bool DequeueBuffer(); bool DequeueBuffer();
/// INTERNAL: Generates a SourceStatus::Status based on our internal state. /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
SourceStatus::Status GetCurrentStatus(); SourceStatus::Status GetCurrentStatus();

View file

@ -17,7 +17,8 @@ constexpr u64 scale_mask = scale_factor - 1;
/// Here we step over the input in steps of rate_multiplier, until we consume all of the input. /// Here we step over the input in steps of rate_multiplier, until we consume all of the input.
/// Three adjacent samples are passed to fn each step. /// Three adjacent samples are passed to fn each step.
template <typename Function> template <typename Function>
static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) { static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
float rate_multiplier, Function fn) {
ASSERT(rate_multiplier > 0); ASSERT(rate_multiplier > 0);
if (input.size() < 2) if (input.size() < 2)
@ -63,22 +64,21 @@ static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
} }
StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) {
return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return StepOverSamples(
return x0; state, input, rate_multiplier,
}); [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; });
} }
StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) {
// Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0,
const auto& x1, const auto& x2) {
// This is a saturated subtraction. (Verified by black-box fuzzing.) // This is a saturated subtraction. (Verified by black-box fuzzing.)
s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767);
s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767);
return std::array<s16, 2> { return std::array<s16, 2>{static_cast<s16>(x0[0] + fraction * delta0 / scale_factor),
static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), static_cast<s16>(x0[1] + fraction * delta1 / scale_factor)};
static_cast<s16>(x0[1] + fraction * delta1 / scale_factor)
};
}); });
} }

View file

@ -24,7 +24,8 @@ struct State {
* No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
* @param input Input buffer. * @param input Input buffer.
* @param rate_multiplier Stretch factor. Must be a positive non-zero value. * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
* rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
* performs upsampling.
* @return The resampled audio buffer. * @return The resampled audio buffer.
*/ */
StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier);
@ -33,7 +34,8 @@ StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multip
* Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
* @param input Input buffer. * @param input Input buffer.
* @param rate_multiplier Stretch factor. Must be a positive non-zero value. * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
* rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
* performs upsampling.
* @return The resampled audio buffer. * @return The resampled audio buffer.
*/ */
StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier);

View file

@ -19,7 +19,8 @@ public:
return native_sample_rate; return native_sample_rate;
} }
void EnqueueSamples(const s16*, size_t) override {} void EnqueueSamples(const s16*, size_t) override {
}
size_t SamplesInQueue() const override { size_t SamplesInQueue() const override {
return 0; return 0;

View file

@ -10,9 +10,9 @@
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/sdl2_sink.h" #include "audio_core/sdl2_sink.h"
#include <numeric>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include <numeric>
namespace AudioCore { namespace AudioCore {
@ -45,7 +45,8 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
SDL_AudioSpec obtained_audiospec; SDL_AudioSpec obtained_audiospec;
SDL_zero(obtained_audiospec); SDL_zero(obtained_audiospec);
impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); impl->audio_device_id =
SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
if (impl->audio_device_id <= 0) { if (impl->audio_device_id <= 0) {
LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed");
return; return;
@ -86,11 +87,12 @@ size_t SDL2Sink::SamplesInQueue() const {
SDL_LockAudioDevice(impl->audio_device_id); SDL_LockAudioDevice(impl->audio_device_id);
size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(),
[](size_t sum, const auto& buffer) { static_cast<size_t>(0), [](size_t sum, const auto& buffer) {
// Division by two because each stereo sample is made of two s16. // Division by two because each stereo sample is made of
return sum + buffer.size() / 2; // two s16.
}); return sum + buffer.size() / 2;
});
SDL_UnlockAudioDevice(impl->audio_device_id); SDL_UnlockAudioDevice(impl->audio_device_id);
@ -100,7 +102,8 @@ size_t SDL2Sink::SamplesInQueue() const {
void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
Impl* impl = reinterpret_cast<Impl*>(impl_); Impl* impl = reinterpret_cast<Impl*>(impl_);
size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments. size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) /
sizeof(s16); // Keep track of size in 16-bit increments.
while (remaining_size > 0 && !impl->queue.empty()) { while (remaining_size > 0 && !impl->queue.empty()) {
if (impl->queue.front().size() <= remaining_size) { if (impl->queue.front().size() <= remaining_size) {
@ -111,7 +114,8 @@ void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes)
} else { } else {
memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16));
buffer += remaining_size * sizeof(s16); buffer += remaining_size * sizeof(s16);
impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); impl->queue.front().erase(impl->queue.front().begin(),
impl->queue.front().begin() + remaining_size);
remaining_size = 0; remaining_size = 0;
} }
} }

View file

@ -11,14 +11,16 @@
namespace AudioCore { namespace AudioCore {
/** /**
* This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output. * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
* PCM16 format to be output.
* Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs. * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs.
*/ */
class Sink { class Sink {
public: public:
virtual ~Sink() = default; virtual ~Sink() = default;
/// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units:
/// samples/sec)
virtual unsigned int GetNativeSampleRate() const = 0; virtual unsigned int GetNativeSampleRate() const = 0;
/** /**

View file

@ -17,9 +17,9 @@ namespace AudioCore {
// g_sink_details is ordered in terms of desirability, with the best choice at the top. // g_sink_details is ordered in terms of desirability, with the best choice at the top.
const std::vector<SinkDetails> g_sink_details = { const std::vector<SinkDetails> g_sink_details = {
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
{ "sdl2", []() { return std::make_unique<SDL2Sink>(); } }, {"sdl2", []() { return std::make_unique<SDL2Sink>(); }},
#endif #endif
{ "null", []() { return std::make_unique<NullSink>(); } }, {"null", []() { return std::make_unique<NullSink>(); }},
}; };
} // namespace AudioCore } // namespace AudioCore

View file

@ -14,7 +14,8 @@ class Sink;
struct SinkDetails { struct SinkDetails {
SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>()> factory_) SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>()> factory_)
: id(id_), factory(factory_) {} : id(id_), factory(factory_) {
}
/// Name for this sink. /// Name for this sink.
const char* id; const char* id;

View file

@ -26,8 +26,8 @@ static double ClampRatio(double ratio) {
return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO); return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO);
} }
constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds
constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds
constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples
constexpr double SMOOTHING_FACTOR = 0.007; constexpr double SMOOTHING_FACTOR = 0.007;
@ -48,7 +48,8 @@ std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) {
double ratio = CalculateCurrentRatio(); double ratio = CalculateCurrentRatio();
ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue); ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue);
impl->smoothed_ratio = (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; impl->smoothed_ratio =
(1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio;
impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio); impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio);
// SoundTouch's tempo definition the inverse of our ratio definition. // SoundTouch's tempo definition the inverse of our ratio definition.
@ -100,7 +101,8 @@ double TimeStretcher::CalculateCurrentRatio() {
const steady_clock::time_point now = steady_clock::now(); const steady_clock::time_point now = steady_clock::now();
const std::chrono::duration<double> duration = now - impl->frame_timer; const std::chrono::duration<double> duration = now - impl->frame_timer;
const double expected_time = static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); const double expected_time =
static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate);
const double actual_time = duration.count(); const double actual_time = duration.count();
double ratio; double ratio;

View file

@ -37,7 +37,8 @@ public:
/** /**
* Does audio stretching and produces the time-stretched samples. * Does audio stretching and produces the time-stretched samples.
* Timer calculations use sample_delay to determine how much of a margin we have. * Timer calculations use sample_delay to determine how much of a margin we have.
* @param sample_delay How many samples are buffered downstream of this module and haven't been played yet. * @param sample_delay How many samples are buffered downstream of this module and haven't been
* played yet.
* @return Samples to play in interleaved stereo PCM16 format. * @return Samples to play in interleaved stereo PCM16 format.
*/ */
std::vector<s16> Process(size_t sample_delay); std::vector<s16> Process(size_t sample_delay);
@ -48,7 +49,8 @@ private:
/// INTERNAL: ratio = wallclock time / emulated time /// INTERNAL: ratio = wallclock time / emulated time
double CalculateCurrentRatio(); double CalculateCurrentRatio();
/// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate direction. /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate
/// direction.
double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const; double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const;
/// INTERNAL: Gets the time-stretched samples from SoundTouch. /// INTERNAL: Gets the time-stretched samples from SoundTouch.
std::vector<s16> GetSamples(); std::vector<s16> GetSamples();

View file

@ -2,10 +2,10 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <string>
#include <thread>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string>
#include <thread>
// This needs to be included before getopt.h because the latter #defines symbols used by it // This needs to be included before getopt.h because the latter #defines symbols used by it
#include "common/microprofile.h" #include "common/microprofile.h"
@ -13,53 +13,51 @@
#ifdef _MSC_VER #ifdef _MSC_VER
#include <getopt.h> #include <getopt.h>
#else #else
#include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <unistd.h>
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <Windows.h>
#endif #endif
#include "common/logging/log.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/settings.h"
#include "core/system.h"
#include "core/core.h" #include "core/core.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/settings.h"
#include "core/system.h"
#include "citra/config.h" #include "citra/config.h"
#include "citra/emu_window/emu_window_sdl2.h" #include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
static void PrintHelp(const char* argv0) {
static void PrintHelp(const char *argv0) std::cout << "Usage: " << argv0
{ << " [options] <filename>\n"
std::cout << "Usage: " << argv0 << " [options] <filename>\n"
"-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
"-h, --help Display this help and exit\n" "-h, --help Display this help and exit\n"
"-v, --version Output version information and exit\n"; "-v, --version Output version information and exit\n";
} }
static void PrintVersion() static void PrintVersion() {
{
std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
} }
/// Application entry point /// Application entry point
int main(int argc, char **argv) { int main(int argc, char** argv) {
Config config; Config config;
int option_index = 0; int option_index = 0;
bool use_gdbstub = Settings::values.use_gdbstub; bool use_gdbstub = Settings::values.use_gdbstub;
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
char *endarg; char* endarg;
#ifdef _WIN32 #ifdef _WIN32
int argc_w; int argc_w;
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@ -71,12 +69,10 @@ int main(int argc, char **argv) {
#endif #endif
std::string boot_filename; std::string boot_filename;
static struct option long_options[] = { static struct option long_options[] = {{"gdbport", required_argument, 0, 'g'},
{ "gdbport", required_argument, 0, 'g' }, {"help", no_argument, 0, 'h'},
{ "help", no_argument, 0, 'h' }, {"version", no_argument, 0, 'v'},
{ "version", no_argument, 0, 'v' }, {0, 0, 0, 0}};
{ 0, 0, 0, 0 }
};
while (optind < argc) { while (optind < argc) {
char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index); char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index);
@ -86,7 +82,8 @@ int main(int argc, char **argv) {
errno = 0; errno = 0;
gdb_port = strtoul(optarg, &endarg, 0); gdb_port = strtoul(optarg, &endarg, 0);
use_gdbstub = true; use_gdbstub = true;
if (endarg == optarg) errno = EINVAL; if (endarg == optarg)
errno = EINVAL;
if (errno != 0) { if (errno != 0) {
perror("--gdbport"); perror("--gdbport");
exit(1); exit(1);

View file

@ -45,15 +45,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
// directly mapped keys // directly mapped keys
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T,
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J,
SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_L,
SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L,
// indirectly mapped keys // indirectly mapped keys
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
SDL_SCANCODE_D,
}; };
void Config::ReadValues() { void Config::ReadValues() {
@ -62,7 +60,8 @@ void Config::ReadValues() {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
} }
Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); Settings::values.pad_circle_modifier_scale =
(float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
// Core // Core
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
@ -71,19 +70,22 @@ void Config::ReadValues() {
// Renderer // Renderer
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); Settings::values.use_scaled_resolution =
sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false);
Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false);
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0);
// Audio // Audio
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); Settings::values.enable_audio_stretching =
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
// Data Storage // Data Storage
Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); Settings::values.use_virtual_sd =
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
// System // System
Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
@ -94,7 +96,8 @@ void Config::ReadValues() {
// Debugging // Debugging
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); Settings::values.gdbstub_port =
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
} }
void Config::Reload() { void Config::Reload() {

View file

@ -13,8 +13,9 @@ class Config {
std::unique_ptr<INIReader> sdl2_config; std::unique_ptr<INIReader> sdl2_config;
std::string sdl2_config_loc; std::string sdl2_config_loc;
bool LoadINI(const std::string& default_contents="", bool retry=true); bool LoadINI(const std::string& default_contents = "", bool retry = true);
void ReadValues(); void ReadValues();
public: public:
Config(); Config();

View file

@ -104,5 +104,4 @@ log_filter = *:Info
use_gdbstub=false use_gdbstub=false
gdbstub_port=24689 gdbstub_port=24689
)"; )";
} }

View file

@ -16,8 +16,8 @@
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/settings.h"
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid.h"
#include "core/settings.h"
#include "citra/emu_window/emu_window_sdl2.h" #include "citra/emu_window/emu_window_sdl2.h"
@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
KeyMap::PressKey(*this, { key, keyboard_id }); KeyMap::PressKey(*this, {key, keyboard_id});
} else if (state == SDL_RELEASED) { } else if (state == SDL_RELEASED) {
KeyMap::ReleaseKey(*this, { key, keyboard_id }); KeyMap::ReleaseKey(*this, {key, keyboard_id});
} }
} }
@ -55,7 +55,8 @@ void EmuWindow_SDL2::OnResize() {
SDL_GetWindowSize(render_window, &width, &height); SDL_GetWindowSize(render_window, &width, &height);
NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); NotifyFramebufferLayoutChanged(
EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
} }
EmuWindow_SDL2::EmuWindow_SDL2() { EmuWindow_SDL2::EmuWindow_SDL2() {
@ -80,12 +81,13 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
render_window = SDL_CreateWindow(window_title.c_str(), Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
render_window = SDL_CreateWindow(
window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // x position SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position SDL_WINDOWPOS_UNDEFINED, // y position
VideoCore::kScreenTopWidth, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (render_window == nullptr) { if (render_window == nullptr) {
@ -171,10 +173,13 @@ void EmuWindow_SDL2::DoneCurrent() {
void EmuWindow_SDL2::ReloadSetKeymaps() { void EmuWindow_SDL2::ReloadSetKeymaps() {
KeyMap::ClearKeyMapping(keyboard_id); KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); KeyMap::SetKeyMapping(
{Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
KeyMap::mapping_targets[i]);
} }
} }
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) { void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
} }

View file

@ -47,7 +47,8 @@ private:
void OnResize(); void OnResize();
/// Called when a configuration change affects the minimal size of the window /// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override; void
OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override;
/// Is the window still open? /// Is the window still open?
bool is_open = true; bool is_open = true;
@ -55,7 +56,7 @@ private:
/// Internal SDL2 render window /// Internal SDL2 render window
SDL_Window* render_window; SDL_Window* render_window;
using SDL_GLContext = void *; using SDL_GLContext = void*;
/// The OpenGL context associated with the window /// The OpenGL context associated with the window
SDL_GLContext gl_context; SDL_GLContext gl_context;

View file

@ -2,15 +2,15 @@
// Microsoft Visual C++ generated include file. // Microsoft Visual C++ generated include file.
// Used by pcafe.rc // Used by pcafe.rc
// //
#define IDI_ICON3 103 #define IDI_ICON3 103
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

View file

@ -22,13 +22,13 @@
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
#define APP_NAME "citra" #define APP_NAME "citra"
#define APP_VERSION "0.1-" VERSION #define APP_VERSION "0.1-" VERSION
#define APP_TITLE APP_NAME " " APP_VERSION #define APP_TITLE APP_NAME " " APP_VERSION
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
EmuThread::EmuThread(GRenderWindow* render_window) : EmuThread::EmuThread(GRenderWindow* render_window)
exec_step(false), running(false), stop_run(false), render_window(render_window) { : exec_step(false), running(false), stop_run(false), render_window(render_window) {
} }
void EmuThread::run() { void EmuThread::run() {
@ -64,7 +64,7 @@ void EmuThread::run() {
was_active = false; was_active = false;
} else { } else {
std::unique_lock<std::mutex> lock(running_mutex); std::unique_lock<std::mutex> lock(running_mutex);
running_cv.wait(lock, [this]{ return IsRunning() || exec_step || stop_run; }); running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
} }
} }
@ -78,13 +78,13 @@ void EmuThread::run() {
render_window->moveContext(); render_window->moveContext();
} }
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
// context.
// The corresponding functionality is handled in EmuThread instead // The corresponding functionality is handled in EmuThread instead
class GGLWidgetInternal : public QGLWidget class GGLWidgetInternal : public QGLWidget {
{
public: public:
GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
: QGLWidget(fmt, parent), parent(parent) { : QGLWidget(fmt, parent), parent(parent) {
} }
void paintEvent(QPaintEvent* ev) override { void paintEvent(QPaintEvent* ev) override {
@ -98,37 +98,43 @@ public:
parent->OnFramebufferSizeChanged(); parent->OnFramebufferSizeChanged();
} }
void DisablePainting() { do_painting = false; } void DisablePainting() {
void EnablePainting() { do_painting = true; } do_painting = false;
}
void EnablePainting() {
do_painting = true;
}
private: private:
GRenderWindow* parent; GRenderWindow* parent;
bool do_painting; bool do_painting;
}; };
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) { : QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) {
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(QString::fromStdString(window_title)); setWindowTitle(QString::fromStdString(window_title));
keyboard_id = KeyMap::NewDeviceId(); keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps(); ReloadSetKeymaps();
} }
void GRenderWindow::moveContext() void GRenderWindow::moveContext() {
{
DoneCurrent(); DoneCurrent();
// We need to move GL context to the swapping thread in Qt5 // We need to move GL context to the swapping thread in Qt5
#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
// If the thread started running, move the GL Context to the new thread. Otherwise, move it back. // If the thread started running, move the GL Context to the new thread. Otherwise, move it
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread(); // back.
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
? emu_thread
: qApp->thread();
child->context()->moveToThread(thread); child->context()->moveToThread(thread);
#endif #endif
} }
void GRenderWindow::SwapBuffers() void GRenderWindow::SwapBuffers() {
{
#if !defined(QT_NO_DEBUG) #if !defined(QT_NO_DEBUG)
// Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent
// since the last time you called swapBuffers. This presumably means something if you're using // since the last time you called swapBuffers. This presumably means something if you're using
@ -139,13 +145,11 @@ void GRenderWindow::SwapBuffers()
child->swapBuffers(); child->swapBuffers();
} }
void GRenderWindow::MakeCurrent() void GRenderWindow::MakeCurrent() {
{
child->makeCurrent(); child->makeCurrent();
} }
void GRenderWindow::DoneCurrent() void GRenderWindow::DoneCurrent() {
{
child->doneCurrent(); child->doneCurrent();
} }
@ -157,36 +161,33 @@ void GRenderWindow::PollEvents() {
// Older versions get the window size (density independent pixels), // Older versions get the window size (density independent pixels),
// and hence, do not support DPI scaling ("retina" displays). // and hence, do not support DPI scaling ("retina" displays).
// The result will be a viewport that is smaller than the extent of the window. // The result will be a viewport that is smaller than the extent of the window.
void GRenderWindow::OnFramebufferSizeChanged() void GRenderWindow::OnFramebufferSizeChanged() {
{ // Screen changes potentially incur a change in screen DPI, hence we should update the
// Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size // framebuffer size
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
unsigned width = child->QPaintDevice::width() * pixelRatio; unsigned width = child->QPaintDevice::width() * pixelRatio;
unsigned height = child->QPaintDevice::height() * pixelRatio; unsigned height = child->QPaintDevice::height() * pixelRatio;
NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); NotifyFramebufferLayoutChanged(
EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
} }
void GRenderWindow::BackupGeometry() void GRenderWindow::BackupGeometry() {
{
geometry = ((QGLWidget*)this)->saveGeometry(); geometry = ((QGLWidget*)this)->saveGeometry();
} }
void GRenderWindow::RestoreGeometry() void GRenderWindow::RestoreGeometry() {
{
// We don't want to back up the geometry here (obviously) // We don't want to back up the geometry here (obviously)
QWidget::restoreGeometry(geometry); QWidget::restoreGeometry(geometry);
} }
void GRenderWindow::restoreGeometry(const QByteArray& geometry) void GRenderWindow::restoreGeometry(const QByteArray& geometry) {
{
// Make sure users of this class don't need to deal with backing up the geometry themselves // Make sure users of this class don't need to deal with backing up the geometry themselves
QWidget::restoreGeometry(geometry); QWidget::restoreGeometry(geometry);
BackupGeometry(); BackupGeometry();
} }
QByteArray GRenderWindow::saveGeometry() QByteArray GRenderWindow::saveGeometry() {
{
// If we are a top-level widget, store the current geometry // If we are a top-level widget, store the current geometry
// otherwise, store the last backup // otherwise, store the last backup
if (parent() == nullptr) if (parent() == nullptr)
@ -195,8 +196,7 @@ QByteArray GRenderWindow::saveGeometry()
return geometry; return geometry;
} }
qreal GRenderWindow::windowPixelRatio() qreal GRenderWindow::windowPixelRatio() {
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
// windowHandle() might not be accessible until the window is displayed to screen. // windowHandle() might not be accessible until the window is displayed to screen.
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
@ -210,20 +210,16 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event); QWidget::closeEvent(event);
} }
void GRenderWindow::keyPressEvent(QKeyEvent* event) void GRenderWindow::keyPressEvent(QKeyEvent* event) {
{ KeyMap::PressKey(*this, {event->key(), keyboard_id});
KeyMap::PressKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
{ KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::mousePressEvent(QMouseEvent *event) void GRenderWindow::mousePressEvent(QMouseEvent* event) {
{ if (event->button() == Qt::LeftButton) {
if (event->button() == Qt::LeftButton)
{
auto pos = event->pos(); auto pos = event->pos();
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
@ -231,30 +227,28 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event)
} }
} }
void GRenderWindow::mouseMoveEvent(QMouseEvent *event) void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
{
auto pos = event->pos(); auto pos = event->pos();
qreal pixelRatio = windowPixelRatio(); qreal pixelRatio = windowPixelRatio();
this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
} }
void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
{
if (event->button() == Qt::LeftButton) if (event->button() == Qt::LeftButton)
this->TouchReleased(); this->TouchReleased();
} }
void GRenderWindow::ReloadSetKeymaps() void GRenderWindow::ReloadSetKeymaps() {
{
KeyMap::ClearKeyMapping(keyboard_id); KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); KeyMap::SetKeyMapping(
{Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
KeyMap::mapping_targets[i]);
} }
} }
void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
{
NotifyClientAreaSizeChanged(std::make_pair(width, height)); NotifyClientAreaSizeChanged(std::make_pair(width, height));
} }
@ -267,7 +261,8 @@ void GRenderWindow::InitRenderTarget() {
delete layout(); delete layout();
} }
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
QGLFormat fmt; QGLFormat fmt;
fmt.setVersion(3, 3); fmt.setVersion(3, 3);
fmt.setProfile(QGLFormat::CoreProfile); fmt.setProfile(QGLFormat::CoreProfile);
@ -279,7 +274,8 @@ void GRenderWindow::InitRenderTarget() {
child = new GGLWidgetInternal(fmt, this); child = new GGLWidgetInternal(fmt, this);
QBoxLayout* layout = new QHBoxLayout(this); QBoxLayout* layout = new QHBoxLayout(this);
resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); resize(VideoCore::kScreenTopWidth,
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
layout->addWidget(child); layout->addWidget(child);
layout->setMargin(0); layout->setMargin(0);
setLayout(layout); setLayout(layout);
@ -292,7 +288,8 @@ void GRenderWindow::InitRenderTarget() {
BackupGeometry(); BackupGeometry();
} }
void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { void GRenderWindow::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
setMinimumSize(minimal_size.first, minimal_size.second); setMinimumSize(minimal_size.first, minimal_size.second);
} }
@ -306,11 +303,12 @@ void GRenderWindow::OnEmulationStopping() {
child->EnablePainting(); child->EnablePainting();
} }
void GRenderWindow::showEvent(QShowEvent * event) { void GRenderWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event); QWidget::showEvent(event);
// windowHandle() is not initialized until the Window is shown, so we connect it here. // windowHandle() is not initialized until the Window is shown, so we connect it here.
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection); connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this,
#endif SLOT(OnFramebufferSizeChanged()), Qt::UniqueConnection);
#endif
} }

View file

@ -19,8 +19,7 @@ class GGLWidgetInternal;
class GMainWindow; class GMainWindow;
class GRenderWindow; class GRenderWindow;
class EmuThread : public QThread class EmuThread : public QThread {
{
Q_OBJECT Q_OBJECT
public: public:
@ -58,7 +57,9 @@ public:
* @return True if the emulation thread is running, otherwise false * @return True if the emulation thread is running, otherwise false
* @note This function is thread-safe * @note This function is thread-safe
*/ */
bool IsRunning() { return running; } bool IsRunning() {
return running;
}
/** /**
* Requests for the emulation thread to stop running * Requests for the emulation thread to stop running
@ -81,20 +82,23 @@ signals:
/** /**
* Emitted when the CPU has halted execution * Emitted when the CPU has halted execution
* *
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) * @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/ */
void DebugModeEntered(); void DebugModeEntered();
/** /**
* Emitted right before the CPU continues execution * Emitted right before the CPU continues execution
* *
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) * @warning When connecting to this signal from other threads, make sure to specify either
* Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/ */
void DebugModeLeft(); void DebugModeLeft();
}; };
class GRenderWindow : public QWidget, public EmuWindow class GRenderWindow : public QWidget, public EmuWindow {
{
Q_OBJECT Q_OBJECT
public: public:
@ -109,7 +113,7 @@ public:
void BackupGeometry(); void BackupGeometry();
void RestoreGeometry(); void RestoreGeometry();
void restoreGeometry(const QByteArray& geometry); // overridden void restoreGeometry(const QByteArray& geometry); // overridden
QByteArray saveGeometry(); // overridden QByteArray saveGeometry(); // overridden
qreal windowPixelRatio(); qreal windowPixelRatio();
@ -118,9 +122,9 @@ public:
void keyPressEvent(QKeyEvent* event) override; void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent* event) override;
void ReloadSetKeymaps() override; void ReloadSetKeymaps() override;
@ -129,7 +133,7 @@ public:
void InitRenderTarget(); void InitRenderTarget();
public slots: public slots:
void moveContext(); // overridden void moveContext(); // overridden
void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStarting(EmuThread* emu_thread);
void OnEmulationStopping(); void OnEmulationStopping();
@ -140,7 +144,8 @@ signals:
void Closed(); void Closed();
private: private:
void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; void
OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override;
GGLWidgetInternal* child; GGLWidgetInternal* child;

View file

@ -20,24 +20,23 @@ Config::Config() {
const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
// directly mapped keys // directly mapped keys
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I,
Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_K, Qt::Key_J, Qt::Key_L,
Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
// indirectly mapped keys // indirectly mapped keys
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
Qt::Key_D,
}; };
void Config::ReadValues() { void Config::ReadValues() {
qt_config->beginGroup("Controls"); qt_config->beginGroup("Controls");
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt(); qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i])
.toInt();
} }
Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); Settings::values.pad_circle_modifier_scale =
qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");
@ -48,17 +47,19 @@ void Config::ReadValues() {
qt_config->beginGroup("Renderer"); qt_config->beginGroup("Renderer");
Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool();
Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool();
Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); Settings::values.use_scaled_resolution =
qt_config->value("use_scaled_resolution", false).toBool();
Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool();
Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat();
Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat();
Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Audio"); qt_config->beginGroup("Audio");
Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
Settings::values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool(); Settings::values.enable_audio_stretching =
qt_config->value("enable_audio_stretching", true).toBool();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Data Storage"); qt_config->beginGroup("Data Storage");
@ -84,10 +85,14 @@ void Config::ReadValues() {
qt_config->beginGroup("UILayout"); qt_config->beginGroup("UILayout");
UISettings::values.geometry = qt_config->value("geometry").toByteArray(); UISettings::values.geometry = qt_config->value("geometry").toByteArray();
UISettings::values.state = qt_config->value("state").toByteArray(); UISettings::values.state = qt_config->value("state").toByteArray();
UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray(); UISettings::values.renderwindow_geometry =
UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray(); qt_config->value("geometryRenderWindow").toByteArray();
UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray(); UISettings::values.gamelist_header_state =
UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool(); qt_config->value("gameListHeaderState").toByteArray();
UISettings::values.microprofile_geometry =
qt_config->value("microProfileDialogGeometry").toByteArray();
UISettings::values.microprofile_visible =
qt_config->value("microProfileDialogVisible", false).toBool();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Paths"); qt_config->beginGroup("Paths");
@ -106,10 +111,10 @@ void Config::ReadValues() {
QStringList hotkeys = qt_config->childGroups(); QStringList hotkeys = qt_config->childGroups();
for (auto hotkey : hotkeys) { for (auto hotkey : hotkeys) {
qt_config->beginGroup(hotkey); qt_config->beginGroup(hotkey);
UISettings::values.shortcuts.emplace_back( UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
UISettings::Shortcut(group + "/" + hotkey, group + "/" + hotkey,
UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
qt_config->value("Context").toInt()))); qt_config->value("Context").toInt())));
qt_config->endGroup(); qt_config->endGroup();
} }
@ -119,7 +124,7 @@ void Config::ReadValues() {
UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool(); UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
qt_config->endGroup(); qt_config->endGroup();
@ -129,9 +134,10 @@ void Config::SaveValues() {
qt_config->beginGroup("Controls"); qt_config->beginGroup("Controls");
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
Settings::values.input_mappings[Settings::NativeInput::All[i]]); Settings::values.input_mappings[Settings::NativeInput::All[i]]);
} }
qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale); qt_config->setValue("pad_circle_modifier_scale",
(double)Settings::values.pad_circle_modifier_scale);
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");
@ -146,9 +152,9 @@ void Config::SaveValues() {
qt_config->setValue("use_vsync", Settings::values.use_vsync); qt_config->setValue("use_vsync", Settings::values.use_vsync);
// Cast to double because Qt's written float values are not human-readable // Cast to double because Qt's written float values are not human-readable
qt_config->setValue("bg_red", (double)Settings::values.bg_red); qt_config->setValue("bg_red", (double)Settings::values.bg_red);
qt_config->setValue("bg_green", (double)Settings::values.bg_green); qt_config->setValue("bg_green", (double)Settings::values.bg_green);
qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Audio"); qt_config->beginGroup("Audio");

View file

@ -4,8 +4,8 @@
#pragma once #pragma once
#include <string>
#include <QVariant> #include <QVariant>
#include <string>
#include "core/settings.h" #include "core/settings.h"
@ -17,6 +17,7 @@ class Config {
void ReadValues(); void ReadValues();
void SaveValues(); void SaveValues();
public: public:
Config(); Config();
~Config(); ~Config();

View file

@ -9,10 +9,8 @@
#include "core/settings.h" #include "core/settings.h"
ConfigureAudio::ConfigureAudio(QWidget* parent) : ConfigureAudio::ConfigureAudio(QWidget* parent)
QWidget(parent), : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
ui(std::make_unique<Ui::ConfigureAudio>())
{
ui->setupUi(this); ui->setupUi(this);
ui->output_sink_combo_box->clear(); ui->output_sink_combo_box->clear();
@ -41,7 +39,9 @@ void ConfigureAudio::setConfiguration() {
} }
void ConfigureAudio::applyConfiguration() { void ConfigureAudio::applyConfiguration() {
Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString(); Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
Settings::Apply(); Settings::Apply();
} }

View file

@ -4,8 +4,8 @@
#pragma once #pragma once
#include <memory>
#include <QWidget> #include <QWidget>
#include <memory>
namespace Ui { namespace Ui {
class ConfigureAudio; class ConfigureAudio;

View file

@ -7,10 +7,7 @@
#include "core/settings.h" #include "core/settings.h"
ConfigureDebug::ConfigureDebug(QWidget *parent) : ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
QWidget(parent),
ui(new Ui::ConfigureDebug)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
} }

View file

@ -4,19 +4,18 @@
#pragma once #pragma once
#include <memory>
#include <QWidget> #include <QWidget>
#include <memory>
namespace Ui { namespace Ui {
class ConfigureDebug; class ConfigureDebug;
} }
class ConfigureDebug : public QWidget class ConfigureDebug : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDebug(QWidget *parent = nullptr); explicit ConfigureDebug(QWidget* parent = nullptr);
~ConfigureDebug(); ~ConfigureDebug();
void applyConfiguration(); void applyConfiguration();

View file

@ -6,13 +6,9 @@
#include "citra_qt/configure_dialog.h" #include "citra_qt/configure_dialog.h"
#include "ui_configure.h" #include "ui_configure.h"
#include "core/settings.h" #include "core/settings.h"
ConfigureDialog::ConfigureDialog(QWidget *parent) : ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
QDialog(parent),
ui(new Ui::ConfigureDialog)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
} }

View file

@ -4,19 +4,18 @@
#pragma once #pragma once
#include <memory>
#include <QDialog> #include <QDialog>
#include <memory>
namespace Ui { namespace Ui {
class ConfigureDialog; class ConfigureDialog;
} }
class ConfigureDialog : public QDialog class ConfigureDialog : public QDialog {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDialog(QWidget *parent); explicit ConfigureDialog(QWidget* parent);
~ConfigureDialog(); ~ConfigureDialog();
void applyConfiguration(); void applyConfiguration();

View file

@ -9,10 +9,8 @@
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
ConfigureGeneral::ConfigureGeneral(QWidget *parent) : ConfigureGeneral::ConfigureGeneral(QWidget* parent)
QWidget(parent), : QWidget(parent), ui(new Ui::ConfigureGeneral) {
ui(new Ui::ConfigureGeneral)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();

View file

@ -4,19 +4,18 @@
#pragma once #pragma once
#include <memory>
#include <QWidget> #include <QWidget>
#include <memory>
namespace Ui { namespace Ui {
class ConfigureGeneral; class ConfigureGeneral;
} }
class ConfigureGeneral : public QWidget class ConfigureGeneral : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGeneral(QWidget *parent = nullptr); explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral(); ~ConfigureGeneral();
void applyConfiguration(); void applyConfiguration();

View file

@ -8,10 +8,8 @@
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
ConfigureGraphics::ConfigureGraphics(QWidget *parent) : ConfigureGraphics::ConfigureGraphics(QWidget* parent)
QWidget(parent), : QWidget(parent), ui(new Ui::ConfigureGraphics) {
ui(new Ui::ConfigureGraphics)
{
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();

View file

@ -4,19 +4,18 @@
#pragma once #pragma once
#include <memory>
#include <QWidget> #include <QWidget>
#include <memory>
namespace Ui { namespace Ui {
class ConfigureGraphics; class ConfigureGraphics;
} }
class ConfigureGraphics : public QWidget class ConfigureGraphics : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGraphics(QWidget *parent = nullptr); explicit ConfigureGraphics(QWidget* parent = nullptr);
~ConfigureGraphics(); ~ConfigureGraphics();
void applyConfiguration(); void applyConfiguration();

View file

@ -2,41 +2,42 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QTimer>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <QTimer>
#include "citra_qt/configure_input.h" #include "citra_qt/configure_input.h"
ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this); ui->setupUi(this);
// Initialize mapping of input enum to UI button. // Initialize mapping of input enum to UI button.
input_mapping = { input_mapping = {
{ std::make_pair(Settings::NativeInput::Values::A, ui->buttonA) }, {std::make_pair(Settings::NativeInput::Values::A, ui->buttonA)},
{ std::make_pair(Settings::NativeInput::Values::B, ui->buttonB) }, {std::make_pair(Settings::NativeInput::Values::B, ui->buttonB)},
{ std::make_pair(Settings::NativeInput::Values::X, ui->buttonX) }, {std::make_pair(Settings::NativeInput::Values::X, ui->buttonX)},
{ std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY) }, {std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY)},
{ std::make_pair(Settings::NativeInput::Values::L, ui->buttonL) }, {std::make_pair(Settings::NativeInput::Values::L, ui->buttonL)},
{ std::make_pair(Settings::NativeInput::Values::R, ui->buttonR) }, {std::make_pair(Settings::NativeInput::Values::R, ui->buttonR)},
{ std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL) }, {std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL)},
{ std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR) }, {std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR)},
{ std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart) }, {std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart)},
{ std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect) }, {std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect)},
{ std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome) }, {std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome)},
{ std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp) }, {std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp)},
{ std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown) }, {std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown)},
{ std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft) }, {std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft)},
{ std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight) }, {std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight)},
{ std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp) }, {std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp)},
{ std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown) }, {std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown)},
{ std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft) }, {std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft)},
{ std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight) }, {std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight)},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp) }, {std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp)},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown) }, {std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown)},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft) }, {std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft)},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) }, {std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight)},
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod) }, {std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod)},
}; };
// Attach handle click method to each button click. // Attach handle click method to each button click.
@ -47,7 +48,10 @@ ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
timer = new QTimer(this); timer = new QTimer(this);
timer->setSingleShot(true); timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [&]() { key_pressed = Qt::Key_Escape; setKey(); }); connect(timer, &QTimer::timeout, this, [&]() {
key_pressed = Qt::Key_Escape;
setKey();
});
this->setConfiguration(); this->setConfiguration();
} }
@ -59,7 +63,7 @@ void ConfigureInput::handleClick() {
grabKeyboard(); grabKeyboard();
grabMouse(); grabMouse();
changing_button = sender; changing_button = sender;
timer->start(5000); //Cancel after 5 seconds timer->start(5000); // Cancel after 5 seconds
} }
void ConfigureInput::applyConfiguration() { void ConfigureInput::applyConfiguration() {

View file

@ -4,8 +4,8 @@
#pragma once #pragma once
#include <QWidget>
#include <QKeyEvent> #include <QKeyEvent>
#include <QWidget>
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "core/settings.h" #include "core/settings.h"
@ -16,7 +16,7 @@ class QString;
class QTimer; class QTimer;
namespace Ui { namespace Ui {
class ConfigureInput; class ConfigureInput;
} }
class ConfigureInput : public QWidget { class ConfigureInput : public QWidget {
@ -39,7 +39,8 @@ private:
/// Load configuration settings into button text /// Load configuration settings into button text
void setConfiguration(); void setConfiguration();
/// Check all inputs for duplicate keys. Clears out any other button with the same value as this button's new value. /// Check all inputs for duplicate keys. Clears out any other button with the same value as this
/// button's new value.
void removeDuplicates(const QString& newValue); void removeDuplicates(const QString& newValue);
/// Handle key press event for input tab when a button is 'waiting'. /// Handle key press event for input tab when a button is 'waiting'.

View file

@ -6,19 +6,16 @@
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "ui_configure_system.h" #include "ui_configure_system.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/system.h" #include "core/system.h"
static const std::array<int, 12> days_in_month = {{ static const std::array<int, 12> days_in_month = {{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}};
ConfigureSystem::ConfigureSystem(QWidget *parent) : ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
QWidget(parent),
ui(new Ui::ConfigureSystem) {
ui->setupUi(this); ui->setupUi(this);
connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int))); connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)),
SLOT(updateBirthdayComboBox(int)));
this->setConfiguration(); this->setConfiguration();
} }
@ -54,13 +51,17 @@ void ConfigureSystem::setConfiguration() {
void ConfigureSystem::ReadSystemSettings() { void ConfigureSystem::ReadSystemSettings() {
// set username // set username
username = Service::CFG::GetUsername(); username = Service::CFG::GetUsername();
// ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5 // ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this
ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); // when we move to Qt 5.5
ui->edit_username->setText(
QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
// set birthday // set birthday
std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); std::tie(birthmonth, birthday) = Service::CFG::GetBirthday();
ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); ui->combo_birthmonth->setCurrentIndex(birthmonth - 1);
updateBirthdayComboBox(birthmonth - 1); // explicitly update it because the signal from setCurrentIndex is not reliable updateBirthdayComboBox(
birthmonth -
1); // explicitly update it because the signal from setCurrentIndex is not reliable
ui->combo_birthday->setCurrentIndex(birthday - 1); ui->combo_birthday->setCurrentIndex(birthday - 1);
// set system language // set system language
@ -79,8 +80,10 @@ void ConfigureSystem::applyConfiguration() {
bool modified = false; bool modified = false;
// apply username // apply username
// std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5 // std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele):
std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); // Use this when we move to Qt 5.5
std::u16string new_username(
reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16()));
if (new_username != username) { if (new_username != username) {
Service::CFG::SetUsername(new_username); Service::CFG::SetUsername(new_username);
modified = true; modified = true;

View file

@ -4,19 +4,18 @@
#pragma once #pragma once
#include <memory>
#include <QWidget> #include <QWidget>
#include <memory>
namespace Ui { namespace Ui {
class ConfigureSystem; class ConfigureSystem;
} }
class ConfigureSystem : public QWidget class ConfigureSystem : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureSystem(QWidget *parent = nullptr); explicit ConfigureSystem(QWidget* parent = nullptr);
~ConfigureSystem(); ~ConfigureSystem();
void applyConfiguration(); void applyConfiguration();

View file

@ -9,13 +9,12 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/symbols.h" #include "common/symbols.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/disassembler/arm_disasm.h"
#include "core/core.h"
#include "core/memory.h"
CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) CallstackWidget::CallstackWidget(QWidget* parent) : QDockWidget(parent) {
{
ui.setupUi(this); ui.setupUi(this);
callstack_model = new QStandardItemModel(this); callstack_model = new QStandardItemModel(this);
@ -27,29 +26,26 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent)
ui.treeView->setModel(callstack_model); ui.treeView->setModel(callstack_model);
} }
void CallstackWidget::OnDebugModeEntered() void CallstackWidget::OnDebugModeEntered() {
{
// Stack pointer // Stack pointer
const u32 sp = Core::g_app_core->GetReg(13); const u32 sp = Core::g_app_core->GetReg(13);
Clear(); Clear();
int counter = 0; int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4) for (u32 addr = 0x10000000; addr >= sp; addr -= 4) {
{
if (!Memory::IsValidVirtualAddress(addr)) if (!Memory::IsValidVirtualAddress(addr))
break; break;
const u32 ret_addr = Memory::Read32(addr); const u32 ret_addr = Memory::Read32(addr);
const u32 call_addr = ret_addr - 4; //get call address??? const u32 call_addr = ret_addr - 4; // get call address???
if (!Memory::IsValidVirtualAddress(call_addr)) if (!Memory::IsValidVirtualAddress(call_addr))
break; break;
/* TODO (mattvail) clean me, move to debugger interface */ /* TODO (mattvail) clean me, move to debugger interface */
u32 insn = Memory::Read32(call_addr); u32 insn = Memory::Read32(call_addr);
if (ARM_Disasm::Decode(insn) == OP_BL) if (ARM_Disasm::Decode(insn) == OP_BL) {
{
std::string name; std::string name;
// ripped from disasm // ripped from disasm
u8 cond = (insn >> 28) & 0xf; u8 cond = (insn >> 28) & 0xf;
@ -63,26 +59,29 @@ void CallstackWidget::OnDebugModeEntered()
i_offset += 8; i_offset += 8;
const u32 func_addr = call_addr + i_offset; const u32 func_addr = call_addr + i_offset;
callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); callstack_model->setItem(
callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(
ret_addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(
call_addr, 8, 16, QLatin1Char('0'))));
name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown";
callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) callstack_model->setItem(
.arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); counter, 3, new QStandardItem(
QString("%1_%2")
.arg(QString::fromStdString(name))
.arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0')))));
counter++; counter++;
} }
} }
} }
void CallstackWidget::OnDebugModeLeft() void CallstackWidget::OnDebugModeLeft() {
{
} }
void CallstackWidget::Clear() void CallstackWidget::Clear() {
{
for (int row = 0; row < callstack_model->rowCount(); row++) { for (int row = 0; row < callstack_model->rowCount(); row++) {
for (int column = 0; column < callstack_model->columnCount(); column++) { for (int column = 0; column < callstack_model->columnCount(); column++) {
callstack_model->setItem(row, column, new QStandardItem()); callstack_model->setItem(row, column, new QStandardItem());

View file

@ -7,8 +7,7 @@
class QStandardItemModel; class QStandardItemModel;
class CallstackWidget : public QDockWidget class CallstackWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -5,20 +5,21 @@
#include <QShortcut> #include <QShortcut>
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/debugger/disassembler.h" #include "citra_qt/debugger/disassembler.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/break_points.h" #include "common/break_points.h"
#include "common/symbols.h" #include "common/symbols.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/disassembler/arm_disasm.h"
#include "core/core.h"
#include "core/memory.h"
DisassemblerModel::DisassemblerModel(QObject* parent) : DisassemblerModel::DisassemblerModel(QObject* parent)
QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0),
selection(QModelIndex()) {
} }
int DisassemblerModel::columnCount(const QModelIndex& parent) const { int DisassemblerModel::columnCount(const QModelIndex& parent) const {
@ -31,62 +32,60 @@ int DisassemblerModel::rowCount(const QModelIndex& parent) const {
QVariant DisassemblerModel::data(const QModelIndex& index, int role) const { QVariant DisassemblerModel::data(const QModelIndex& index, int role) const {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{ u32 address = base_address + index.row() * 4;
u32 address = base_address + index.row() * 4; u32 instr = Memory::Read32(address);
u32 instr = Memory::Read32(address); std::string disassembly = ARM_Disasm::Disassemble(address, instr);
std::string disassembly = ARM_Disasm::Disassemble(address, instr);
if (index.column() == 0) { if (index.column() == 0) {
return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0')); return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0'));
} else if (index.column() == 1) { } else if (index.column() == 1) {
return QString::fromStdString(disassembly); return QString::fromStdString(disassembly);
} else if (index.column() == 2) { } else if (index.column() == 2) {
if(Symbols::HasSymbol(address)) { if (Symbols::HasSymbol(address)) {
TSymbol symbol = Symbols::GetSymbol(address); TSymbol symbol = Symbols::GetSymbol(address);
return QString("%1 - Size:%2").arg(QString::fromStdString(symbol.name)) return QString("%1 - Size:%2")
.arg(symbol.size / 4); // divide by 4 to get instruction count .arg(QString::fromStdString(symbol.name))
} else if (ARM_Disasm::Decode(instr) == OP_BL) { .arg(symbol.size / 4); // divide by 4 to get instruction count
u32 offset = instr & 0xFFFFFF; } else if (ARM_Disasm::Decode(instr) == OP_BL) {
u32 offset = instr & 0xFFFFFF;
// Sign-extend the 24-bit offset // Sign-extend the 24-bit offset
if ((offset >> 23) & 1) if ((offset >> 23) & 1)
offset |= 0xFF000000; offset |= 0xFF000000;
// Pre-compute the left-shift and the prefetch offset // Pre-compute the left-shift and the prefetch offset
offset <<= 2; offset <<= 2;
offset += 8; offset += 8;
TSymbol symbol = Symbols::GetSymbol(address + offset); TSymbol symbol = Symbols::GetSymbol(address + offset);
return QString(" --> %1").arg(QString::fromStdString(symbol.name)); return QString(" --> %1").arg(QString::fromStdString(symbol.name));
}
} }
break;
} }
case Qt::BackgroundRole: break;
{ }
unsigned int address = base_address + 4 * index.row();
if (breakpoints.IsAddressBreakPoint(address)) case Qt::BackgroundRole: {
return QBrush(QColor(0xFF, 0xC0, 0xC0)); unsigned int address = base_address + 4 * index.row();
else if (address == program_counter)
return QBrush(QColor(0xC0, 0xC0, 0xFF));
break; if (breakpoints.IsAddressBreakPoint(address))
return QBrush(QColor(0xFF, 0xC0, 0xC0));
else if (address == program_counter)
return QBrush(QColor(0xC0, 0xC0, 0xFF));
break;
}
case Qt::FontRole: {
if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
return GetMonospaceFont();
} }
break;
}
case Qt::FontRole: default:
{ break;
if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column
return GetMonospaceFont();
}
break;
}
default:
break;
} }
return QVariant(); return QVariant();
@ -103,7 +102,7 @@ const BreakPoints& DisassemblerModel::GetBreakPoints() const {
void DisassemblerModel::ParseFromAddress(unsigned int address) { void DisassemblerModel::ParseFromAddress(unsigned int address) {
// NOTE: A too large value causes lagging when scrolling the disassembly // NOTE: A too large value causes lagging when scrolling the disassembly
const unsigned int chunk_size = 1000*500; const unsigned int chunk_size = 1000 * 500;
// If we haven't loaded anything yet, initialize base address to the parameter address // If we haven't loaded anything yet, initialize base address to the parameter address
if (code_size == 0) if (code_size == 0)
@ -165,23 +164,26 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
emit dataChanged(prev_index, prev_index); emit dataChanged(prev_index, prev_index);
} }
DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) : DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread)
QDockWidget(parent), base_addr(0), emu_thread(emu_thread) { : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) {
disasm_ui.setupUi(this); disasm_ui.setupUi(this);
RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut);
RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9),
Qt::ApplicationShortcut);
connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep()));
connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause()));
connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue()));
connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this,
SLOT(OnToggleStartStop()));
connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep()));
connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this,
SLOT(OnStepInto()));
setEnabled(false); setEnabled(false);
} }
@ -195,7 +197,8 @@ void DisassemblerWidget::Init() {
QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC()); QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::g_app_core->GetPC());
disasm_ui.treeView->scrollTo(model_index); disasm_ui.treeView->scrollTo(model_index);
disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); disasm_ui.treeView->selectionModel()->setCurrentIndex(
model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
} }
void DisassemblerWidget::OnContinue() { void DisassemblerWidget::OnContinue() {
@ -234,7 +237,8 @@ void DisassemblerWidget::OnDebugModeEntered() {
QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr); QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr);
disasm_ui.treeView->scrollTo(model_index); disasm_ui.treeView->scrollTo(model_index);
disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); disasm_ui.treeView->selectionModel()->setCurrentIndex(
model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
} }
void DisassemblerWidget::OnDebugModeLeft() { void DisassemblerWidget::OnDebugModeLeft() {
@ -254,10 +258,12 @@ void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) {
model = new DisassemblerModel(this); model = new DisassemblerModel(this);
disasm_ui.treeView->setModel(model); disasm_ui.treeView->setModel(model);
connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), connect(disasm_ui.treeView->selectionModel(),
model, SLOT(OnSelectionChanged(const QModelIndex&))); SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), model,
SLOT(OnSelectionChanged(const QModelIndex&)));
connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model,
SLOT(OnSetOrUnsetBreakpoint()));
Init(); Init();
setEnabled(true); setEnabled(true);

View file

@ -15,8 +15,7 @@
class QAction; class QAction;
class EmuThread; class EmuThread;
class DisassemblerModel : public QAbstractListModel class DisassemblerModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -46,8 +45,7 @@ private:
mutable BreakPoints breakpoints; mutable BreakPoints breakpoints;
}; };
class DisassemblerWidget : public QDockWidget class DisassemblerWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -9,69 +9,62 @@
extern GraphicsDebugger g_debugger; extern GraphicsDebugger g_debugger;
GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) : QAbstractListModel(parent), command_count(0) GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent)
{ : QAbstractListModel(parent), command_count(0) {
connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int))); connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int)));
} }
int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const {
{
return command_count; return command_count;
} }
QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const {
{
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
int command_index = index.row(); int command_index = index.row();
const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index); const GSP_GPU::Command& command = GetDebugger()->ReadGXCommandHistory(command_index);
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole) {
{
std::map<GSP_GPU::CommandId, const char*> command_names = { std::map<GSP_GPU::CommandId, const char*> command_names = {
{ GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA" }, {GSP_GPU::CommandId::REQUEST_DMA, "REQUEST_DMA"},
{ GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST" }, {GSP_GPU::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"},
{ GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL" }, {GSP_GPU::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"},
{ GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER" }, {GSP_GPU::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"},
{ GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY" }, {GSP_GPU::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"},
{ GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH" }, {GSP_GPU::CommandId::CACHE_FLUSH, "CACHE_FLUSH"},
}; };
const u32* command_data = reinterpret_cast<const u32*>(&command); const u32* command_data = reinterpret_cast<const u32*>(&command);
QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[command.id]) QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9")
.arg(command_data[0], 8, 16, QLatin1Char('0')) .arg(command_names[command.id])
.arg(command_data[1], 8, 16, QLatin1Char('0')) .arg(command_data[0], 8, 16, QLatin1Char('0'))
.arg(command_data[2], 8, 16, QLatin1Char('0')) .arg(command_data[1], 8, 16, QLatin1Char('0'))
.arg(command_data[3], 8, 16, QLatin1Char('0')) .arg(command_data[2], 8, 16, QLatin1Char('0'))
.arg(command_data[4], 8, 16, QLatin1Char('0')) .arg(command_data[3], 8, 16, QLatin1Char('0'))
.arg(command_data[5], 8, 16, QLatin1Char('0')) .arg(command_data[4], 8, 16, QLatin1Char('0'))
.arg(command_data[6], 8, 16, QLatin1Char('0')) .arg(command_data[5], 8, 16, QLatin1Char('0'))
.arg(command_data[7], 8, 16, QLatin1Char('0')); .arg(command_data[6], 8, 16, QLatin1Char('0'))
.arg(command_data[7], 8, 16, QLatin1Char('0'));
return QVariant(str); return QVariant(str);
} } else {
else
{
return QVariant(); return QVariant();
} }
} }
void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) {
{
emit GXCommandFinished(total_command_count); emit GXCommandFinished(total_command_count);
} }
void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) {
{
if (total_command_count == 0) if (total_command_count == 0)
return; return;
int prev_command_count = command_count; int prev_command_count = command_count;
command_count = total_command_count; command_count = total_command_count;
emit dataChanged(index(prev_command_count,0), index(total_command_count-1,0)); emit dataChanged(index(prev_command_count, 0), index(total_command_count - 1, 0));
} }
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr("Graphics Debugger"), parent) : QDockWidget(tr("Graphics Debugger"), parent) {
{
setObjectName("GraphicsDebugger"); setObjectName("GraphicsDebugger");
GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this); GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);

View file

@ -9,8 +9,8 @@
#include "video_core/gpu_debugger.h" #include "video_core/gpu_debugger.h"
class GPUCommandStreamItemModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver class GPUCommandStreamItemModel : public QAbstractListModel,
{ public GraphicsDebugger::DebuggerObserver {
Q_OBJECT Q_OBJECT
public: public:
@ -32,8 +32,7 @@ private:
int command_count; int command_count;
}; };
class GPUCommandStreamWidget : public QDockWidget class GPUCommandStreamWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -8,25 +8,21 @@
BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
const QString& title, QWidget* parent) const QString& title, QWidget* parent)
: QDockWidget(title, parent), BreakPointObserver(debug_context) : QDockWidget(title, parent), BreakPointObserver(debug_context) {
{
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
// NOTE: This signal is emitted from a non-GUI thread, but connect() takes // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
// care of delaying its handling to the GUI thread. // care of delaying its handling to the GUI thread.
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
} }
void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
emit BreakPointHit(event, data); emit BreakPointHit(event, data);
} }
void BreakPointObserverDock::OnPicaResume() void BreakPointObserverDock::OnPicaResume() {
{
emit Resumed(); emit Resumed();
} }

View file

@ -13,7 +13,8 @@
* This is because the Pica breakpoint callbacks are called from a non-GUI thread, while * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
* the widget usually wants to perform reactions in the GUI thread. * the widget usually wants to perform reactions in the GUI thread.
*/ */
class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { class BreakPointObserverDock : public QDockWidget,
protected Pica::DebugContext::BreakPointObserver {
Q_OBJECT Q_OBJECT
public: public:

View file

@ -16,39 +16,33 @@
BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
: QAbstractListModel(parent), context_weak(debug_context), : QAbstractListModel(parent), context_weak(debug_context),
at_breakpoint(debug_context->at_breakpoint), at_breakpoint(debug_context->at_breakpoint),
active_breakpoint(debug_context->active_breakpoint) active_breakpoint(debug_context->active_breakpoint) {
{
} }
int BreakPointModel::columnCount(const QModelIndex& parent) const int BreakPointModel::columnCount(const QModelIndex& parent) const {
{
return 1; return 1;
} }
int BreakPointModel::rowCount(const QModelIndex& parent) const int BreakPointModel::rowCount(const QModelIndex& parent) const {
{
return static_cast<int>(Pica::DebugContext::Event::NumEvents); return static_cast<int>(Pica::DebugContext::Event::NumEvents);
} }
QVariant BreakPointModel::data(const QModelIndex& index, int role) const QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
{
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); const auto event = static_cast<Pica::DebugContext::Event>(index.row());
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
if (index.column() == 0) { if (index.column() == 0) {
static const std::map<Pica::DebugContext::Event, QString> map = { static const std::map<Pica::DebugContext::Event, QString> map = {
{ Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") }, {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")},
{ Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") }, {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")},
{ Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")},
{ Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")},
{ Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") }, {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")},
{ Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, {Pica::DebugContext::Event::IncomingDisplayTransfer,
{ Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, tr("Incoming display transfer")},
{ Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } {Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")},
}; {Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")}};
DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
return (map.find(event) != map.end()) ? map.at(event) : QString(); return (map.find(event) != map.end()) ? map.at(event) : QString();
@ -57,23 +51,20 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
break; break;
} }
case Qt::CheckStateRole: case Qt::CheckStateRole: {
{
if (index.column() == 0) if (index.column() == 0)
return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
break; break;
} }
case Qt::BackgroundRole: case Qt::BackgroundRole: {
{
if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
return QBrush(QColor(0xE0, 0xE0, 0x10)); return QBrush(QColor(0xE0, 0xE0, 0x10));
} }
break; break;
} }
case Role_IsEnabled: case Role_IsEnabled: {
{
auto context = context_weak.lock(); auto context = context_weak.lock();
return context && context->breakpoints[(int)event].enabled; return context && context->breakpoints[(int)event].enabled;
} }
@ -84,8 +75,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
return QVariant(); return QVariant();
} }
Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
{
if (!index.isValid()) if (!index.isValid())
return 0; return 0;
@ -95,14 +85,11 @@ Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const
return flags; return flags;
} }
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) {
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); const auto event = static_cast<Pica::DebugContext::Event>(index.row());
switch (role) { switch (role) {
case Qt::CheckStateRole: case Qt::CheckStateRole: {
{
if (index.column() != 0) if (index.column() != 0)
return false; return false;
@ -120,9 +107,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i
return false; return false;
} }
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) {
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
{
auto context = context_weak.lock(); auto context = context_weak.lock();
if (!context) if (!context)
return; return;
@ -133,8 +118,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
createIndex(static_cast<int>(event), 0)); createIndex(static_cast<int>(event), 0));
} }
void BreakPointModel::OnResumed() void BreakPointModel::OnResumed() {
{
auto context = context_weak.lock(); auto context = context_weak.lock();
if (!context) if (!context)
return; return;
@ -145,12 +129,10 @@ void BreakPointModel::OnResumed()
active_breakpoint = context->active_breakpoint; active_breakpoint = context->active_breakpoint;
} }
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
QWidget* parent)
: QDockWidget(tr("Pica Breakpoints"), parent), : QDockWidget(tr("Pica Breakpoints"), parent),
Pica::DebugContext::BreakPointObserver(debug_context) Pica::DebugContext::BreakPointObserver(debug_context) {
{
setObjectName("PicaBreakPointsWidget"); setObjectName("PicaBreakPointsWidget");
status_text = new QLabel(tr("Emulation running")); status_text = new QLabel(tr("Emulation running"));
@ -165,23 +147,21 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this,
this, SLOT(OnItemDoubleClicked(const QModelIndex&))); SLOT(OnItemDoubleClicked(const QModelIndex&)));
connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), breakpoint_model,
breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), SLOT(OnBreakPointHit(Pica::DebugContext::Event)), Qt::BlockingQueuedConnection);
Qt::BlockingQueuedConnection);
connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)),
breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
QWidget* main_widget = new QWidget; QWidget* main_widget = new QWidget;
auto main_layout = new QVBoxLayout; auto main_layout = new QVBoxLayout;
@ -197,38 +177,32 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug
setWidget(main_widget); setWidget(main_widget);
} }
void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) {
{
// Process in GUI thread // Process in GUI thread
emit BreakPointHit(event, data); emit BreakPointHit(event, data);
} }
void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
status_text->setText(tr("Emulation halted at breakpoint")); status_text->setText(tr("Emulation halted at breakpoint"));
resume_button->setEnabled(true); resume_button->setEnabled(true);
} }
void GraphicsBreakPointsWidget::OnPicaResume() void GraphicsBreakPointsWidget::OnPicaResume() {
{
// Process in GUI thread // Process in GUI thread
emit Resumed(); emit Resumed();
} }
void GraphicsBreakPointsWidget::OnResumed() void GraphicsBreakPointsWidget::OnResumed() {
{
status_text->setText(tr("Emulation running")); status_text->setText(tr("Emulation running"));
resume_button->setEnabled(false); resume_button->setEnabled(false);
} }
void GraphicsBreakPointsWidget::OnResumeRequested() void GraphicsBreakPointsWidget::OnResumeRequested() {
{
if (auto context = context_weak.lock()) if (auto context = context_weak.lock())
context->Resume(); context->Resume();
} }
void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
{
if (!index.isValid()) if (!index.isValid())
return; return;

View file

@ -23,7 +23,7 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex& index) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;

View file

@ -20,9 +20,9 @@
#include "common/vector_math.h" #include "common/vector_math.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/debug_utils/debug_utils.h"
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
@ -38,7 +38,8 @@ QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
class TextureInfoWidget : public QWidget { class TextureInfoWidget : public QWidget {
public: public:
TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) { TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr)
: QWidget(parent) {
QLabel* image_widget = new QLabel; QLabel* image_widget = new QLabel;
QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
@ -51,7 +52,6 @@ public:
}; };
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) { GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
} }
int GPUCommandListModel::rowCount(const QModelIndex& parent) const { int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
@ -70,7 +70,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
QString content; QString content;
switch ( index.column() ) { switch (index.column()) {
case 0: case 0:
return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str()); return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str());
case 1: case 1:
@ -88,9 +88,8 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
} }
QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const {
switch(role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
switch (section) { switch (section) {
case 0: case 0:
return tr("Command Name"); return tr("Command Name");
@ -117,14 +116,14 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
endResetModel(); endResetModel();
} }
#define COMMAND_IN_RANGE(cmd_id, reg_name) \ #define COMMAND_IN_RANGE(cmd_id, reg_name) \
(cmd_id >= PICA_REG_INDEX(reg_name) && \ (cmd_id >= PICA_REG_INDEX(reg_name) && \
cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4) cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4)
void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); const unsigned int command_id =
if (COMMAND_IN_RANGE(command_id, texture0) || list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
COMMAND_IN_RANGE(command_id, texture1) || if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
COMMAND_IN_RANGE(command_id, texture2)) { COMMAND_IN_RANGE(command_id, texture2)) {
unsigned index; unsigned index;
@ -148,9 +147,9 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
QWidget* new_info_widget = nullptr; QWidget* new_info_widget = nullptr;
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt(); const unsigned int command_id =
if (COMMAND_IN_RANGE(command_id, texture0) || list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
COMMAND_IN_RANGE(command_id, texture1) || if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
COMMAND_IN_RANGE(command_id, texture2)) { COMMAND_IN_RANGE(command_id, texture2)) {
unsigned index; unsigned index;
@ -179,7 +178,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
} }
#undef COMMAND_IN_RANGE #undef COMMAND_IN_RANGE
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) { GPUCommandListWidget::GPUCommandListWidget(QWidget* parent)
: QDockWidget(tr("Pica Command List"), parent) {
setObjectName("Pica Command List"); setObjectName("Pica Command List");
GPUCommandListModel* model = new GPUCommandListModel(this); GPUCommandListModel* model = new GPUCommandListModel(this);
@ -191,23 +191,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
list_widget->setRootIsDecorated(false); list_widget->setRootIsDecorated(false);
list_widget->setUniformRowHeights(true); list_widget->setUniformRowHeights(true);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
#else #else
list_widget->header()->setResizeMode(QHeaderView::ResizeToContents); list_widget->header()->setResizeMode(QHeaderView::ResizeToContents);
#endif #endif
connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), connect(list_widget->selectionModel(),
this, SLOT(SetCommandInfo(const QModelIndex&))); SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this,
connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(SetCommandInfo(const QModelIndex&)));
this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), this,
SLOT(OnCommandDoubleClicked(const QModelIndex&)));
toggle_tracing = new QPushButton(tr("Start Tracing")); toggle_tracing = new QPushButton(tr("Start Tracing"));
QPushButton* copy_all = new QPushButton(tr("Copy All")); QPushButton* copy_all = new QPushButton(tr("Copy All"));
connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model,
model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard()));

View file

@ -7,14 +7,13 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/gpu_debugger.h"
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu_debugger.h"
class QPushButton; class QPushButton;
class QTreeView; class QTreeView;
class GPUCommandListModel : public QAbstractListModel class GPUCommandListModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -27,7 +26,8 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
public slots: public slots:
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
@ -36,8 +36,7 @@ private:
Pica::DebugUtils::PicaTrace pica_trace; Pica::DebugUtils::PicaTrace pica_trace;
}; };
class GPUCommandListWidget : public QDockWidget class GPUCommandListWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -17,18 +17,20 @@
#include "common/color.h" #include "common/color.h"
#include "core/memory.h"
#include "core/hw/gpu.h" #include "core/hw/gpu.h"
#include "core/memory.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/utils.h" #include "video_core/utils.h"
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {} SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
SurfacePicture::~SurfacePicture() {} : QLabel(parent), surface_widget(surface_widget_) {
}
SurfacePicture::~SurfacePicture() {
}
void SurfacePicture::mousePressEvent(QMouseEvent* event) void SurfacePicture::mousePressEvent(QMouseEvent* event) {
{
// Only do something while the left mouse button is held down // Only do something while the left mouse button is held down
if (!(event->buttons() & Qt::LeftButton)) if (!(event->buttons() & Qt::LeftButton))
return; return;
@ -41,18 +43,15 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event)
event->y() * pixmap()->height() / height()); event->y() * pixmap()->height() / height());
} }
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
{
// We also want to handle the event if the user moves the mouse while holding down the LMB // We also want to handle the event if the user moves the mouse while holding down the LMB
mousePressEvent(event); mousePressEvent(event);
} }
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent) QWidget* parent)
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
surface_source(Source::ColorBuffer) surface_source(Source::ColorBuffer) {
{
setObjectName("PicaSurface"); setObjectName("PicaSurface");
surface_source_list = new QComboBox; surface_source_list = new QComboBox;
@ -124,13 +123,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext>
// Connections // Connections
connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int))); connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this,
connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64))); SLOT(OnSurfaceSourceChanged(int)));
connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int))); connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this,
connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int))); SLOT(OnSurfaceAddressChanged(qint64)));
connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int))); connect(surface_width_control, SIGNAL(valueChanged(int)), this,
connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int))); SLOT(OnSurfaceWidthChanged(int)));
connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(int))); connect(surface_height_control, SIGNAL(valueChanged(int)), this,
SLOT(OnSurfaceHeightChanged(int)));
connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this,
SLOT(OnSurfaceFormatChanged(int)));
connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this,
SLOT(OnSurfacePickerXChanged(int)));
connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this,
SLOT(OnSurfacePickerYChanged(int)));
connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface())); connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
auto main_widget = new QWidget; auto main_widget = new QWidget;
@ -203,25 +209,21 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext>
} }
} }
void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
{
emit Update(); emit Update();
widget()->setEnabled(true); widget()->setEnabled(true);
} }
void GraphicsSurfaceWidget::OnResumed() void GraphicsSurfaceWidget::OnResumed() {
{
widget()->setEnabled(false); widget()->setEnabled(false);
} }
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
{
surface_source = static_cast<Source>(new_value); surface_source = static_cast<Source>(new_value);
emit Update(); emit Update();
} }
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
{
if (surface_address != new_value) { if (surface_address != new_value) {
surface_address = static_cast<unsigned>(new_value); surface_address = static_cast<unsigned>(new_value);
@ -230,8 +232,7 @@ void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
{
if (surface_width != static_cast<unsigned>(new_value)) { if (surface_width != static_cast<unsigned>(new_value)) {
surface_width = static_cast<unsigned>(new_value); surface_width = static_cast<unsigned>(new_value);
@ -240,8 +241,7 @@ void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
{
if (surface_height != static_cast<unsigned>(new_value)) { if (surface_height != static_cast<unsigned>(new_value)) {
surface_height = static_cast<unsigned>(new_value); surface_height = static_cast<unsigned>(new_value);
@ -250,8 +250,7 @@ void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
{
if (surface_format != static_cast<Format>(new_value)) { if (surface_format != static_cast<Format>(new_value)) {
surface_format = static_cast<Format>(new_value); surface_format = static_cast<Format>(new_value);
@ -260,24 +259,21 @@ void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value)
} }
} }
void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
{
if (surface_picker_x != new_value) { if (surface_picker_x != new_value) {
surface_picker_x = new_value; surface_picker_x = new_value;
Pick(surface_picker_x, surface_picker_y); Pick(surface_picker_x, surface_picker_y);
} }
} }
void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
{
if (surface_picker_y != new_value) { if (surface_picker_y != new_value) {
surface_picker_y = new_value; surface_picker_y = new_value;
Pick(surface_picker_x, surface_picker_y); Pick(surface_picker_x, surface_picker_y);
} }
} }
void GraphicsSurfaceWidget::Pick(int x, int y) void GraphicsSurfaceWidget::Pick(int x, int y) {
{
surface_picker_x_control->setValue(x); surface_picker_x_control->setValue(x);
surface_picker_y_control->setValue(y); surface_picker_y_control->setValue(y);
@ -312,74 +308,63 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
auto GetText = [offset](Format format, const u8* pixel) { auto GetText = [offset](Format format, const u8* pixel) {
switch (format) { switch (format) {
case Format::RGBA8: case Format::RGBA8: {
{
auto value = Color::DecodeRGBA8(pixel) / 255.0f; auto value = Color::DecodeRGBA8(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::RGB8: case Format::RGB8: {
{
auto value = Color::DecodeRGB8(pixel) / 255.0f; auto value = Color::DecodeRGB8(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3") return QString("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)); .arg(QString::number(value.b(), 'f', 2));
} }
case Format::RGB5A1: case Format::RGB5A1: {
{
auto value = Color::DecodeRGB5A1(pixel) / 255.0f; auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::RGB565: case Format::RGB565: {
{
auto value = Color::DecodeRGB565(pixel) / 255.0f; auto value = Color::DecodeRGB565(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3") return QString("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)); .arg(QString::number(value.b(), 'f', 2));
} }
case Format::RGBA4: case Format::RGBA4: {
{
auto value = Color::DecodeRGBA4(pixel) / 255.0f; auto value = Color::DecodeRGBA4(pixel) / 255.0f;
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)) .arg(QString::number(value.g(), 'f', 2))
.arg(QString::number(value.b(), 'f', 2)) .arg(QString::number(value.b(), 'f', 2))
.arg(QString::number(value.a(), 'f', 2)); .arg(QString::number(value.a(), 'f', 2));
} }
case Format::IA8: case Format::IA8:
return QString("Index: %1, Alpha: %2") return QString("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
.arg(pixel[0])
.arg(pixel[1]);
case Format::RG8: { case Format::RG8: {
auto value = Color::DecodeRG8(pixel) / 255.0f; auto value = Color::DecodeRG8(pixel) / 255.0f;
return QString("Red: %1, Green: %2") return QString("Red: %1, Green: %2")
.arg(QString::number(value.r(), 'f', 2)) .arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2)); .arg(QString::number(value.g(), 'f', 2));
} }
case Format::I8: case Format::I8:
return QString("Index: %1").arg(*pixel); return QString("Index: %1").arg(*pixel);
case Format::A8: case Format::A8:
return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2)); return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
case Format::IA4: case Format::IA4:
return QString("Index: %1, Alpha: %2") return QString("Index: %1, Alpha: %2").arg(*pixel & 0xF).arg((*pixel & 0xF0) >> 4);
.arg(*pixel & 0xF) case Format::I4: {
.arg((*pixel & 0xF0) >> 4);
case Format::I4:
{
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QString("Index: %1").arg(i); return QString("Index: %1").arg(i);
} }
case Format::A4: case Format::A4: {
{
u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
} }
@ -387,21 +372,20 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
case Format::ETC1A4: case Format::ETC1A4:
// TODO: Display block information or channel values? // TODO: Display block information or channel values?
return QString("Compressed data"); return QString("Compressed data");
case Format::D16: case Format::D16: {
{
auto value = Color::DecodeD16(pixel); auto value = Color::DecodeD16(pixel);
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
} }
case Format::D24: case Format::D24: {
{
auto value = Color::DecodeD24(pixel); auto value = Color::DecodeD24(pixel);
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
} }
case Format::D24X8: case Format::D24X8:
case Format::X24S8: case Format::X24S8: {
{
auto values = Color::DecodeD24S8(pixel); auto values = Color::DecodeD24S8(pixel);
return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]); return QString("Depth: %1, Stencil: %2")
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
.arg(values[1]);
} }
case Format::Unknown: case Format::Unknown:
return QString("Unknown format"); return QString("Unknown format");
@ -422,18 +406,18 @@ void GraphicsSurfaceWidget::Pick(int x, int y)
nibbles.append(QString::number(nibble, 16).toUpper()); nibbles.append(QString::number(nibble, 16).toUpper());
} }
surface_info_label->setText(QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel))); surface_info_label->setText(
QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
} }
void GraphicsSurfaceWidget::OnUpdate() void GraphicsSurfaceWidget::OnUpdate() {
{
QPixmap pixmap; QPixmap pixmap;
switch (surface_source) { switch (surface_source) {
case Source::ColorBuffer: case Source::ColorBuffer: {
{ // TODO: Store a reference to the registers in the debug context instead of accessing them
// TODO: Store a reference to the registers in the debug context instead of accessing them directly... // directly...
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
@ -470,8 +454,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::DepthBuffer: case Source::DepthBuffer: {
{
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress(); surface_address = framebuffer.GetDepthBufferPhysicalAddress();
@ -499,8 +482,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::StencilBuffer: case Source::StencilBuffer: {
{
const auto& framebuffer = Pica::g_state.regs.framebuffer; const auto& framebuffer = Pica::g_state.regs.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress(); surface_address = framebuffer.GetDepthBufferPhysicalAddress();
@ -522,12 +504,14 @@ void GraphicsSurfaceWidget::OnUpdate()
case Source::Texture0: case Source::Texture0:
case Source::Texture1: case Source::Texture1:
case Source::Texture2: case Source::Texture2: {
{
unsigned texture_index; unsigned texture_index;
if (surface_source == Source::Texture0) texture_index = 0; if (surface_source == Source::Texture0)
else if (surface_source == Source::Texture1) texture_index = 1; texture_index = 0;
else if (surface_source == Source::Texture2) texture_index = 2; else if (surface_source == Source::Texture1)
texture_index = 1;
else if (surface_source == Source::Texture2)
texture_index = 2;
else { else {
qDebug() << "Unknown texture source " << static_cast<int>(surface_source); qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
break; break;
@ -547,8 +531,7 @@ void GraphicsSurfaceWidget::OnUpdate()
break; break;
} }
case Source::Custom: case Source::Custom: {
{
// Keep user-specified values // Keep user-specified values
break; break;
} }
@ -613,7 +596,8 @@ void GraphicsSurfaceWidget::OnUpdate()
} else { } else {
ASSERT_MSG(nibbles_per_pixel >= 2, "Depth decoder only supports formats with at least one byte per pixel"); ASSERT_MSG(nibbles_per_pixel >= 2,
"Depth decoder only supports formats with at least one byte per pixel");
unsigned bytes_per_pixel = nibbles_per_pixel / 2; unsigned bytes_per_pixel = nibbles_per_pixel / 2;
for (unsigned int y = 0; y < surface_height; ++y) { for (unsigned int y = 0; y < surface_height; ++y) {
@ -621,34 +605,30 @@ void GraphicsSurfaceWidget::OnUpdate()
const u32 coarse_y = y & ~7; const u32 coarse_y = y & ~7;
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
const u8* pixel = buffer + offset; const u8* pixel = buffer + offset;
Math::Vec4<u8> color = { 0, 0, 0, 0 }; Math::Vec4<u8> color = {0, 0, 0, 0};
switch(surface_format) { switch (surface_format) {
case Format::D16: case Format::D16: {
{
u32 data = Color::DecodeD16(pixel); u32 data = Color::DecodeD16(pixel);
color.r() = data & 0xFF; color.r() = data & 0xFF;
color.g() = (data >> 8) & 0xFF; color.g() = (data >> 8) & 0xFF;
break; break;
} }
case Format::D24: case Format::D24: {
{
u32 data = Color::DecodeD24(pixel); u32 data = Color::DecodeD24(pixel);
color.r() = data & 0xFF; color.r() = data & 0xFF;
color.g() = (data >> 8) & 0xFF; color.g() = (data >> 8) & 0xFF;
color.b() = (data >> 16) & 0xFF; color.b() = (data >> 16) & 0xFF;
break; break;
} }
case Format::D24X8: case Format::D24X8: {
{
Math::Vec2<u32> data = Color::DecodeD24S8(pixel); Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
color.r() = data.x & 0xFF; color.r() = data.x & 0xFF;
color.g() = (data.x >> 8) & 0xFF; color.g() = (data.x >> 8) & 0xFF;
color.b() = (data.x >> 16) & 0xFF; color.b() = (data.x >> 16) & 0xFF;
break; break;
} }
case Format::X24S8: case Format::X24S8: {
{
Math::Vec2<u32> data = Color::DecodeD24S8(pixel); Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
color.r() = color.g() = color.b() = data.y; color.r() = color.g() = color.b() = data.y;
break; break;
@ -661,7 +641,6 @@ void GraphicsSurfaceWidget::OnUpdate()
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255)); decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
} }
} }
} }
pixmap = QPixmap::fromImage(decoded_image); pixmap = QPixmap::fromImage(decoded_image);
@ -682,8 +661,10 @@ void GraphicsSurfaceWidget::SaveSurface() {
QString bin_filter = tr("Binary data (*.bin)"); QString bin_filter = tr("Binary data (*.bin)");
QString selectedFilter; QString selectedFilter;
QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), QString filename = QFileDialog::getSaveFileName(
QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); this, tr("Save Surface"),
QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
if (filename.isEmpty()) { if (filename.isEmpty()) {
// If the user canceled the dialog, don't save anything. // If the user canceled the dialog, don't save anything.
@ -718,19 +699,19 @@ unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Forma
} }
switch (format) { switch (format) {
case Format::D24X8: case Format::D24X8:
case Format::X24S8: case Format::X24S8:
return 4 * 2; return 4 * 2;
case Format::D24: case Format::D24:
return 3 * 2; return 3 * 2;
case Format::D16: case Format::D16:
return 2 * 2; return 2 * 2;
default: default:
UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this " UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this "
"should not be reached as this function should " "should not be reached as this function should "
"be given a format which is in " "be given a format which is in "
"GraphicsSurfaceWidget::Format. Instead got %i", "GraphicsSurfaceWidget::Format. Instead got %i",
static_cast<int>(format)); static_cast<int>(format));
return 0; return 0;
} }
} }

View file

@ -15,8 +15,7 @@ class CSpinBox;
class GraphicsSurfaceWidget; class GraphicsSurfaceWidget;
class SurfacePicture : public QLabel class SurfacePicture : public QLabel {
{
Q_OBJECT Q_OBJECT
public: public:
@ -29,7 +28,6 @@ protected slots:
private: private:
GraphicsSurfaceWidget* surface_widget; GraphicsSurfaceWidget* surface_widget;
}; };
class GraphicsSurfaceWidget : public BreakPointObserverDock { class GraphicsSurfaceWidget : public BreakPointObserverDock {
@ -38,43 +36,44 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock {
using Event = Pica::DebugContext::Event; using Event = Pica::DebugContext::Event;
enum class Source { enum class Source {
ColorBuffer = 0, ColorBuffer = 0,
DepthBuffer = 1, DepthBuffer = 1,
StencilBuffer = 2, StencilBuffer = 2,
Texture0 = 3, Texture0 = 3,
Texture1 = 4, Texture1 = 4,
Texture2 = 5, Texture2 = 5,
Custom = 6, Custom = 6,
}; };
enum class Format { enum class Format {
// These must match the TextureFormat type! // These must match the TextureFormat type!
RGBA8 = 0, RGBA8 = 0,
RGB8 = 1, RGB8 = 1,
RGB5A1 = 2, RGB5A1 = 2,
RGB565 = 3, RGB565 = 3,
RGBA4 = 4, RGBA4 = 4,
IA8 = 5, IA8 = 5,
RG8 = 6, ///< @note Also called HILO8 in 3DBrew. RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
I8 = 7, I8 = 7,
A8 = 8, A8 = 8,
IA4 = 9, IA4 = 9,
I4 = 10, I4 = 10,
A4 = 11, A4 = 11,
ETC1 = 12, // compressed ETC1 = 12, // compressed
ETC1A4 = 13, ETC1A4 = 13,
MaxTextureFormat = 13, MaxTextureFormat = 13,
D16 = 14, D16 = 14,
D24 = 15, D24 = 15,
D24X8 = 16, D24X8 = 16,
X24S8 = 17, X24S8 = 17,
Unknown = 18, Unknown = 18,
}; };
static unsigned int NibblesPerPixel(Format format); static unsigned int NibblesPerPixel(Format format);
public: public:
GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
void Pick(int x, int y); void Pick(int x, int y);
public slots: public slots:
@ -97,7 +96,6 @@ signals:
void Update(); void Update();
private: private:
QComboBox* surface_source_list; QComboBox* surface_source_list;
CSpinBox* surface_address_control; CSpinBox* surface_address_control;
QSpinBox* surface_width_control; QSpinBox* surface_width_control;

View file

@ -35,12 +35,16 @@ GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext>
setObjectName("CiTracing"); setObjectName("CiTracing");
QPushButton* start_recording = new QPushButton(tr("Start Recording")); QPushButton* start_recording = new QPushButton(tr("Start Recording"));
QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); QPushButton* stop_recording =
new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save"));
QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); QPushButton* abort_recording = new QPushButton(tr("Abort Recording"));
connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording,
connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); SLOT(setVisible(bool)));
connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording,
SLOT(setVisible(bool)));
connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording,
SLOT(setVisible(bool)));
connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording()));
connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording()));
connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording()));
@ -74,26 +78,31 @@ void GraphicsTracingWidget::StartRecording() {
std::array<u32, 4 * 16> default_attributes; std::array<u32, 4 * 16> default_attributes;
for (unsigned i = 0; i < 16; ++i) { for (unsigned i = 0; i < 16; ++i) {
for (unsigned comp = 0; comp < 3; ++comp) { for (unsigned comp = 0; comp < 3; ++comp) {
default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32()); default_attributes[4 * i + comp] =
nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32());
} }
} }
std::array<u32, 4 * 96> vs_float_uniforms; std::array<u32, 4 * 96> vs_float_uniforms;
for (unsigned i = 0; i < 96; ++i) for (unsigned i = 0; i < 96; ++i)
for (unsigned comp = 0; comp < 3; ++comp) for (unsigned comp = 0; comp < 3; ++comp)
vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); vs_float_uniforms[4 * i + comp] =
nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32());
CiTrace::Recorder::InitialState state; CiTrace::Recorder::InitialState state;
std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers)); std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32),
std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers)); std::back_inserter(state.gpu_registers));
std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers)); std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32),
std::back_inserter(state.lcd_registers));
std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32),
std::back_inserter(state.pica_registers));
boost::copy(default_attributes, std::back_inserter(state.default_attributes)); boost::copy(default_attributes, std::back_inserter(state.default_attributes));
boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); boost::copy(shader_binary, std::back_inserter(state.vs_program_binary));
boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data));
boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
//boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms)); // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
auto recorder = new CiTrace::Recorder(state); auto recorder = new CiTrace::Recorder(state);
context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder);
@ -156,11 +165,12 @@ void GraphicsTracingWidget::OnEmulationStopping() {
if (!context) if (!context)
return; return;
if (context->recorder) { if (context->recorder) {
auto reply = QMessageBox::question(this, tr("CiTracing still active"), auto reply =
tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."), QMessageBox::question(this, tr("CiTracing still active"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); tr("A CiTrace is still being recorded. Do you want to save it? "
"If not, all recorded data will be discarded."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (reply == QMessageBox::Yes) { if (reply == QMessageBox::Yes) {
StopRecording(); StopRecording();

View file

@ -12,7 +12,8 @@ class GraphicsTracingWidget : public BreakPointObserverDock {
Q_OBJECT Q_OBJECT
public: public:
GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
private slots: private slots:
void StartRecording(); void StartRecording();

View file

@ -28,8 +28,8 @@ using nihstro::Instruction;
using nihstro::SourceRegister; using nihstro::SourceRegister;
using nihstro::SwizzlePattern; using nihstro::SwizzlePattern;
GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractTableModel(parent), par(parent) { GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent)
: QAbstractTableModel(parent), par(parent) {
} }
int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const {
@ -40,10 +40,10 @@ int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const {
return static_cast<int>(par->info.code.size()); return static_cast<int>(par->info.code.size());
} }
QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation,
switch(role) { int role) const {
case Qt::DisplayRole: switch (role) {
{ case Qt::DisplayRole: {
if (section == 0) { if (section == 0) {
return tr("Offset"); return tr("Offset");
} else if (section == 1) { } else if (section == 1) {
@ -69,8 +69,8 @@ static std::string SelectorToString(u32 selector) {
} }
// e.g. "-c92[a0.x].xyzw" // e.g. "-c92[a0.x].xyzw"
static void print_input(std::ostringstream& output, const SourceRegister& input, static void print_input(std::ostringstream& output, const SourceRegister& input, bool negate,
bool negate, const std::string& swizzle_mask, bool align = true, const std::string& swizzle_mask, bool align = true,
const std::string& address_register_name = std::string()) { const std::string& address_register_name = std::string()) {
if (align) if (align)
output << std::setw(4) << std::right; output << std::setw(4) << std::right;
@ -83,20 +83,18 @@ static void print_input(std::ostringstream& output, const SourceRegister& input,
QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole: {
{
switch (index.column()) { switch (index.column()) {
case 0: case 0:
if (par->info.HasLabel(index.row())) if (par->info.HasLabel(index.row()))
return QString::fromStdString(par->info.GetLabel(index.row())); return QString::fromStdString(par->info.GetLabel(index.row()));
return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); return QString("%1").arg(4 * index.row(), 4, 16, QLatin1Char('0'));
case 1: case 1:
return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0'));
case 2: case 2: {
{
std::ostringstream output; std::ostringstream output;
output.flags(std::ios::uppercase); output.flags(std::ios::uppercase);
@ -117,8 +115,9 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
const Instruction instr = par->info.code[index.row()]; const Instruction instr = par->info.code[index.row()];
const OpCode opcode = instr.opcode; const OpCode opcode = instr.opcode;
const OpCode::Info opcode_info = opcode.GetInfo(); const OpCode::Info opcode_info = opcode.GetInfo();
const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd ? const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd
instr.mad.operand_desc_id.Value() : instr.common.operand_desc_id.Value(); ? instr.mad.operand_desc_id.Value()
: instr.common.operand_desc_id.Value();
const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern; const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
// longest known instruction name: "setemit " // longest known instruction name: "setemit "
@ -136,15 +135,14 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
break; break;
case OpCode::Type::Arithmetic: case OpCode::Type::Arithmetic:
case OpCode::Type::MultiplyAdd: case OpCode::Type::MultiplyAdd: {
{
// Use custom code for special instructions // Use custom code for special instructions
switch (opcode.EffectiveOpCode()) { switch (opcode.EffectiveOpCode()) {
case OpCode::Id::CMP: case OpCode::Id::CMP: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
// NOTE: CMP always writes both cc components, so we do not consider the dest mask here. // NOTE: CMP always writes both cc components, so we do not consider the dest
// mask here.
output << " cc.xy"; output << " cc.xy";
AlignToColumn(kOutputColumnWidth); AlignToColumn(kOutputColumnWidth);
@ -152,22 +150,29 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
SourceRegister src2 = instr.common.GetSrc2(false); SourceRegister src2 = instr.common.GetSrc2(false);
output << ' '; output << ' ';
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), false, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) << ' '; swizzle.SelectorToString(false).substr(0, 1), false,
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(0,1), false); instr.common.AddressRegisterName());
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x)
<< ' ';
print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true).substr(0, 1), false);
output << ", "; output << ", ";
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), false, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) << ' '; swizzle.SelectorToString(false).substr(1, 1), false,
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(1,1), false); instr.common.AddressRegisterName());
output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y)
<< ' ';
print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true).substr(1, 1), false);
break; break;
} }
case OpCode::Id::MAD: case OpCode::Id::MAD:
case OpCode::Id::MADI: case OpCode::Id::MADI: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
@ -175,34 +180,42 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted); SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted);
SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted); SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted);
output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.'
<< swizzle.DestMaskToString();
AlignToColumn(kOutputColumnWidth); AlignToColumn(kOutputColumnWidth);
print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector)); print_input(output, src1, swizzle.negate_src1,
SelectorToString(swizzle.src1_selector));
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
if (src_is_inverted) { if (src_is_inverted) {
print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector)); print_input(output, src2, swizzle.negate_src2,
SelectorToString(swizzle.src2_selector));
} else { } else {
print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector), true, instr.mad.AddressRegisterName()); print_input(output, src2, swizzle.negate_src2,
SelectorToString(swizzle.src2_selector), true,
instr.mad.AddressRegisterName());
} }
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
if (src_is_inverted) { if (src_is_inverted) {
print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector), true, instr.mad.AddressRegisterName()); print_input(output, src3, swizzle.negate_src3,
SelectorToString(swizzle.src3_selector), true,
instr.mad.AddressRegisterName());
} else { } else {
print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector)); print_input(output, src3, swizzle.negate_src3,
SelectorToString(swizzle.src3_selector));
} }
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
break; break;
} }
default: default: {
{
AlignToColumn(kOpcodeColumnWidth); AlignToColumn(kOpcodeColumnWidth);
bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
if (opcode_info.subtype & OpCode::Info::Dest) { if (opcode_info.subtype & OpCode::Info::Dest) {
// e.g. "r12.xy__" // e.g. "r12.xy__"
output << std::setw(3) << std::right << instr.common.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); output << std::setw(3) << std::right << instr.common.dest.Value().GetName()
<< '.' << swizzle.DestMaskToString();
} else if (opcode_info.subtype == OpCode::Info::MOVA) { } else if (opcode_info.subtype == OpCode::Info::MOVA) {
output << " a0." << swizzle.DestMaskToString(); output << " a0." << swizzle.DestMaskToString();
} }
@ -210,14 +223,18 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
if (opcode_info.subtype & OpCode::Info::Src1) { if (opcode_info.subtype & OpCode::Info::Src1) {
SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); SourceRegister src1 = instr.common.GetSrc1(src_is_inverted);
print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), true, instr.common.AddressRegisterName()); print_input(output, src1, swizzle.negate_src1,
swizzle.SelectorToString(false), true,
instr.common.AddressRegisterName());
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
} }
// TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 // TODO: In some cases, the Address Register is used as an index for SRC2
// instead of SRC1
if (opcode_info.subtype & OpCode::Info::Src2) { if (opcode_info.subtype & OpCode::Info::Src2) {
SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); SourceRegister src2 = instr.common.GetSrc2(src_is_inverted);
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true)); print_input(output, src2, swizzle.negate_src2,
swizzle.SelectorToString(true));
AlignToColumn(kInputOperandColumnWidth); AlignToColumn(kInputOperandColumnWidth);
} }
break; break;
@ -228,8 +245,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
case OpCode::Type::Conditional: case OpCode::Type::Conditional:
case OpCode::Type::UniformFlowControl: case OpCode::Type::UniformFlowControl: {
{
output << ' '; output << ' ';
switch (opcode.EffectiveOpCode()) { switch (opcode.EffectiveOpCode()) {
@ -242,7 +258,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
output << '('; output << '(';
if (instr.flow_control.op != instr.flow_control.JustY) { if (instr.flow_control.op != instr.flow_control.JustY) {
if (instr.flow_control.refx) output << '!'; if (instr.flow_control.refx)
output << '!';
output << "cc.x"; output << "cc.x";
} }
@ -253,7 +270,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
if (instr.flow_control.op != instr.flow_control.JustX) { if (instr.flow_control.op != instr.flow_control.JustX) {
if (instr.flow_control.refy) output << '!'; if (instr.flow_control.refy)
output << '!';
output << "cc.y"; output << "cc.y";
} }
@ -266,17 +284,23 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
u32 target_addr_else = instr.flow_control.dest_offset; u32 target_addr_else = instr.flow_control.dest_offset;
if (opcode_info.subtype & OpCode::Info::HasAlternative) { if (opcode_info.subtype & OpCode::Info::HasAlternative) {
output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); output << "else jump to 0x" << std::setw(4) << std::right
<< std::setfill('0') << std::hex
<< (4 * instr.flow_control.dest_offset);
} else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) { } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) {
output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0')
<< std::hex << (4 * instr.flow_control.dest_offset);
} else { } else {
// TODO: Handle other cases // TODO: Handle other cases
output << "(unknown destination)"; output << "(unknown destination)";
} }
if (opcode_info.subtype & OpCode::Info::HasFinishPoint) { if (opcode_info.subtype & OpCode::Info::HasFinishPoint) {
output << " (return on 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex output << " (return on 0x" << std::setw(4) << std::right
<< (4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions) << ')'; << std::setfill('0') << std::hex
<< (4 * instr.flow_control.dest_offset +
4 * instr.flow_control.num_instructions)
<< ')';
} }
break; break;
@ -300,8 +324,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
case Qt::FontRole: case Qt::FontRole:
return GetMonospaceFont(); return GetMonospaceFont();
case Qt::BackgroundRole: case Qt::BackgroundRole: {
{
// Highlight current instruction // Highlight current instruction
int current_record_index = par->cycle_index->value(); int current_record_index = par->cycle_index->value();
if (current_record_index < static_cast<int>(par->debug_data.records.size())) { if (current_record_index < static_cast<int>(par->debug_data.records.size())) {
@ -319,10 +342,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
return QBrush(QColor(192, 192, 192)); return QBrush(QColor(192, 192, 192));
} }
// TODO: Draw arrows for each "reachable" instruction to visualize control flow // TODO: Draw arrows for each "reachable" instruction to visualize control flow
default: default:
break; break;
} }
@ -331,23 +352,24 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
} }
void GraphicsVertexShaderWidget::DumpShader() { void GraphicsVertexShaderWidget::DumpShader() {
QString filename = QFileDialog::getSaveFileName(this, tr("Save Shader Dump"), "shader_dump.shbin", QString filename = QFileDialog::getSaveFileName(
tr("Shader Binary (*.shbin)")); this, tr("Save Shader Dump"), "shader_dump.shbin", tr("Shader Binary (*.shbin)"));
if (filename.isEmpty()) { if (filename.isEmpty()) {
// If the user canceled the dialog, don't dump anything. // If the user canceled the dialog, don't dump anything.
return; return;
} }
auto& setup = Pica::g_state.vs; auto& setup = Pica::g_state.vs;
auto& config = Pica::g_state.regs.vs; auto& config = Pica::g_state.regs.vs;
Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, Pica::g_state.regs.vs_output_attributes); Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup,
Pica::g_state.regs.vs_output_attributes);
} }
GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
QWidget* parent) std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
: BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
setObjectName("PicaVertexShader"); setObjectName("PicaVertexShader");
// Clear input vertex data so that it contains valid float values in case a debug shader // Clear input vertex data so that it contains valid float values in case a debug shader
@ -365,7 +387,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
input_data[i]->setValidator(new QDoubleValidator(input_data[i])); input_data[i]->setValidator(new QDoubleValidator(input_data[i]));
} }
breakpoint_warning = new QLabel(tr("(data only available at vertex shader invocation breakpoints)")); breakpoint_warning =
new QLabel(tr("(data only available at vertex shader invocation breakpoints)"));
// TODO: Add some button for jumping to the shader entry point // TODO: Add some button for jumping to the shader entry point
@ -442,7 +465,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
// Set a minimum height so that the size of this label doesn't cause the rest of the bottom // Set a minimum height so that the size of this label doesn't cause the rest of the bottom
// part of the UI to keep jumping up and down when cycling through instructions. // part of the UI to keep jumping up and down when cycling through instructions.
instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * 6); instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() *
6);
instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop); instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop);
main_layout->addWidget(instruction_description); main_layout->addWidget(instruction_description);
@ -471,7 +495,8 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); memcpy(&input_vertex, vertex_data, sizeof(input_vertex));
for (unsigned attr = 0; attr < 16; ++attr) { for (unsigned attr = 0; attr < 16; ++attr) {
for (unsigned comp = 0; comp < 4; ++comp) { for (unsigned comp = 0; comp < 4; ++comp) {
input_data[4 * attr + comp]->setText(QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); input_data[4 * attr + comp]->setText(
QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32()));
} }
} }
breakpoint_warning->hide(); breakpoint_warning->hide();
@ -498,10 +523,11 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
info.swizzle_info.push_back({pattern}); info.swizzle_info.push_back({pattern});
u32 entry_point = Pica::g_state.regs.vs.main_offset; u32 entry_point = Pica::g_state.regs.vs.main_offset;
info.labels.insert({ entry_point, "main" }); info.labels.insert({entry_point, "main"});
// Generate debug information // Generate debug information
debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup); debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config,
shader_setup);
// Reload widget state // Reload widget state
for (int attr = 0; attr < num_attributes; ++attr) { for (int attr = 0; attr < num_attributes; ++attr) {
@ -537,29 +563,60 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
auto& record = debug_data.records[index]; auto& record = debug_data.records[index];
if (record.mask & Pica::Shader::DebugDataRecord::SRC1) if (record.mask & Pica::Shader::DebugDataRecord::SRC1)
text += tr("SRC1: %1, %2, %3, %4\n").arg(record.src1.x.ToFloat32()).arg(record.src1.y.ToFloat32()).arg(record.src1.z.ToFloat32()).arg(record.src1.w.ToFloat32()); text += tr("SRC1: %1, %2, %3, %4\n")
.arg(record.src1.x.ToFloat32())
.arg(record.src1.y.ToFloat32())
.arg(record.src1.z.ToFloat32())
.arg(record.src1.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::SRC2) if (record.mask & Pica::Shader::DebugDataRecord::SRC2)
text += tr("SRC2: %1, %2, %3, %4\n").arg(record.src2.x.ToFloat32()).arg(record.src2.y.ToFloat32()).arg(record.src2.z.ToFloat32()).arg(record.src2.w.ToFloat32()); text += tr("SRC2: %1, %2, %3, %4\n")
.arg(record.src2.x.ToFloat32())
.arg(record.src2.y.ToFloat32())
.arg(record.src2.z.ToFloat32())
.arg(record.src2.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::SRC3) if (record.mask & Pica::Shader::DebugDataRecord::SRC3)
text += tr("SRC3: %1, %2, %3, %4\n").arg(record.src3.x.ToFloat32()).arg(record.src3.y.ToFloat32()).arg(record.src3.z.ToFloat32()).arg(record.src3.w.ToFloat32()); text += tr("SRC3: %1, %2, %3, %4\n")
.arg(record.src3.x.ToFloat32())
.arg(record.src3.y.ToFloat32())
.arg(record.src3.z.ToFloat32())
.arg(record.src3.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN)
text += tr("DEST_IN: %1, %2, %3, %4\n").arg(record.dest_in.x.ToFloat32()).arg(record.dest_in.y.ToFloat32()).arg(record.dest_in.z.ToFloat32()).arg(record.dest_in.w.ToFloat32()); text += tr("DEST_IN: %1, %2, %3, %4\n")
.arg(record.dest_in.x.ToFloat32())
.arg(record.dest_in.y.ToFloat32())
.arg(record.dest_in.z.ToFloat32())
.arg(record.dest_in.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT)
text += tr("DEST_OUT: %1, %2, %3, %4\n").arg(record.dest_out.x.ToFloat32()).arg(record.dest_out.y.ToFloat32()).arg(record.dest_out.z.ToFloat32()).arg(record.dest_out.w.ToFloat32()); text += tr("DEST_OUT: %1, %2, %3, %4\n")
.arg(record.dest_out.x.ToFloat32())
.arg(record.dest_out.y.ToFloat32())
.arg(record.dest_out.z.ToFloat32())
.arg(record.dest_out.w.ToFloat32());
if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT)
text += tr("Addres Registers: %1, %2\n").arg(record.address_registers[0]).arg(record.address_registers[1]); text += tr("Addres Registers: %1, %2\n")
.arg(record.address_registers[0])
.arg(record.address_registers[1]);
if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT)
text += tr("Compare Result: %1, %2\n").arg(record.conditional_code[0] ? "true" : "false").arg(record.conditional_code[1] ? "true" : "false"); text += tr("Compare Result: %1, %2\n")
.arg(record.conditional_code[0] ? "true" : "false")
.arg(record.conditional_code[1] ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN)
text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN)
text += tr("Dynamic Conditions: %1, %2\n").arg(record.cond_cmp[0] ? "true" : "false").arg(record.cond_cmp[1] ? "true" : "false"); text += tr("Dynamic Conditions: %1, %2\n")
.arg(record.cond_cmp[0] ? "true" : "false")
.arg(record.cond_cmp[1] ? "true" : "false");
if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN)
text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n").arg(record.loop_int.x).arg(record.loop_int.y).arg(record.loop_int.z).arg(record.loop_int.w); text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n")
.arg(record.loop_int.x)
.arg(record.loop_int.y)
.arg(record.loop_int.z)
.arg(record.loop_int.w);
text += tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); text +=
tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0'));
if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) {
text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0'));
} else { } else {
@ -570,6 +627,7 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
// Emit model update notification and scroll to current instruction // Emit model update notification and scroll to current instruction
QModelIndex instr_index = model->index(record.instruction_offset, 0); QModelIndex instr_index = model->index(record.instruction_offset, 0);
emit model->dataChanged(instr_index, model->index(record.instruction_offset, model->columnCount())); emit model->dataChanged(instr_index,
model->index(record.instruction_offset, model->columnCount()));
binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible);
} }

View file

@ -26,7 +26,8 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
private: private:
GraphicsVertexShaderWidget* par; GraphicsVertexShaderWidget* par;
@ -56,7 +57,9 @@ private slots:
/** /**
* Reload widget based on the current PICA200 state * Reload widget based on the current PICA200 state
* @param replace_vertex_data If true, invalidate all current vertex data * @param replace_vertex_data If true, invalidate all current vertex data
* @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to specify that no valid vertex data can be retrieved currently. Only used if replace_vertex_data is true. * @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to
* specify that no valid vertex data can be retrieved currently. Only used if
* replace_vertex_data is true.
*/ */
void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr); void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr);
@ -66,9 +69,12 @@ private:
GraphicsVertexShaderModel* model; GraphicsVertexShaderModel* model;
/// TODO: Move these into a single struct /// TODO: Move these into a single struct
std::array<QLineEdit*, 4*16> input_data; // A text box for each of the 4 components of up to 16 vertex attributes std::array<QLineEdit*, 4 * 16>
std::array<QWidget*, 16> input_data_container; // QWidget containing the QLayout containing each vertex attribute input_data; // A text box for each of the 4 components of up to 16 vertex attributes
std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute which the vertex attribute maps to std::array<QWidget*, 16>
input_data_container; // QWidget containing the QLayout containing each vertex attribute
std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute
// which the vertex attribute maps to
// Text to be shown when input vertex data is not retrievable // Text to be shown when input vertex data is not retrievable
QLabel* breakpoint_warning; QLabel* breakpoint_warning;

View file

@ -22,57 +22,58 @@
using namespace Common::Profiling; using namespace Common::Profiling;
static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) {
{
static auto duration_to_float = [](Duration dur) -> float { static auto duration_to_float = [](Duration dur) -> float {
using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>; using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
return std::chrono::duration_cast<FloatMs>(dur).count(); return std::chrono::duration_cast<FloatMs>(dur).count();
}; };
switch (col) { switch (col) {
case 1: return duration_to_float(duration.avg); case 1:
case 2: return duration_to_float(duration.min); return duration_to_float(duration.avg);
case 3: return duration_to_float(duration.max); case 2:
default: return QVariant(); return duration_to_float(duration.min);
case 3:
return duration_to_float(duration.max);
default:
return QVariant();
} }
} }
ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) {
{
updateProfilingInfo(); updateProfilingInfo();
} }
QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const {
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) { switch (section) {
case 0: return tr("Category"); case 0:
case 1: return tr("Avg"); return tr("Category");
case 2: return tr("Min"); case 1:
case 3: return tr("Max"); return tr("Avg");
case 2:
return tr("Min");
case 3:
return tr("Max");
} }
} }
return QVariant(); return QVariant();
} }
QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const {
{
return createIndex(row, column); return createIndex(row, column);
} }
QModelIndex ProfilerModel::parent(const QModelIndex& child) const QModelIndex ProfilerModel::parent(const QModelIndex& child) const {
{
return QModelIndex(); return QModelIndex();
} }
int ProfilerModel::columnCount(const QModelIndex& parent) const int ProfilerModel::columnCount(const QModelIndex& parent) const {
{
return 4; return 4;
} }
int ProfilerModel::rowCount(const QModelIndex& parent) const int ProfilerModel::rowCount(const QModelIndex& parent) const {
{
if (parent.isValid()) { if (parent.isValid()) {
return 0; return 0;
} else { } else {
@ -80,8 +81,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const
} }
} }
QVariant ProfilerModel::data(const QModelIndex& index, int role) const QVariant ProfilerModel::data(const QModelIndex& index, int role) const {
{
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
if (index.row() == 0) { if (index.row() == 0) {
if (index.column() == 0) { if (index.column() == 0) {
@ -101,14 +101,12 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const
return QVariant(); return QVariant();
} }
void ProfilerModel::updateProfilingInfo() void ProfilerModel::updateProfilingInfo() {
{
results = GetTimingResultsAggregator()->GetAggregatedResults(); results = GetTimingResultsAggregator()->GetAggregatedResults();
emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3)); emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3));
} }
ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) {
{
ui.setupUi(this); ui.setupUi(this);
model = new ProfilerModel(this); model = new ProfilerModel(this);
@ -118,8 +116,7 @@ ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent)
connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo())); connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo()));
} }
void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) {
{
if (enable) { if (enable) {
update_timer.start(100); update_timer.start(100);
model->updateProfilingInfo(); model->updateProfilingInfo();
@ -157,9 +154,7 @@ private:
#endif #endif
MicroProfileDialog::MicroProfileDialog(QWidget* parent) MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
: QWidget(parent, Qt::Dialog)
{
setObjectName("MicroProfile"); setObjectName("MicroProfile");
setWindowTitle(tr("MicroProfile")); setWindowTitle(tr("MicroProfile"));
resize(1000, 600); resize(1000, 600);
@ -175,7 +170,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent)
layout->addWidget(widget); layout->addWidget(widget);
setLayout(layout); setLayout(layout);
// Configure focus so that widget is focusable and the dialog automatically forwards focus to it. // Configure focus so that widget is focusable and the dialog automatically forwards focus to
// it.
setFocusProxy(widget); setFocusProxy(widget);
widget->setFocusPolicy(Qt::StrongFocus); widget->setFocusPolicy(Qt::StrongFocus);
widget->setFocus(); widget->setFocus();
@ -207,7 +203,6 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) {
QWidget::hideEvent(ev); QWidget::hideEvent(ev);
} }
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED
/// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
@ -308,7 +303,8 @@ void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 tex
} }
} }
void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color, MicroProfileBoxType type) { void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color,
MicroProfileBoxType type) {
QColor color = QColor::fromRgba(hex_color); QColor color = QColor::fromRgba(hex_color);
QBrush brush = color; QBrush brush = color;
if (type == MicroProfileBoxTypeBar) { if (type == MicroProfileBoxTypeBar) {
@ -326,7 +322,7 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color)
static std::vector<QPointF> point_buf; static std::vector<QPointF> point_buf;
for (u32 i = 0; i < vertices_length; ++i) { for (u32 i = 0; i < vertices_length; ++i) {
point_buf.emplace_back(vertices[i*2 + 0], vertices[i*2 + 1]); point_buf.emplace_back(vertices[i * 2 + 0], vertices[i * 2 + 1]);
} }
// hex_color does not include an alpha, so it must be assumed to be 255 // hex_color does not include an alpha, so it must be assumed to be 255

View file

@ -13,15 +13,16 @@
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/profiler_reporting.h" #include "common/profiler_reporting.h"
class ProfilerModel : public QAbstractItemModel class ProfilerModel : public QAbstractItemModel {
{
Q_OBJECT Q_OBJECT
public: public:
ProfilerModel(QObject* parent); ProfilerModel(QObject* parent);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override; QModelIndex parent(const QModelIndex& child) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@ -34,8 +35,7 @@ private:
Common::Profiling::AggregatedFrameResult results; Common::Profiling::AggregatedFrameResult results;
}; };
class ProfilerWidget : public QDockWidget class ProfilerWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:
@ -51,7 +51,6 @@ private:
QTimer update_timer; QTimer update_timer;
}; };
class MicroProfileDialog : public QWidget { class MicroProfileDialog : public QWidget {
Q_OBJECT Q_OBJECT

View file

@ -4,12 +4,10 @@
#include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/ramview.h"
GRamView::GRamView(QWidget* parent) : QHexEdit(parent) GRamView::GRamView(QWidget* parent) : QHexEdit(parent) {
{
} }
void GRamView::OnCPUStepped() void GRamView::OnCPUStepped() {
{
// TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams... // TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams...
//setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8)); // setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8));
} }

View file

@ -4,8 +4,7 @@
#include "qhexedit.h" #include "qhexedit.h"
class GRamView : public QHexEdit class GRamView : public QHexEdit {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -7,8 +7,8 @@
#include "citra_qt/debugger/registers.h" #include "citra_qt/debugger/registers.h"
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "core/core.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/core.h"
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
cpu_regs_ui.setupUi(this); cpu_regs_ui.setupUi(this);
@ -16,7 +16,8 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
tree = cpu_regs_ui.treeWidget; tree = cpu_regs_ui.treeWidget;
tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers")))); tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers"))));
tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers")))); tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers"))));
tree->addTopLevelItem(vfp_system_registers = new QTreeWidgetItem(QStringList(tr("VFP System Registers")))); tree->addTopLevelItem(vfp_system_registers =
new QTreeWidgetItem(QStringList(tr("VFP System Registers"))));
tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR"))); tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR")));
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
@ -63,10 +64,12 @@ void RegistersWidget::OnDebugModeEntered() {
return; return;
for (int i = 0; i < core_registers->childCount(); ++i) for (int i = 0; i < core_registers->childCount(); ++i)
core_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0'))); core_registers->child(i)->setText(
1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0')));
for (int i = 0; i < vfp_registers->childCount(); ++i) for (int i = 0; i < vfp_registers->childCount(); ++i)
vfp_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); vfp_registers->child(i)->setText(
1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0')));
UpdateCPSRValues(); UpdateCPSRValues();
UpdateVFPSystemRegisterValues(); UpdateVFPSystemRegisterValues();
@ -130,21 +133,24 @@ void RegistersWidget::UpdateCPSRValues() {
const u32 cpsr_val = Core::g_app_core->GetCPSR(); const u32 cpsr_val = Core::g_app_core->GetCPSR();
cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
cpsr->child(0)->setText(1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode cpsr->child(0)->setText(
cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State 1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State
cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable
cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable
cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianess cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort
cpsr->child(6)->setText(1, QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM) cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianess
cpsr->child(7)->setText(1, QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal cpsr->child(6)->setText(1,
cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle cpsr->child(7)->setText(1,
cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal
cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify
cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle
cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation
cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow
cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend
cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero
cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than
} }
void RegistersWidget::CreateVFPSystemRegisterChildren() { void RegistersWidget::CreateVFPSystemRegisterChildren() {
@ -188,9 +194,9 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() {
} }
void RegistersWidget::UpdateVFPSystemRegisterValues() { void RegistersWidget::UpdateVFPSystemRegisterValues() {
const u32 fpscr_val = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR); const u32 fpscr_val = Core::g_app_core->GetVFPSystemReg(VFP_FPSCR);
const u32 fpexc_val = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC); const u32 fpexc_val = Core::g_app_core->GetVFPSystemReg(VFP_FPEXC);
const u32 fpinst_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST); const u32 fpinst_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST);
const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2); const u32 fpinst2_val = Core::g_app_core->GetVFPSystemReg(VFP_FPINST2);
QTreeWidgetItem* const fpscr = vfp_system_registers->child(0); QTreeWidgetItem* const fpscr = vfp_system_registers->child(0);
@ -228,6 +234,8 @@ void RegistersWidget::UpdateVFPSystemRegisterValues() {
fpexc->child(6)->setText(1, QString::number((fpexc_val >> 30) & 1)); fpexc->child(6)->setText(1, QString::number((fpexc_val >> 30) & 1));
fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1)); fpexc->child(7)->setText(1, QString::number((fpexc_val >> 31) & 1));
vfp_system_registers->child(2)->setText(1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0'))); vfp_system_registers->child(2)->setText(
vfp_system_registers->child(3)->setText(1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0'))); 1, QString("0x%1").arg(fpinst_val, 8, 16, QLatin1Char('0')));
vfp_system_registers->child(3)->setText(
1, QString("0x%1").arg(fpinst2_val, 8, 16, QLatin1Char('0')));
} }

View file

@ -10,8 +10,7 @@ class QTreeWidget;
class QTreeWidgetItem; class QTreeWidgetItem;
class EmuThread; class EmuThread;
class RegistersWidget : public QDockWidget class RegistersWidget : public QDockWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -16,8 +16,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/string_util.h" #include "common/string_util.h"
GameList::GameList(QWidget* parent) GameList::GameList(QWidget* parent) {
{
QVBoxLayout* layout = new QVBoxLayout; QVBoxLayout* layout = new QVBoxLayout;
tree_view = new QTreeView; tree_view = new QTreeView;
@ -38,9 +37,11 @@ GameList::GameList(QWidget* parent)
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&))); connect(tree_view, SIGNAL(activated(const QModelIndex&)), this,
SLOT(ValidateEntry(const QModelIndex&)));
// We must register all custom types with the Qt Automoc system so that we are able to use it with // We must register all custom types with the Qt Automoc system so that we are able to use it
// with
// signals/slots. In this case, QList falls under the umbrells of custom types. // signals/slots. In this case, QList falls under the umbrells of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@ -48,18 +49,15 @@ GameList::GameList(QWidget* parent)
setLayout(layout); setLayout(layout);
} }
GameList::~GameList() GameList::~GameList() {
{
emit ShouldCancelWorker(); emit ShouldCancelWorker();
} }
void GameList::AddEntry(QList<QStandardItem*> entry_items) void GameList::AddEntry(QList<QStandardItem*> entry_items) {
{
item_model->invisibleRootItem()->appendRow(entry_items); item_model->invisibleRootItem()->appendRow(entry_items);
} }
void GameList::ValidateEntry(const QModelIndex& item) void GameList::ValidateEntry(const QModelIndex& item) {
{
// We don't care about the individual QStandardItem that was selected, but its row. // We don't care about the individual QStandardItem that was selected, but its row.
int row = item_model->itemFromIndex(item)->row(); int row = item_model->itemFromIndex(item)->row();
QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
@ -73,14 +71,13 @@ void GameList::ValidateEntry(const QModelIndex& item)
emit GameChosen(file_path); emit GameChosen(file_path);
} }
void GameList::DonePopulating() void GameList::DonePopulating() {
{
tree_view->setEnabled(true); tree_view->setEnabled(true);
} }
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
{ if (!FileUtil::Exists(dir_path.toStdString()) ||
if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) { !FileUtil::IsDirectory(dir_path.toStdString())) {
LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data()); LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data());
return; return;
} }
@ -92,22 +89,22 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
emit ShouldCancelWorker(); emit ShouldCancelWorker();
GameListWorker* worker = new GameListWorker(dir_path, deep_scan); GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this, SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection); connect(worker, SIGNAL(EntryReady(QList<QStandardItem*>)), this,
SLOT(AddEntry(QList<QStandardItem*>)), Qt::QueuedConnection);
connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection); connect(worker, SIGNAL(Finished()), this, SLOT(DonePopulating()), Qt::QueuedConnection);
// Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel without delay. // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel
// without delay.
connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection); connect(this, SIGNAL(ShouldCancelWorker()), worker, SLOT(Cancel()), Qt::DirectConnection);
QThreadPool::globalInstance()->start(worker); QThreadPool::globalInstance()->start(worker);
current_worker = std::move(worker); current_worker = std::move(worker);
} }
void GameList::SaveInterfaceLayout() void GameList::SaveInterfaceLayout() {
{
UISettings::values.gamelist_header_state = tree_view->header()->saveState(); UISettings::values.gamelist_header_state = tree_view->header()->saveState();
} }
void GameList::LoadInterfaceLayout() void GameList::LoadInterfaceLayout() {
{
auto header = tree_view->header(); auto header = tree_view->header();
if (!header->restoreState(UISettings::values.gamelist_header_state)) { if (!header->restoreState(UISettings::values.gamelist_header_state)) {
// We are using the name column to display icons and titles // We are using the name column to display icons and titles
@ -118,10 +115,8 @@ void GameList::LoadInterfaceLayout()
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
} }
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
{ const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
const auto callback = [this, recursion](unsigned* num_entries_out,
const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name; std::string physical_name = directory + DIR_SEP + virtual_name;
@ -138,7 +133,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
emit EntryReady({ emit EntryReady({
new GameListItemPath(QString::fromStdString(physical_name), smdh), new GameListItemPath(QString::fromStdString(physical_name), smdh),
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)), new GameListItemSize(FileUtil::GetSize(physical_name)),
}); });
} else if (recursion > 0) { } else if (recursion > 0) {
@ -151,15 +147,13 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
} }
void GameListWorker::run() void GameListWorker::run() {
{
stop_processing = false; stop_processing = false;
AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
emit Finished(); emit Finished();
} }
void GameListWorker::Cancel() void GameListWorker::Cancel() {
{
disconnect(this, 0, 0, 0); disconnect(this, 0, 0, 0);
stop_processing = true; stop_processing = true;
} }

View file

@ -14,7 +14,6 @@
class GameListWorker; class GameListWorker;
class GameList : public QWidget { class GameList : public QWidget {
Q_OBJECT Q_OBJECT

View file

@ -12,8 +12,8 @@
#include <QString> #include <QString>
#include "citra_qt/util/util.h" #include "citra_qt/util/util.h"
#include "common/string_util.h"
#include "common/color.h" #include "common/color.h"
#include "common/string_util.h"
#include "core/loader/smdh.h" #include "core/loader/smdh.h"
@ -51,19 +51,22 @@ static QPixmap GetDefaultIcon(bool large) {
* @param language title language * @param language title language
* @return QString short title * @return QString short title
*/ */
static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh,
Loader::SMDH::TitleLanguage language) {
return QString::fromUtf16(smdh.GetShortTitle(language).data()); return QString::fromUtf16(smdh.GetShortTitle(language).data());
} }
class GameListItem : public QStandardItem { class GameListItem : public QStandardItem {
public: public:
GameListItem(): QStandardItem() {} GameListItem() : QStandardItem() {
GameListItem(const QString& string): QStandardItem(string) {} }
virtual ~GameListItem() override {} GameListItem(const QString& string) : QStandardItem(string) {
}
virtual ~GameListItem() override {
}
}; };
/** /**
* A specialization of GameListItem for path values. * A specialization of GameListItem for path values.
* This class ensures that for every full path value it holds, a correct string representation * This class ensures that for every full path value it holds, a correct string representation
@ -76,9 +79,9 @@ public:
static const int FullPathRole = Qt::UserRole + 1; static const int FullPathRole = Qt::UserRole + 1;
static const int TitleRole = Qt::UserRole + 2; static const int TitleRole = Qt::UserRole + 2;
GameListItemPath(): GameListItem() {} GameListItemPath() : GameListItem() {
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data): GameListItem() }
{ GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() {
setData(game_path, FullPathRole); setData(game_path, FullPathRole);
if (!Loader::IsValidSMDH(smdh_data)) { if (!Loader::IsValidSMDH(smdh_data)) {
@ -94,13 +97,15 @@ public:
setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole); setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
// Get title form SMDH // Get title form SMDH
setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English),
TitleRole);
} }
QVariant data(int role) const override { QVariant data(int role) const override {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
std::string filename; std::string filename;
Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, nullptr); Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
nullptr);
QString title = data(TitleRole).toString(); QString title = data(TitleRole).toString();
return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title); return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title);
} else { } else {
@ -109,7 +114,6 @@ public:
} }
}; };
/** /**
* A specialization of GameListItem for size values. * A specialization of GameListItem for size values.
* This class ensures that for every numerical size value it holds (in bytes), a correct * This class ensures that for every numerical size value it holds (in bytes), a correct
@ -120,14 +124,13 @@ class GameListItemSize : public GameListItem {
public: public:
static const int SizeRole = Qt::UserRole + 1; static const int SizeRole = Qt::UserRole + 1;
GameListItemSize(): GameListItem() {} GameListItemSize() : GameListItem() {
GameListItemSize(const qulonglong size_bytes): GameListItem() }
{ GameListItemSize(const qulonglong size_bytes) : GameListItem() {
setData(size_bytes, SizeRole); setData(size_bytes, SizeRole);
} }
void setData(const QVariant& value, int role) override void setData(const QVariant& value, int role) override {
{
// By specializing setData for SizeRole, we can ensure that the numerical and string // By specializing setData for SizeRole, we can ensure that the numerical and string
// representations of the data are always accurate and in the correct format. // representations of the data are always accurate and in the correct format.
if (role == SizeRole) { if (role == SizeRole) {
@ -141,15 +144,14 @@ public:
/** /**
* This operator is, in practice, only used by the TreeView sorting systems. * This operator is, in practice, only used by the TreeView sorting systems.
* Override it so that it will correctly sort by numerical value instead of by string representation. * Override it so that it will correctly sort by numerical value instead of by string
* representation.
*/ */
bool operator<(const QStandardItem& other) const override bool operator<(const QStandardItem& other) const override {
{
return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong();
} }
}; };
/** /**
* Asynchronous worker object for populating the game list. * Asynchronous worker object for populating the game list.
* Communicates with other threads through Qt's signal/slot system. * Communicates with other threads through Qt's signal/slot system.
@ -158,8 +160,9 @@ class GameListWorker : public QObject, public QRunnable {
Q_OBJECT Q_OBJECT
public: public:
GameListWorker(QString dir_path, bool deep_scan): GameListWorker(QString dir_path, bool deep_scan)
QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {
}
public slots: public slots:
/// Starts the processing of directory tree information. /// Starts the processing of directory tree information.

View file

@ -4,16 +4,16 @@
#include <map> #include <map>
#include <QtGlobal>
#include <QKeySequence> #include <QKeySequence>
#include <QShortcut> #include <QShortcut>
#include <QtGlobal>
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
struct Hotkey struct Hotkey {
{ Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {
Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} }
QKeySequence keyseq; QKeySequence keyseq;
QShortcut* shortcut; QShortcut* shortcut;
@ -25,33 +25,28 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
HotkeyGroupMap hotkey_groups; HotkeyGroupMap hotkey_groups;
void SaveHotkeys() void SaveHotkeys() {
{
UISettings::values.shortcuts.clear(); UISettings::values.shortcuts.clear();
for (auto group : hotkey_groups) for (auto group : hotkey_groups) {
{ for (auto hotkey : group.second) {
for (auto hotkey : group.second)
{
UISettings::values.shortcuts.emplace_back( UISettings::values.shortcuts.emplace_back(
UISettings::Shortcut(group.first + "/" + hotkey.first, UISettings::Shortcut(group.first + "/" + hotkey.first,
UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
hotkey.second.context))); hotkey.second.context)));
} }
} }
} }
void LoadHotkeys() void LoadHotkeys() {
{ // Make sure NOT to use a reference here because it would become invalid once we call
// Make sure NOT to use a reference here because it would become invalid once we call beginGroup() // beginGroup()
for (auto shortcut : UISettings::values.shortcuts) for (auto shortcut : UISettings::values.shortcuts) {
{
QStringList cat = shortcut.first.split("/"); QStringList cat = shortcut.first.split("/");
Q_ASSERT(cat.size() >= 2); Q_ASSERT(cat.size() >= 2);
// RegisterHotkey assigns default keybindings, so use old values as default parameters // RegisterHotkey assigns default keybindings, so use old values as default parameters
Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
if (!shortcut.second.first.isEmpty()) if (!shortcut.second.first.isEmpty()) {
{
hk.keyseq = QKeySequence::fromString(shortcut.second.first); hk.keyseq = QKeySequence::fromString(shortcut.second.first);
hk.context = (Qt::ShortcutContext)shortcut.second.second; hk.context = (Qt::ShortcutContext)shortcut.second.second;
} }
@ -60,17 +55,15 @@ void LoadHotkeys()
} }
} }
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context) void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq,
{ Qt::ShortcutContext default_context) {
if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) {
{
hotkey_groups[group][action].keyseq = default_keyseq; hotkey_groups[group][action].keyseq = default_keyseq;
hotkey_groups[group][action].context = default_context; hotkey_groups[group][action].context = default_context;
} }
} }
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) {
{
Hotkey& hk = hotkey_groups[group][action]; Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut) if (!hk.shortcut)
@ -79,16 +72,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
return hk.shortcut; return hk.shortcut;
} }
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent)
{
ui.setupUi(this); ui.setupUi(this);
for (auto group : hotkey_groups) for (auto group : hotkey_groups) {
{
QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
for (auto hotkey : group.second) for (auto hotkey : group.second) {
{
QStringList columns; QStringList columns;
columns << hotkey.first << hotkey.second.keyseq.toString(); columns << hotkey.first << hotkey.second.keyseq.toString();
QTreeWidgetItem* item = new QTreeWidgetItem(columns); QTreeWidgetItem* item = new QTreeWidgetItem(columns);

View file

@ -16,36 +16,42 @@ class QShortcut;
* *
* @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
* @param action Name of the action (e.g. "Start Emulation", "Load Image") * @param action Name of the action (e.g. "Start Emulation", "Load Image")
* @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings file before * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings
* @param default_context Default context to assign if the hotkey wasn't present in the settings file before * file before
* @param default_context Default context to assign if the hotkey wasn't present in the settings
* file before
* @warning Both the group and action strings will be displayed in the hotkey settings dialog * @warning Both the group and action strings will be displayed in the hotkey settings dialog
*/ */
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq = QKeySequence(), Qt::ShortcutContext default_context = Qt::WindowShortcut); void RegisterHotkey(const QString& group, const QString& action,
const QKeySequence& default_keyseq = QKeySequence(),
Qt::ShortcutContext default_context = Qt::WindowShortcut);
/** /**
* Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots.
* *
* @param widget Parent widget of the returned QShortcut. * @param widget Parent widget of the returned QShortcut.
* @warning If multiple QWidgets' call this function for the same action, the returned QShortcut will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
* will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent.
*/ */
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
/** /**
* Saves all registered hotkeys to the settings file. * Saves all registered hotkeys to the settings file.
* *
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a settings group will be created to store the key sequence and the hotkey context. * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
* settings group will be created to store the key sequence and the hotkey context.
*/ */
void SaveHotkeys(); void SaveHotkeys();
/** /**
* Loads hotkeys from the settings file. * Loads hotkeys from the settings file.
* *
* @note Yet unregistered hotkeys which are present in the settings will automatically be registered. * @note Yet unregistered hotkeys which are present in the settings will automatically be
* registered.
*/ */
void LoadHotkeys(); void LoadHotkeys();
class GHotkeysDialog : public QWidget class GHotkeysDialog : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:

View file

@ -10,9 +10,9 @@
#define QT_NO_OPENGL #define QT_NO_OPENGL
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QtGui>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QtGui>
#include "qhexedit.h" #include "qhexedit.h"
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
@ -36,27 +36,26 @@
#include "citra_qt/debugger/ramview.h" #include "citra_qt/debugger/ramview.h"
#include "citra_qt/debugger/registers.h" #include "citra_qt/debugger/registers.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/platform.h" #include "common/platform.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "core/core.h"
#include "core/settings.h"
#include "core/system.h"
#include "core/arm/disassembler/load_symbol_map.h" #include "core/arm/disassembler/load_symbol_map.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/settings.h"
#include "core/system.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
{
Pica::g_debug_context = Pica::DebugContext::Construct(); Pica::g_debug_context = Pica::DebugContext::Construct();
ui.setupUi(this); ui.setupUi(this);
@ -91,7 +90,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
graphicsWidget = new GPUCommandStreamWidget(this); graphicsWidget = new GPUCommandStreamWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
graphicsWidget ->hide(); graphicsWidget->hide();
graphicsCommandsWidget = new GPUCommandListWidget(this); graphicsCommandsWidget = new GPUCommandListWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
@ -110,7 +109,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
graphicsTracingWidget->hide(); graphicsTracingWidget->hide();
auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this); auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this);
connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer())); connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
SLOT(OnCreateGraphicsSurfaceViewer()));
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(graphicsSurfaceViewerAction); debug_menu->addAction(graphicsSurfaceViewerAction);
@ -167,35 +167,44 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
UpdateRecentFiles(); UpdateRecentFiles();
// Setup connections // Setup connections
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection); connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)),
Qt::DirectConnection);
connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection); connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),
Qt::DirectConnection);
connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap())); connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot())); connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this,
SLOT(OnMenuSelectGameListRoot()));
connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
// Setup hotkeys // Setup hotkeys
RegisterHotkey("Main Window", "Load File", QKeySequence::Open); RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
RegisterHotkey("Main Window", "Start Emulation"); RegisterHotkey("Main Window", "Start Emulation");
LoadHotkeys(); LoadHotkeys();
connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); SLOT(OnMenuLoadFile()));
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this,
SLOT(OnStartGame()));
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); std::string window_title =
Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(window_title.c_str()); setWindowTitle(window_title.c_str());
show(); show();
@ -208,8 +217,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
} }
} }
GMainWindow::~GMainWindow() GMainWindow::~GMainWindow() {
{
// will get automatically deleted otherwise // will get automatically deleted otherwise
if (render_window->parent() == nullptr) if (render_window->parent() == nullptr)
delete render_window; delete render_window;
@ -217,19 +225,18 @@ GMainWindow::~GMainWindow()
Pica::g_debug_context.reset(); Pica::g_debug_context.reset();
} }
void GMainWindow::OnDisplayTitleBars(bool show) void GMainWindow::OnDisplayTitleBars(bool show) {
{
QList<QDockWidget*> widgets = findChildren<QDockWidget*>(); QList<QDockWidget*> widgets = findChildren<QDockWidget*>();
if (show) { if (show) {
for (QDockWidget* widget: widgets) { for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget(); QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(nullptr); widget->setTitleBarWidget(nullptr);
if (old != nullptr) if (old != nullptr)
delete old; delete old;
} }
} else { } else {
for (QDockWidget* widget: widgets) { for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget(); QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(new QWidget()); widget->setTitleBarWidget(new QWidget());
if (old != nullptr) if (old != nullptr)
@ -249,7 +256,8 @@ bool GMainWindow::InitializeSystem() {
if (!gladLoadGL()) { if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while starting Citra!"), QMessageBox::critical(this, tr("Error while starting Citra!"),
tr("Failed to initialize the video core!\n\n" tr("Failed to initialize the video core!\n\n"
"Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); "Please ensure that your GPU supports OpenGL 3.3 and that you "
"have the latest graphics driver."));
return false; return false;
} }
@ -260,7 +268,8 @@ bool GMainWindow::InitializeSystem() {
case System::Result::ErrorInitVideoCore: case System::Result::ErrorInitVideoCore:
QMessageBox::critical(this, tr("Error while starting Citra!"), QMessageBox::critical(this, tr("Error while starting Citra!"),
tr("Failed to initialize the video core!\n\n" tr("Failed to initialize the video core!\n\n"
"Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); "Please ensure that your GPU supports OpenGL 3.3 and that you "
"have the latest graphics driver."));
break; break;
default: default:
@ -293,8 +302,12 @@ bool GMainWindow::LoadROM(const std::string& filename) {
QMessageBox popup_error; QMessageBox popup_error;
popup_error.setTextFormat(Qt::RichText); popup_error.setTextFormat(Qt::RichText);
popup_error.setWindowTitle(tr("Error while loading ROM!")); popup_error.setWindowTitle(tr("Error while loading ROM!"));
popup_error.setText(tr("The game that you are trying to load must be decrypted before being used with Citra.<br/><br/>" popup_error.setText(
"For more information on dumping and decrypting games, please see: <a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); tr("The game that you are trying to load must be decrypted before being used with "
"Citra.<br/><br/>"
"For more information on dumping and decrypting games, please see: <a "
"href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://"
"citra-emu.org/wiki/Dumping-Game-Cartridges</a>"));
popup_error.setIcon(QMessageBox::Critical); popup_error.setIcon(QMessageBox::Critical);
popup_error.exec(); popup_error.exec();
break; break;
@ -306,8 +319,7 @@ bool GMainWindow::LoadROM(const std::string& filename) {
case Loader::ResultStatus::Error: case Loader::ResultStatus::Error:
default: default:
QMessageBox::critical(this, tr("Error while loading ROM!"), QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!"));
tr("Unknown error!"));
break; break;
} }
return false; return false;
@ -332,13 +344,20 @@ void GMainWindow::BootGame(const std::string& filename) {
emu_thread->start(); emu_thread->start();
connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame())); connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame()));
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); // before the CPU continues
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()),
connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget,
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget,
SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()),
Qt::BlockingQueuedConnection);
// Update the GUI // Update the GUI
registersWidget->OnDebugModeEntered(); registersWidget->OnDebugModeEntered();
@ -393,10 +412,12 @@ void GMainWindow::StoreRecentFile(const std::string& filename) {
} }
void GMainWindow::UpdateRecentFiles() { void GMainWindow::UpdateRecentFiles() {
unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); unsigned int num_recent_files =
std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
for (unsigned int i = 0; i < num_recent_files; i++) { for (unsigned int i = 0; i < num_recent_files; i++) {
QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName()); QString text = QString("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text); actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]); actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]); actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]);
@ -420,7 +441,9 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
} }
void GMainWindow::OnMenuLoadFile() { void GMainWindow::OnMenuLoadFile() {
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)")); QString filename =
QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path,
tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
UISettings::values.roms_path = QFileInfo(filename).path(); UISettings::values.roms_path = QFileInfo(filename).path();
@ -429,7 +452,8 @@ void GMainWindow::OnMenuLoadFile() {
} }
void GMainWindow::OnMenuLoadSymbolMap() { void GMainWindow::OnMenuLoadSymbolMap() {
QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)")); QString filename = QFileDialog::getOpenFileName(
this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
UISettings::values.symbols_path = QFileInfo(filename).path(); UISettings::values.symbols_path = QFileInfo(filename).path();
@ -455,7 +479,8 @@ void GMainWindow::OnMenuRecentFile() {
BootGame(filename.toStdString()); BootGame(filename.toStdString());
} else { } else {
// Display an error message and remove the file from the list. // Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename)); QMessageBox::information(this, tr("File not found"),
tr("File \"%1\" not found").arg(filename));
UISettings::values.recent_files.removeOne(filename); UISettings::values.recent_files.removeOne(filename);
UpdateRecentFiles(); UpdateRecentFiles();
@ -512,8 +537,7 @@ void GMainWindow::ToggleWindowMode() {
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
ConfigureDialog configureDialog(this); ConfigureDialog configureDialog(this);
auto result = configureDialog.exec(); auto result = configureDialog.exec();
if (result == QDialog::Accepted) if (result == QDialog::Accepted) {
{
configureDialog.applyConfiguration(); configureDialog.applyConfiguration();
render_window->ReloadSetKeymaps(); render_window->ReloadSetKeymaps();
config->Save(); config->Save();
@ -531,9 +555,9 @@ bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true; return true;
auto answer = QMessageBox::question(this, tr("Citra"), auto answer =
tr("Are you sure you want to close Citra?"), QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No); QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
return answer != QMessageBox::No; return answer != QMessageBox::No;
} }
@ -575,9 +599,7 @@ int main(int argc, char* argv[]) {
Log::SetFilter(&log_filter); Log::SetFilter(&log_filter);
MicroProfileOnThreadCreate("Frontend"); MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ SCOPE_EXIT({ MicroProfileShutdown(); });
MicroProfileShutdown();
});
// Init settings params // Init settings params
QCoreApplication::setOrganizationName("Citra team"); QCoreApplication::setOrganizationName("Citra team");
@ -586,7 +608,8 @@ int main(int argc, char* argv[]) {
QApplication::setAttribute(Qt::AA_X11InitThreads); QApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication app(argc, argv); QApplication app(argc, argv);
// Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders // Qt changes the locale and causes issues in float conversion using std::to_string() when
// generating shaders
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
GMainWindow main_window; GMainWindow main_window;

View file

@ -5,8 +5,8 @@
#ifndef _CITRA_QT_MAIN_HXX_ #ifndef _CITRA_QT_MAIN_HXX_
#define _CITRA_QT_MAIN_HXX_ #define _CITRA_QT_MAIN_HXX_
#include <memory>
#include <QMainWindow> #include <QMainWindow>
#include <memory>
#include "ui_main.h" #include "ui_main.h"
@ -23,11 +23,11 @@ class CallstackWidget;
class GPUCommandStreamWidget; class GPUCommandStreamWidget;
class GPUCommandListWidget; class GPUCommandListWidget;
class GMainWindow : public QMainWindow class GMainWindow : public QMainWindow {
{
Q_OBJECT Q_OBJECT
static const int max_recent_files_item = 10; ///< Max number of recently loaded items to keep track static const int max_recent_files_item =
10; ///< Max number of recently loaded items to keep track
// TODO: Make use of this! // TODO: Make use of this!
enum { enum {

View file

@ -7,5 +7,4 @@
namespace UISettings { namespace UISettings {
Values values = {}; Values values = {};
} }

View file

@ -5,14 +5,14 @@
#pragma once #pragma once
#include <QByteArray> #include <QByteArray>
#include <QStringList>
#include <QString> #include <QString>
#include <QStringList>
#include <vector> #include <vector>
namespace UISettings { namespace UISettings {
using ContextualShortcut = std::pair<QString, int> ; using ContextualShortcut = std::pair<QString, int>;
using Shortcut = std::pair<QString, ContextualShortcut>; using Shortcut = std::pair<QString, ContextualShortcut>;
struct Values { struct Values {
@ -43,5 +43,4 @@ struct Values {
}; };
extern Values values; extern Values values;
} }

View file

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -29,15 +28,15 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cstdlib>
#include <QLineEdit> #include <QLineEdit>
#include <QRegExpValidator> #include <QRegExpValidator>
#include <cstdlib>
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "common/assert.h" #include "common/assert.h"
CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) CSpinBox::CSpinBox(QWidget* parent)
{ : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) {
// TODO: Might be nice to not immediately call the slot. // TODO: Might be nice to not immediately call the slot.
// Think of an address that is being replaced by a different one, in which case a lot // Think of an address that is being replaced by a different one, in which case a lot
// invalid intermediate addresses would be read from during editing. // invalid intermediate addresses would be read from during editing.
@ -46,8 +45,7 @@ CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), min_value(-100),
UpdateText(); UpdateText();
} }
void CSpinBox::SetValue(qint64 val) void CSpinBox::SetValue(qint64 val) {
{
auto old_value = value; auto old_value = value;
value = std::max(std::min(val, max_value), min_value); value = std::max(std::min(val, max_value), min_value);
@ -57,8 +55,7 @@ void CSpinBox::SetValue(qint64 val)
} }
} }
void CSpinBox::SetRange(qint64 min, qint64 max) void CSpinBox::SetRange(qint64 min, qint64 max) {
{
min_value = min; min_value = min;
max_value = max; max_value = max;
@ -66,8 +63,7 @@ void CSpinBox::SetRange(qint64 min, qint64 max)
UpdateText(); UpdateText();
} }
void CSpinBox::stepBy(int steps) void CSpinBox::stepBy(int steps) {
{
auto new_value = value; auto new_value = value;
// Scale number of steps by the currently selected digit // Scale number of steps by the currently selected digit
// TODO: Move this code elsewhere and enable it. // TODO: Move this code elsewhere and enable it.
@ -93,8 +89,7 @@ void CSpinBox::stepBy(int steps)
UpdateText(); UpdateText();
} }
QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const {
{
StepEnabled ret = StepNone; StepEnabled ret = StepNone;
if (value > min_value) if (value > min_value)
@ -106,29 +101,25 @@ QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
return ret; return ret;
} }
void CSpinBox::SetBase(int base) void CSpinBox::SetBase(int base) {
{
this->base = base; this->base = base;
UpdateText(); UpdateText();
} }
void CSpinBox::SetNumDigits(int num_digits) void CSpinBox::SetNumDigits(int num_digits) {
{
this->num_digits = num_digits; this->num_digits = num_digits;
UpdateText(); UpdateText();
} }
void CSpinBox::SetPrefix(const QString& prefix) void CSpinBox::SetPrefix(const QString& prefix) {
{
this->prefix = prefix; this->prefix = prefix;
UpdateText(); UpdateText();
} }
void CSpinBox::SetSuffix(const QString& suffix) void CSpinBox::SetSuffix(const QString& suffix) {
{
this->suffix = suffix; this->suffix = suffix;
UpdateText(); UpdateText();
@ -161,8 +152,7 @@ static QString StringToInputMask(const QString& input) {
return mask; return mask;
} }
void CSpinBox::UpdateText() void CSpinBox::UpdateText() {
{
// If a fixed number of digits is used, we put the line edit in insertion mode by setting an // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
// input mask. // input mask.
QString mask; QString mask;
@ -179,10 +169,9 @@ void CSpinBox::UpdateText()
// The greatest signed 64-bit number has 19 decimal digits. // The greatest signed 64-bit number has 19 decimal digits.
// TODO: Could probably make this more generic with some logarithms. // TODO: Could probably make this more generic with some logarithms.
// For reference, unsigned 64-bit can have up to 20 decimal digits. // For reference, unsigned 64-bit can have up to 20 decimal digits.
int digits = (num_digits != 0) ? num_digits int digits = (num_digits != 0)
: (base == 16) ? 16 ? num_digits
: (base == 10) ? 19 : (base == 16) ? 16 : (base == 10) ? 19 : 0xFF; // fallback case...
: 0xFF; // fallback case...
// Match num_digits digits // Match num_digits digits
// Digits irrelevant to the chosen number base are filtered in the validator // Digits irrelevant to the chosen number base are filtered in the validator
@ -203,29 +192,24 @@ void CSpinBox::UpdateText()
lineEdit()->setCursorPosition(cursor_position); lineEdit()->setCursorPosition(cursor_position);
} }
QString CSpinBox::TextFromValue() QString CSpinBox::TextFromValue() {
{ return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") +
return prefix QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() +
+ QString(HasSign() ? ((value < 0) ? "-" : "+") : "") suffix;
+ QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper()
+ suffix;
} }
qint64 CSpinBox::ValueFromText() qint64 CSpinBox::ValueFromText() {
{
unsigned strpos = prefix.length(); unsigned strpos = prefix.length();
QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
return num_string.toLongLong(nullptr, base); return num_string.toLongLong(nullptr, base);
} }
bool CSpinBox::HasSign() const bool CSpinBox::HasSign() const {
{
return base == 10 && min_value < 0; return base == 10 && min_value < 0;
} }
void CSpinBox::OnEditingFinished() void CSpinBox::OnEditingFinished() {
{
// Only update for valid input // Only update for valid input
QString input = lineEdit()->text(); QString input = lineEdit()->text();
int pos = 0; int pos = 0;
@ -233,8 +217,7 @@ void CSpinBox::OnEditingFinished()
SetValue(ValueFromText()); SetValue(ValueFromText());
} }
QValidator::State CSpinBox::validate(QString& input, int& pos) const QValidator::State CSpinBox::validate(QString& input, int& pos) const {
{
if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
return QValidator::Invalid; return QValidator::Invalid;

View file

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -29,7 +28,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#include <QAbstractSpinBox> #include <QAbstractSpinBox>

View file

@ -16,10 +16,12 @@ QFont GetMonospaceFont() {
} }
QString ReadableByteSize(qulonglong size) { QString ReadableByteSize(qulonglong size) {
static const std::array<const char*, 6> units = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" }; static const std::array<const char*, 6> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
if (size == 0) if (size == 0)
return "0"; return "0";
int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), static_cast<int>(units.size())); int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)),
return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1) static_cast<int>(units.size()));
.arg(units[digit_groups]); return QString("%L1 %2")
.arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
.arg(units[digit_groups]);
} }

View file

@ -18,25 +18,29 @@
// enough for our purposes. // enough for our purposes.
template <typename Fn> template <typename Fn>
#if defined(_MSC_VER) #if defined(_MSC_VER)
__declspec(noinline, noreturn) __declspec(noinline, noreturn)
#elif defined(__GNUC__) #elif defined(__GNUC__)
__attribute__((noinline, noreturn, cold)) __attribute__((noinline, noreturn, cold))
#endif #endif
static void assert_noinline_call(const Fn& fn) { static void assert_noinline_call(const Fn& fn) {
fn(); fn();
Crash(); Crash();
exit(1); // Keeps GCC's mouth shut about this actually returning exit(1); // Keeps GCC's mouth shut about this actually returning
} }
#define ASSERT(_a_) \ #define ASSERT(_a_) \
do if (!(_a_)) { assert_noinline_call([] { \ do \
LOG_CRITICAL(Debug, "Assertion Failed!"); \ if (!(_a_)) { \
}); } while (0) assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
} \
while (0)
#define ASSERT_MSG(_a_, ...) \ #define ASSERT_MSG(_a_, ...) \
do if (!(_a_)) { assert_noinline_call([&] { \ do \
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \ if (!(_a_)) { \
}); } while (0) assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
} \
while (0)
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__) #define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)

View file

@ -1,7 +1,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -29,7 +28,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#include <cstddef> #include <cstddef>
@ -111,9 +109,8 @@
* symptoms. * symptoms.
*/ */
#pragma pack(1) #pragma pack(1)
template<std::size_t position, std::size_t bits, typename T> template <std::size_t position, std::size_t bits, typename T>
struct BitField struct BitField {
{
private: private:
// We hide the copy assigment operator here, because the default copy // We hide the copy assigment operator here, because the default copy
// assignment would copy the full storage value, rather than just the bits // assignment would copy the full storage value, rather than just the bits
@ -141,13 +138,10 @@ public:
} }
FORCE_INLINE T Value() const { FORCE_INLINE T Value() const {
if (std::numeric_limits<T>::is_signed) if (std::numeric_limits<T>::is_signed) {
{ std::size_t shift = 8 * sizeof(T) - bits;
std::size_t shift = 8 * sizeof(T)-bits;
return (T)((storage << (shift - position)) >> shift); return (T)((storage << (shift - position)) >> shift);
} } else {
else
{
return (T)((storage & GetMask()) >> position); return (T)((storage & GetMask()) >> position);
} }
} }
@ -162,15 +156,14 @@ private:
// T is an enumeration. Note that T is wrapped within an enable_if in the // T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using // former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly. // std::underlying_type<T>::type directly.
typedef typename std::conditional < std::is_enum<T>::value, typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::underlying_type<T>, std::enable_if<true, T>>::type::type StorageType;
std::enable_if < true, T >> ::type::type StorageType;
// Unsigned version of StorageType // Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU; typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
FORCE_INLINE StorageType GetMask() const { FORCE_INLINE StorageType GetMask() const {
return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position; return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
} }
StorageType storage; StorageType storage;
@ -186,5 +179,6 @@ private:
#pragma pack() #pragma pack()
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value, "BitField must be trivially copyable"); static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value,
"BitField must be trivially copyable");
#endif #endif

View file

@ -18,49 +18,60 @@ namespace Common {
#ifdef _WIN32 #ifdef _WIN32
template <typename T> template <typename T>
static inline int CountSetBits(T v) static inline int CountSetBits(T v) {
{
// from https://graphics.stanford.edu/~seander/bithacks.html // from https://graphics.stanford.edu/~seander/bithacks.html
// GCC has this built in, but MSVC's intrinsic will only emit the actual // GCC has this built in, but MSVC's intrinsic will only emit the actual
// POPCNT instruction, which we're not depending on // POPCNT instruction, which we're not depending on
v = v - ((v >> 1) & (T)~(T)0/3); v = v - ((v >> 1) & (T) ~(T)0 / 3);
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
v = (v + (v >> 4)) & (T)~(T)0/255*15; v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
} }
static inline int LeastSignificantSetBit(u8 val) static inline int LeastSignificantSetBit(u8 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u16 val) static inline int LeastSignificantSetBit(u16 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u32 val) static inline int LeastSignificantSetBit(u32 val) {
{
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u64 val) static inline int LeastSignificantSetBit(u64 val) {
{
unsigned long index; unsigned long index;
_BitScanForward64(&index, val); _BitScanForward64(&index, val);
return (int)index; return (int)index;
} }
#else #else
static inline int CountSetBits(u8 val) { return __builtin_popcount(val); } static inline int CountSetBits(u8 val) {
static inline int CountSetBits(u16 val) { return __builtin_popcount(val); } return __builtin_popcount(val);
static inline int CountSetBits(u32 val) { return __builtin_popcount(val); } }
static inline int CountSetBits(u64 val) { return __builtin_popcountll(val); } static inline int CountSetBits(u16 val) {
static inline int LeastSignificantSetBit(u8 val) { return __builtin_ctz(val); } return __builtin_popcount(val);
static inline int LeastSignificantSetBit(u16 val) { return __builtin_ctz(val); } }
static inline int LeastSignificantSetBit(u32 val) { return __builtin_ctz(val); } static inline int CountSetBits(u32 val) {
static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val); } return __builtin_popcount(val);
}
static inline int CountSetBits(u64 val) {
return __builtin_popcountll(val);
}
static inline int LeastSignificantSetBit(u8 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u16 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u32 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u64 val) {
return __builtin_ctzll(val);
}
#endif #endif
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e. // Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
@ -84,100 +95,144 @@ static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val);
// TODO: use constexpr when MSVC gets out of the Dark Ages // TODO: use constexpr when MSVC gets out of the Dark Ages
template <typename IntTy> template <typename IntTy>
class BitSet class BitSet {
{
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
public: public:
// A reference to a particular bit, returned from operator[]. // A reference to a particular bit, returned from operator[].
class Ref class Ref {
{
public: public:
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} }
operator bool() const { return (m_bs->m_val & m_mask) != 0; } Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {
bool operator=(bool set) }
{ operator bool() const {
return (m_bs->m_val & m_mask) != 0;
}
bool operator=(bool set) {
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
return set; return set;
} }
private: private:
BitSet* m_bs; BitSet* m_bs;
IntTy m_mask; IntTy m_mask;
}; };
// A STL-like iterator is required to be able to use range-based for loops. // A STL-like iterator is required to be able to use range-based for loops.
class Iterator class Iterator {
{
public: public:
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {} Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {
Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {} }
Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; } Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {
int operator*() { return m_bit; } }
Iterator& operator++() Iterator& operator=(Iterator other) {
{ new (this) Iterator(other);
if (m_val == 0) return *this;
{ }
int operator*() {
return m_bit;
}
Iterator& operator++() {
if (m_val == 0) {
m_bit = -1; m_bit = -1;
} } else {
else
{
int bit = LeastSignificantSetBit(m_val); int bit = LeastSignificantSetBit(m_val);
m_val &= ~(1 << bit); m_val &= ~(1 << bit);
m_bit = bit; m_bit = bit;
} }
return *this; return *this;
} }
Iterator operator++(int _) Iterator operator++(int _) {
{
Iterator other(*this); Iterator other(*this);
++*this; ++*this;
return other; return other;
} }
bool operator==(Iterator other) const { return m_bit == other.m_bit; } bool operator==(Iterator other) const {
bool operator!=(Iterator other) const { return m_bit != other.m_bit; } return m_bit == other.m_bit;
}
bool operator!=(Iterator other) const {
return m_bit != other.m_bit;
}
private: private:
IntTy m_val; IntTy m_val;
int m_bit; int m_bit;
}; };
BitSet() : m_val(0) {} BitSet() : m_val(0) {
explicit BitSet(IntTy val) : m_val(val) {} }
BitSet(std::initializer_list<int> init) explicit BitSet(IntTy val) : m_val(val) {
{ }
BitSet(std::initializer_list<int> init) {
m_val = 0; m_val = 0;
for (int bit : init) for (int bit : init)
m_val |= (IntTy)1 << bit; m_val |= (IntTy)1 << bit;
} }
static BitSet AllTrue(size_t count) static BitSet AllTrue(size_t count) {
{ return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
} }
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); } Ref operator[](size_t bit) {
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; } return Ref(this, (IntTy)1 << bit);
bool operator==(BitSet other) const { return m_val == other.m_val; } }
bool operator!=(BitSet other) const { return m_val != other.m_val; } const Ref operator[](size_t bit) const {
bool operator<(BitSet other) const { return m_val < other.m_val; } return (*const_cast<BitSet*>(this))[bit];
bool operator>(BitSet other) const { return m_val > other.m_val; } }
BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); } bool operator==(BitSet other) const {
BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); } return m_val == other.m_val;
BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); } }
BitSet operator~() const { return BitSet(~m_val); } bool operator!=(BitSet other) const {
BitSet& operator|=(BitSet other) { return *this = *this | other; } return m_val != other.m_val;
BitSet& operator&=(BitSet other) { return *this = *this & other; } }
BitSet& operator^=(BitSet other) { return *this = *this ^ other; } bool operator<(BitSet other) const {
return m_val < other.m_val;
}
bool operator>(BitSet other) const {
return m_val > other.m_val;
}
BitSet operator|(BitSet other) const {
return BitSet(m_val | other.m_val);
}
BitSet operator&(BitSet other) const {
return BitSet(m_val & other.m_val);
}
BitSet operator^(BitSet other) const {
return BitSet(m_val ^ other.m_val);
}
BitSet operator~() const {
return BitSet(~m_val);
}
BitSet& operator|=(BitSet other) {
return *this = *this | other;
}
BitSet& operator&=(BitSet other) {
return *this = *this & other;
}
BitSet& operator^=(BitSet other) {
return *this = *this ^ other;
}
operator u32() = delete; operator u32() = delete;
operator bool() { return m_val != 0; } operator bool() {
return m_val != 0;
}
// Warning: Even though on modern CPUs this is a single fast instruction, // Warning: Even though on modern CPUs this is a single fast instruction,
// Dolphin's official builds do not currently assume POPCNT support on x86, // Dolphin's official builds do not currently assume POPCNT support on x86,
// so slower explicit bit twiddling is generated. Still should generally // so slower explicit bit twiddling is generated. Still should generally
// be faster than a loop. // be faster than a loop.
unsigned int Count() const { return CountSetBits(m_val); } unsigned int Count() const {
return CountSetBits(m_val);
}
Iterator begin() const { Iterator it(m_val, 0); return ++it; } Iterator begin() const {
Iterator end() const { return Iterator(m_val, -1); } Iterator it(m_val, 0);
return ++it;
}
Iterator end() const {
return Iterator(m_val, -1);
}
IntTy m_val; IntTy m_val;
}; };

View file

@ -5,30 +5,27 @@
#include "common/break_points.h" #include "common/break_points.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include <sstream>
#include <algorithm> #include <algorithm>
#include <sstream>
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
{
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end(); return it != m_BreakPoints.end();
} }
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const bool BreakPoints::IsTempBreakPoint(u32 iAddress) const {
{ auto cond = [&iAddress](const TBreakPoint& bp) {
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress && bp.bTemporary; }; return bp.iAddress == iAddress && bp.bTemporary;
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end(); return it != m_BreakPoints.end();
} }
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const {
{
TBreakPointsStr bps; TBreakPointsStr bps;
for (auto breakpoint : m_BreakPoints) for (auto breakpoint : m_BreakPoints) {
{ if (!breakpoint.bTemporary) {
if (!breakpoint.bTemporary)
{
std::stringstream bp; std::stringstream bp;
bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : ""); bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
bps.push_back(bp.str()); bps.push_back(bp.str());
@ -38,10 +35,8 @@ BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
return bps; return bps;
} }
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) {
{ for (auto bps_item : bps) {
for (auto bps_item : bps)
{
TBreakPoint bp; TBreakPoint bp;
std::stringstream bpstr; std::stringstream bpstr;
bpstr << std::hex << bps_item; bpstr << std::hex << bps_item;
@ -52,18 +47,15 @@ void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
} }
} }
void BreakPoints::Add(const TBreakPoint& bp) void BreakPoints::Add(const TBreakPoint& bp) {
{ if (!IsAddressBreakPoint(bp.iAddress)) {
if (!IsAddressBreakPoint(bp.iAddress))
{
m_BreakPoints.push_back(bp); m_BreakPoints.push_back(bp);
//if (jit) // if (jit)
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
} }
} }
void BreakPoints::Add(u32 em_address, bool temp) void BreakPoints::Add(u32 em_address, bool temp) {
{
if (!IsAddressBreakPoint(em_address)) // only add new addresses if (!IsAddressBreakPoint(em_address)) // only add new addresses
{ {
TBreakPoint pt; // breakpoint settings TBreakPoint pt; // breakpoint settings
@ -73,22 +65,20 @@ void BreakPoints::Add(u32 em_address, bool temp)
m_BreakPoints.push_back(pt); m_BreakPoints.push_back(pt);
//if (jit) // if (jit)
// jit->GetBlockCache()->InvalidateICache(em_address, 4); // jit->GetBlockCache()->InvalidateICache(em_address, 4);
} }
} }
void BreakPoints::Remove(u32 em_address) void BreakPoints::Remove(u32 em_address) {
{
auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; }; auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
if (it != m_BreakPoints.end()) if (it != m_BreakPoints.end())
m_BreakPoints.erase(it); m_BreakPoints.erase(it);
} }
void BreakPoints::Clear() void BreakPoints::Clear() {
{ // if (jit)
//if (jit)
//{ //{
// std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(), // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
// [](const TBreakPoint& bp) // [](const TBreakPoint& bp)

View file

@ -4,28 +4,28 @@
#pragma once #pragma once
#include <vector>
#include <string> #include <string>
#include <vector>
#include "common/common_types.h" #include "common/common_types.h"
class DebugInterface; class DebugInterface;
struct TBreakPoint struct TBreakPoint {
{ u32 iAddress;
u32 iAddress;
bool bOn; bool bOn;
bool bTemporary; bool bTemporary;
}; };
// Code breakpoints. // Code breakpoints.
class BreakPoints class BreakPoints {
{
public: public:
typedef std::vector<TBreakPoint> TBreakPoints; typedef std::vector<TBreakPoint> TBreakPoints;
typedef std::vector<std::string> TBreakPointsStr; typedef std::vector<std::string> TBreakPointsStr;
const TBreakPoints& GetBreakPoints() { return m_BreakPoints; } const TBreakPoints& GetBreakPoints() {
return m_BreakPoints;
}
TBreakPointsStr GetStrings() const; TBreakPointsStr GetStrings() const;
void AddFromStrings(const TBreakPointsStr& bps); void AddFromStrings(const TBreakPointsStr& bps);
@ -35,7 +35,7 @@ public:
bool IsTempBreakPoint(u32 iAddress) const; bool IsTempBreakPoint(u32 iAddress) const;
// Add BreakPoint // Add BreakPoint
void Add(u32 em_address, bool temp=false); void Add(u32 em_address, bool temp = false);
void Add(const TBreakPoint& bp); void Add(const TBreakPoint& bp);
// Remove Breakpoint // Remove Breakpoint
@ -46,5 +46,5 @@ public:
private: private:
TBreakPoints m_BreakPoints; TBreakPoints m_BreakPoints;
u32 m_iBreakOnCount; u32 m_iBreakOnCount;
}; };

View file

@ -41,81 +41,86 @@
#include "common/logging/log.h" #include "common/logging/log.h"
template <class T> template <class T>
struct LinkedListItem : public T struct LinkedListItem : public T {
{ LinkedListItem<T>* next;
LinkedListItem<T> *next;
}; };
class PointerWrap; class PointerWrap;
class PointerWrapSection class PointerWrapSection {
{
public: public:
PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { PointerWrapSection(PointerWrap& p, int ver, const char* title)
: p_(p), ver_(ver), title_(title) {
} }
~PointerWrapSection(); ~PointerWrapSection();
bool operator == (const int &v) const { return ver_ == v; } bool operator==(const int& v) const {
bool operator != (const int &v) const { return ver_ != v; } return ver_ == v;
bool operator <= (const int &v) const { return ver_ <= v; } }
bool operator >= (const int &v) const { return ver_ >= v; } bool operator!=(const int& v) const {
bool operator < (const int &v) const { return ver_ < v; } return ver_ != v;
bool operator > (const int &v) const { return ver_ > v; } }
bool operator<=(const int& v) const {
return ver_ <= v;
}
bool operator>=(const int& v) const {
return ver_ >= v;
}
bool operator<(const int& v) const {
return ver_ < v;
}
bool operator>(const int& v) const {
return ver_ > v;
}
operator bool() const { operator bool() const {
return ver_ > 0; return ver_ > 0;
} }
private: private:
PointerWrap &p_; PointerWrap& p_;
int ver_; int ver_;
const char *title_; const char* title_;
}; };
// Wrapper class // Wrapper class
class PointerWrap class PointerWrap {
{ // This makes it a compile error if you forget to define DoState() on non-POD.
// This makes it a compile error if you forget to define DoState() on non-POD. // Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
// Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
#ifdef _MSC_VER #ifdef _MSC_VER
template<typename T, bool isPOD = std::is_pod<T>::value, bool isPointer = std::is_pointer<T>::value> template <typename T, bool isPOD = std::is_pod<T>::value,
bool isPointer = std::is_pointer<T>::value>
#else #else
template<typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value> template <typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value>
#endif #endif
struct DoHelper struct DoHelper {
{ static void DoArray(PointerWrap* p, T* x, int count) {
static void DoArray(PointerWrap *p, T *x, int count)
{
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
p->Do(x[i]); p->Do(x[i]);
} }
static void Do(PointerWrap *p, T &x) static void Do(PointerWrap* p, T& x) {
{
p->DoClass(x); p->DoClass(x);
} }
}; };
template<typename T> template <typename T>
struct DoHelper<T, true, false> struct DoHelper<T, true, false> {
{ static void DoArray(PointerWrap* p, T* x, int count) {
static void DoArray(PointerWrap *p, T *x, int count) p->DoVoid((void*)x, sizeof(T) * count);
{
p->DoVoid((void *)x, sizeof(T) * count);
} }
static void Do(PointerWrap *p, T &x) static void Do(PointerWrap* p, T& x) {
{ p->DoVoid((void*)&x, sizeof(x));
p->DoVoid((void *)&x, sizeof(x));
} }
}; };
public: public:
enum Mode { enum Mode {
MODE_READ = 1, // load MODE_READ = 1, // load
MODE_WRITE, // save MODE_WRITE, // save
MODE_MEASURE, // calculate size MODE_MEASURE, // calculate size
MODE_VERIFY, // compare MODE_VERIFY, // compare
}; };
enum Error { enum Error {
@ -124,247 +129,239 @@ public:
ERROR_FAILURE = 2, ERROR_FAILURE = 2,
}; };
u8 **ptr; u8** ptr;
Mode mode; Mode mode;
Error error; Error error;
public: public:
PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {} PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {
PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {} }
PointerWrap(unsigned char** ptr_, int mode_)
: ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {
}
PointerWrapSection Section(const char *title, int ver) { PointerWrapSection Section(const char* title, int ver) {
return Section(title, ver, ver); return Section(title, ver, ver);
} }
// The returned object can be compared against the version that was loaded. // The returned object can be compared against the version that was loaded.
// This can be used to support versions as old as minVer. // This can be used to support versions as old as minVer.
// Version = 0 means the section was not found. // Version = 0 means the section was not found.
PointerWrapSection Section(const char *title, int minVer, int ver) { PointerWrapSection Section(const char* title, int minVer, int ver) {
char marker[16] = {0}; char marker[16] = {0};
int foundVersion = ver; int foundVersion = ver;
strncpy(marker, title, sizeof(marker)); strncpy(marker, title, sizeof(marker));
if (!ExpectVoid(marker, sizeof(marker))) if (!ExpectVoid(marker, sizeof(marker))) {
{
// Might be before we added name markers for safety. // Might be before we added name markers for safety.
if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion)))
DoMarker(title); DoMarker(title);
// Wasn't found, but maybe we can still load the state. // Wasn't found, but maybe we can still load the state.
else else
foundVersion = 0; foundVersion = 0;
} } else
else
Do(foundVersion); Do(foundVersion);
if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title); LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion,
title);
SetError(ERROR_FAILURE); SetError(ERROR_FAILURE);
return PointerWrapSection(*this, -1, title); return PointerWrapSection(*this, -1, title);
} }
return PointerWrapSection(*this, foundVersion, title); return PointerWrapSection(*this, foundVersion, title);
} }
void SetMode(Mode mode_) {mode = mode_;} void SetMode(Mode mode_) {
Mode GetMode() const {return mode;} mode = mode_;
u8 **GetPPtr() {return ptr;} }
void SetError(Error error_) Mode GetMode() const {
{ return mode;
}
u8** GetPPtr() {
return ptr;
}
void SetError(Error error_) {
if (error < error_) if (error < error_)
error = error_; error = error_;
if (error > ERROR_WARNING) if (error > ERROR_WARNING)
mode = PointerWrap::MODE_MEASURE; mode = PointerWrap::MODE_MEASURE;
} }
bool ExpectVoid(void *data, int size) bool ExpectVoid(void* data, int size) {
{
switch (mode) { switch (mode) {
case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, data, size); break; if (memcmp(data, *ptr, size) != 0)
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything return false;
break;
case MODE_WRITE:
memcpy(*ptr, data, size);
break;
case MODE_MEASURE:
break; // MODE_MEASURE - don't need to do anything
case MODE_VERIFY: case MODE_VERIFY:
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], DEBUG_ASSERT_MSG(
((u8*)data)[i] == (*ptr)[i],
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
(*ptr)[i], (*ptr)[i], &(*ptr)[i]); &(*ptr)[i]);
} }
break; break;
default: break; // throw an error? default:
break; // throw an error?
} }
(*ptr) += size; (*ptr) += size;
return true; return true;
} }
void DoVoid(void *data, int size) void DoVoid(void* data, int size) {
{
switch (mode) { switch (mode) {
case MODE_READ: memcpy(data, *ptr, size); break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, data, size); break; memcpy(data, *ptr, size);
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything break;
case MODE_WRITE:
memcpy(*ptr, data, size);
break;
case MODE_MEASURE:
break; // MODE_MEASURE - don't need to do anything
case MODE_VERIFY: case MODE_VERIFY:
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i], DEBUG_ASSERT_MSG(
((u8*)data)[i] == (*ptr)[i],
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i],
(*ptr)[i], (*ptr)[i], &(*ptr)[i]); &(*ptr)[i]);
} }
break; break;
default: break; // throw an error? default:
break; // throw an error?
} }
(*ptr) += size; (*ptr) += size;
} }
template<class K, class T> template <class K, class T>
void Do(std::map<K, T *> &x) void Do(std::map<K, T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (it->second != nullptr) if (it->second != nullptr)
delete it->second; delete it->second;
} }
} }
T *dv = nullptr; T* dv = nullptr;
DoMap(x, dv); DoMap(x, dv);
} }
template<class K, class T> template <class K, class T>
void Do(std::map<K, T> &x) void Do(std::map<K, T>& x) {
{
T dv = T(); T dv = T();
DoMap(x, dv); DoMap(x, dv);
} }
template<class K, class T> template <class K, class T>
void DoMap(std::map<K, T> &x, T &default_val) void DoMap(std::map<K, T>& x, T& default_val) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) { switch (mode) {
case MODE_READ: case MODE_READ: {
{ x.clear();
x.clear(); while (number > 0) {
while (number > 0) K first = K();
{ Do(first);
K first = K(); T second = default_val;
Do(first); Do(second);
T second = default_val; x[first] = second;
Do(second); --number;
x[first] = second;
--number;
}
} }
break; } break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{ typename std::map<K, T>::iterator itr = x.begin();
typename std::map<K, T>::iterator itr = x.begin(); while (number > 0) {
while (number > 0) K first = itr->first;
{ Do(first);
K first = itr->first; Do(itr->second);
Do(first); --number;
Do(itr->second); ++itr;
--number;
++itr;
}
} }
break; } break;
} }
} }
template<class K, class T> template <class K, class T>
void Do(std::multimap<K, T *> &x) void Do(std::multimap<K, T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (it->second != nullptr) if (it->second != nullptr)
delete it->second; delete it->second;
} }
} }
T *dv = nullptr; T* dv = nullptr;
DoMultimap(x, dv); DoMultimap(x, dv);
} }
template<class K, class T> template <class K, class T>
void Do(std::multimap<K, T> &x) void Do(std::multimap<K, T>& x) {
{
T dv = T(); T dv = T();
DoMultimap(x, dv); DoMultimap(x, dv);
} }
template<class K, class T> template <class K, class T>
void DoMultimap(std::multimap<K, T> &x, T &default_val) void DoMultimap(std::multimap<K, T>& x, T& default_val) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) { switch (mode) {
case MODE_READ: case MODE_READ: {
{ x.clear();
x.clear(); while (number > 0) {
while (number > 0) K first = K();
{ Do(first);
K first = K(); T second = default_val;
Do(first); Do(second);
T second = default_val; x.insert(std::make_pair(first, second));
Do(second); --number;
x.insert(std::make_pair(first, second));
--number;
}
} }
break; } break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{ typename std::multimap<K, T>::iterator itr = x.begin();
typename std::multimap<K, T>::iterator itr = x.begin(); while (number > 0) {
while (number > 0) Do(itr->first);
{ Do(itr->second);
Do(itr->first); --number;
Do(itr->second); ++itr;
--number;
++itr;
}
} }
break; } break;
} }
} }
// Store vectors. // Store vectors.
template<class T> template <class T>
void Do(std::vector<T *> &x) void Do(std::vector<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
DoVector(x, dv); DoVector(x, dv);
} }
template<class T> template <class T>
void Do(std::vector<T> &x) void Do(std::vector<T>& x) {
{
T dv = T(); T dv = T();
DoVector(x, dv); DoVector(x, dv);
} }
template <class T>
template<class T> void DoPOD(std::vector<T>& x) {
void DoPOD(std::vector<T> &x)
{
T dv = T(); T dv = T();
DoVectorPOD(x, dv); DoVectorPOD(x, dv);
} }
template<class T> template <class T>
void Do(std::vector<T> &x, T &default_val) void Do(std::vector<T>& x, T& default_val) {
{
DoVector(x, default_val); DoVector(x, default_val);
} }
template<class T> template <class T>
void DoVector(std::vector<T> &x, T &default_val) void DoVector(std::vector<T>& x, T& default_val) {
{
u32 vec_size = (u32)x.size(); u32 vec_size = (u32)x.size();
Do(vec_size); Do(vec_size);
x.resize(vec_size, default_val); x.resize(vec_size, default_val);
@ -372,9 +369,8 @@ public:
DoArray(&x[0], vec_size); DoArray(&x[0], vec_size);
} }
template<class T> template <class T>
void DoVectorPOD(std::vector<T> &x, T &default_val) void DoVectorPOD(std::vector<T>& x, T& default_val) {
{
u32 vec_size = (u32)x.size(); u32 vec_size = (u32)x.size();
Do(vec_size); Do(vec_size);
x.resize(vec_size, default_val); x.resize(vec_size, default_val);
@ -383,55 +379,48 @@ public:
} }
// Store deques. // Store deques.
template<class T> template <class T>
void Do(std::deque<T *> &x) void Do(std::deque<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
DoDeque(x, dv); DoDeque(x, dv);
} }
template<class T> template <class T>
void Do(std::deque<T> &x) void Do(std::deque<T>& x) {
{
T dv = T(); T dv = T();
DoDeque(x, dv); DoDeque(x, dv);
} }
template<class T> template <class T>
void DoDeque(std::deque<T> &x, T &default_val) void DoDeque(std::deque<T>& x, T& default_val) {
{
u32 deq_size = (u32)x.size(); u32 deq_size = (u32)x.size();
Do(deq_size); Do(deq_size);
x.resize(deq_size, default_val); x.resize(deq_size, default_val);
u32 i; u32 i;
for(i = 0; i < deq_size; i++) for (i = 0; i < deq_size; i++)
Do(x[i]); Do(x[i]);
} }
// Store STL lists. // Store STL lists.
template<class T> template <class T>
void Do(std::list<T *> &x) void Do(std::list<T*>& x) {
{ T* dv = nullptr;
T *dv = nullptr;
Do(x, dv); Do(x, dv);
} }
template<class T> template <class T>
void Do(std::list<T> &x) void Do(std::list<T>& x) {
{
T dv = T(); T dv = T();
DoList(x, dv); DoList(x, dv);
} }
template<class T> template <class T>
void Do(std::list<T> &x, T &default_val) void Do(std::list<T>& x, T& default_val) {
{
DoList(x, default_val); DoList(x, default_val);
} }
template<class T> template <class T>
void DoList(std::list<T> &x, T &default_val) void DoList(std::list<T>& x, T& default_val) {
{
u32 list_size = (u32)x.size(); u32 list_size = (u32)x.size();
Do(list_size); Do(list_size);
x.resize(list_size, default_val); x.resize(list_size, default_val);
@ -441,15 +430,11 @@ public:
Do(*itr); Do(*itr);
} }
// Store STL sets. // Store STL sets.
template <class T> template <class T>
void Do(std::set<T *> &x) void Do(std::set<T*>& x) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ) for (auto it = x.begin(), end = x.end(); it != end; ++it) {
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
if (*it != nullptr) if (*it != nullptr)
delete *it; delete *it;
} }
@ -458,39 +443,31 @@ public:
} }
template <class T> template <class T>
void Do(std::set<T> &x) void Do(std::set<T>& x) {
{
DoSet(x); DoSet(x);
} }
template <class T> template <class T>
void DoSet(std::set<T> &x) void DoSet(std::set<T>& x) {
{
unsigned int number = (unsigned int)x.size(); unsigned int number = (unsigned int)x.size();
Do(number); Do(number);
switch (mode) switch (mode) {
{ case MODE_READ: {
case MODE_READ: x.clear();
{ while (number-- > 0) {
x.clear(); T it = T();
while (number-- > 0) Do(it);
{ x.insert(it);
T it = T();
Do(it);
x.insert(it);
}
} }
break; } break;
case MODE_WRITE: case MODE_WRITE:
case MODE_MEASURE: case MODE_MEASURE:
case MODE_VERIFY: case MODE_VERIFY: {
{ typename std::set<T>::iterator itr = x.begin();
typename std::set<T>::iterator itr = x.begin(); while (number-- > 0)
while (number-- > 0) Do(*itr++);
Do(*itr++); } break;
}
break;
default: default:
LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
@ -498,51 +475,58 @@ public:
} }
// Store strings. // Store strings.
void Do(std::string &x) void Do(std::string& x) {
{
int stringLen = (int)x.length() + 1; int stringLen = (int)x.length() + 1;
Do(stringLen); Do(stringLen);
switch (mode) { switch (mode) {
case MODE_READ: x = (char*)*ptr; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; x = (char*)*ptr;
case MODE_MEASURE: break; break;
case MODE_WRITE:
memcpy(*ptr, x.c_str(), stringLen);
break;
case MODE_MEASURE:
break;
case MODE_VERIFY: case MODE_VERIFY:
DEBUG_ASSERT_MSG((x == (char*)*ptr), DEBUG_ASSERT_MSG((x == (char*)*ptr),
"Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
x.c_str(), (char*)*ptr, ptr); x.c_str(), (char*)*ptr, ptr);
break; break;
} }
(*ptr) += stringLen; (*ptr) += stringLen;
} }
void Do(std::wstring &x) void Do(std::wstring& x) {
{ int stringLen = sizeof(wchar_t) * ((int)x.length() + 1);
int stringLen = sizeof(wchar_t)*((int)x.length() + 1);
Do(stringLen); Do(stringLen);
switch (mode) { switch (mode) {
case MODE_READ: x = (wchar_t*)*ptr; break; case MODE_READ:
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; x = (wchar_t*)*ptr;
case MODE_MEASURE: break; break;
case MODE_WRITE:
memcpy(*ptr, x.c_str(), stringLen);
break;
case MODE_MEASURE:
break;
case MODE_VERIFY: case MODE_VERIFY:
DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr), DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr),
"Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
x.c_str(), (wchar_t*)*ptr, ptr); x.c_str(), (wchar_t*)*ptr, ptr);
break; break;
} }
(*ptr) += stringLen; (*ptr) += stringLen;
} }
template<class T> template <class T>
void DoClass(T &x) { void DoClass(T& x) {
x.DoState(*this); x.DoState(*this);
} }
template<class T> template <class T>
void DoClass(T *&x) { void DoClass(T*& x) {
if (mode == MODE_READ) if (mode == MODE_READ) {
{
if (x != nullptr) if (x != nullptr)
delete x; delete x;
x = new T(); x = new T();
@ -550,81 +534,70 @@ public:
x->DoState(*this); x->DoState(*this);
} }
template<class T> template <class T>
void DoArray(T *x, int count) { void DoArray(T* x, int count) {
DoHelper<T>::DoArray(this, x, count); DoHelper<T>::DoArray(this, x, count);
} }
template<class T> template <class T>
void Do(T &x) { void Do(T& x) {
DoHelper<T>::Do(this, x); DoHelper<T>::Do(this, x);
} }
template<class T> template <class T>
void DoPOD(T &x) { void DoPOD(T& x) {
DoHelper<T>::Do(this, x); DoHelper<T>::Do(this, x);
} }
template<class T> template <class T>
void DoPointer(T* &x, T*const base) { void DoPointer(T*& x, T* const base) {
// pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range // pointers can be more than 2^31 apart, but you're using this function wrong if you need
// that much range
s32 offset = x - base; s32 offset = x - base;
Do(offset); Do(offset);
if (mode == MODE_READ) if (mode == MODE_READ)
x = base + offset; x = base + offset;
} }
template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*),
void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) void (*TDo)(PointerWrap&, T*)>
{ void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) {
LinkedListItem<T>* list_cur = list_start; LinkedListItem<T>* list_cur = list_start;
LinkedListItem<T>* prev = nullptr; LinkedListItem<T>* prev = nullptr;
while (true) while (true) {
{
u8 shouldExist = (list_cur ? 1 : 0); u8 shouldExist = (list_cur ? 1 : 0);
Do(shouldExist); Do(shouldExist);
if (shouldExist == 1) if (shouldExist == 1) {
{
LinkedListItem<T>* cur = list_cur ? list_cur : TNew(); LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
TDo(*this, (T*)cur); TDo(*this, (T*)cur);
if (!list_cur) if (!list_cur) {
{ if (mode == MODE_READ) {
if (mode == MODE_READ)
{
cur->next = nullptr; cur->next = nullptr;
list_cur = cur; list_cur = cur;
if (prev) if (prev)
prev->next = cur; prev->next = cur;
else else
list_start = cur; list_start = cur;
} } else {
else
{
TFree(cur); TFree(cur);
continue; continue;
} }
} }
} } else {
else if (mode == MODE_READ) {
{
if (mode == MODE_READ)
{
if (prev) if (prev)
prev->next = nullptr; prev->next = nullptr;
if (list_end) if (list_end)
*list_end = prev; *list_end = prev;
if (list_cur) if (list_cur) {
{
if (list_start == list_cur) if (list_start == list_cur)
list_start = nullptr; list_start = nullptr;
do do {
{
LinkedListItem<T>* next = list_cur->next; LinkedListItem<T>* next = list_cur->next;
TFree(list_cur); TFree(list_cur);
list_cur = next; list_cur = next;
} } while (list_cur);
while (list_cur);
} }
} }
break; break;
@ -634,13 +607,13 @@ public:
} }
} }
void DoMarker(const char* prevName, u32 arbitraryNumber=0x42) void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42) {
{
u32 cookie = arbitraryNumber; u32 cookie = arbitraryNumber;
Do(cookie); Do(cookie);
if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) {
{ LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). "
LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); "Aborting savestate load...",
prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
SetError(ERROR_FAILURE); SetError(ERROR_FAILURE);
} }
} }

View file

@ -14,24 +14,28 @@
// having to prefix them with gen-> or something similar. // having to prefix them with gen-> or something similar.
// Example implementation: // Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {} // class JIT : public CodeBlock<ARMXEmitter> {}
template<class T> class CodeBlock : public T, NonCopyable template <class T>
{ class CodeBlock : public T, NonCopyable {
private: private:
// A privately used function to set the executable RAM space to something invalid. // A privately used function to set the executable RAM space to something invalid.
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction // For debugging usefulness it should be used to set the RAM to a host specific breakpoint
// instruction
virtual void PoisonMemory() = 0; virtual void PoisonMemory() = 0;
protected: protected:
u8 *region; u8* region;
size_t region_size; size_t region_size;
public: public:
CodeBlock() : region(nullptr), region_size(0) {} CodeBlock() : region(nullptr), region_size(0) {
virtual ~CodeBlock() { if (region) FreeCodeSpace(); } }
virtual ~CodeBlock() {
if (region)
FreeCodeSpace();
}
// Call this before you generate any code. // Call this before you generate any code.
void AllocCodeSpace(int size) void AllocCodeSpace(int size) {
{
region_size = size; region_size = size;
region = (u8*)AllocateExecutableMemory(region_size); region = (u8*)AllocateExecutableMemory(region_size);
T::SetCodePtr(region); T::SetCodePtr(region);
@ -39,15 +43,13 @@ public:
// Always clear code space with breakpoints, so that if someone accidentally executes // Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger. // uninitialized, it just breaks into the debugger.
void ClearCodeSpace() void ClearCodeSpace() {
{
PoisonMemory(); PoisonMemory();
ResetCodePtr(); ResetCodePtr();
} }
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job. // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace() void FreeCodeSpace() {
{
#ifdef __SYMBIAN32__ #ifdef __SYMBIAN32__
ResetExecutableMemory(region); ResetExecutableMemory(region);
#else #else
@ -57,33 +59,29 @@ public:
region_size = 0; region_size = 0;
} }
bool IsInSpace(const u8 *ptr) bool IsInSpace(const u8* ptr) {
{
return (ptr >= region) && (ptr < (region + region_size)); return (ptr >= region) && (ptr < (region + region_size));
} }
// Cannot currently be undone. Will write protect the entire code region. // Cannot currently be undone. Will write protect the entire code region.
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
void WriteProtect() void WriteProtect() {
{
WriteProtectMemory(region, region_size, true); WriteProtectMemory(region, region_size, true);
} }
void ResetCodePtr() void ResetCodePtr() {
{
T::SetCodePtr(region); T::SetCodePtr(region);
} }
size_t GetSpaceLeft() const size_t GetSpaceLeft() const {
{
return region_size - (T::GetCodePtr() - region); return region_size - (T::GetCodePtr() - region);
} }
u8 *GetBasePtr() { u8* GetBasePtr() {
return region; return region;
} }
size_t GetOffset(const u8 *ptr) const { size_t GetOffset(const u8* ptr) const {
return ptr - region; return ptr - region;
} }
}; };

View file

@ -56,7 +56,7 @@ constexpr u8 Convert8To6(u8 value) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return { bytes[3], bytes[2], bytes[1], bytes[0] }; return {bytes[3], bytes[2], bytes[1], bytes[0]};
} }
/** /**
@ -65,7 +65,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
return { bytes[2], bytes[1], bytes[0], 255 }; return {bytes[2], bytes[1], bytes[0], 255};
} }
/** /**
@ -74,7 +74,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @return Result color decoded as Math::Vec4<u8> * @return Result color decoded as Math::Vec4<u8>
*/ */
inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
return { bytes[1], bytes[0], 0, 255 }; return {bytes[1], bytes[0], 0, 255};
} }
/** /**
@ -84,8 +84,8 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255 }; Convert5To8(pixel & 0x1F), 255};
} }
/** /**
@ -95,8 +95,8 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1) }; Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
} }
/** /**
@ -106,8 +106,8 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
*/ */
inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return { Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF) }; Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
} }
/** /**
@ -134,7 +134,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @return Resulting values stored as a Math::Vec2 * @return Resulting values stored as a Math::Vec2
*/ */
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
return { static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3] }; return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
} }
/** /**
@ -175,8 +175,8 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color * @param bytes Destination pointer to store encoded color
*/ */
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | *reinterpret_cast<u16_le*>(bytes) =
(Convert8To6(color.g()) << 5) | Convert8To5(color.b()); (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
} }
/** /**
@ -186,7 +186,8 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
*/ */
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
(Convert8To5(color.g()) << 6) | (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); (Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
} }
/** /**
@ -196,7 +197,8 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
*/ */
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) | *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
(Convert8To4(color.g()) << 8) | (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); (Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
} }
/** /**

View file

@ -14,7 +14,7 @@
/// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
#define CONCAT2(x, y) DO_CONCAT2(x, y) #define CONCAT2(x, y) DO_CONCAT2(x, y)
#define DO_CONCAT2(x, y) x ## y #define DO_CONCAT2(x, y) x##y
// helper macro to properly align structure members. // helper macro to properly align structure members.
// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121", // Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
@ -24,9 +24,9 @@
// Inlining // Inlining
#ifdef _WIN32 #ifdef _WIN32
#define FORCE_INLINE __forceinline #define FORCE_INLINE __forceinline
#else #else
#define FORCE_INLINE inline __attribute__((always_inline)) #define FORCE_INLINE inline __attribute__((always_inline))
#endif #endif
#ifndef _MSC_VER #ifndef _MSC_VER
@ -46,7 +46,8 @@
#else #else
inline u32 rotl(u32 x, int shift) { inline u32 rotl(u32 x, int shift) {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return x;
return (x << shift) | (x >> (32 - shift)); return (x << shift) | (x >> (32 - shift));
} }
#endif #endif
@ -56,17 +57,18 @@ inline u32 rotl(u32 x, int shift) {
#else #else
inline u32 rotr(u32 x, int shift) { inline u32 rotr(u32 x, int shift) {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return x;
return (x >> shift) | (x << (32 - shift)); return (x >> shift) | (x << (32 - shift));
} }
#endif #endif
inline u64 _rotl64(u64 x, unsigned int shift){ inline u64 _rotl64(u64 x, unsigned int shift) {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x << n) | (x >> (64 - n)); return (x << n) | (x >> (64 - n));
} }
inline u64 _rotr64(u64 x, unsigned int shift){ inline u64 _rotr64(u64 x, unsigned int shift) {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x >> n) | (x << (64 - n)); return (x >> n) | (x << (64 - n));
} }
@ -74,17 +76,18 @@ inline u64 _rotr64(u64 x, unsigned int shift){
#else // _MSC_VER #else // _MSC_VER
#if (_MSC_VER < 1900) #if (_MSC_VER < 1900)
// Function Cross-Compatibility // Function Cross-Compatibility
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif
// Locale Cross-Compatibility // Locale Cross-Compatibility
#define locale_t _locale_t #define locale_t _locale_t
extern "C" { extern "C" {
__declspec(dllimport) void __stdcall DebugBreak(void); __declspec(dllimport) void __stdcall DebugBreak(void);
} }
#define Crash() {DebugBreak();} #define Crash() \
{ DebugBreak(); }
// cstdlib provides these on MSVC // cstdlib provides these on MSVC
#define rotr _rotr #define rotr _rotr

View file

@ -16,13 +16,13 @@
#define ROOT_DIR "." #define ROOT_DIR "."
#define USERDATA_DIR "user" #define USERDATA_DIR "user"
#ifdef USER_DIR #ifdef USER_DIR
#define EMU_DATA_DIR USER_DIR #define EMU_DATA_DIR USER_DIR
#else #else
#ifdef _WIN32 #ifdef _WIN32
#define EMU_DATA_DIR "Citra Emulator" #define EMU_DATA_DIR "Citra Emulator"
#else #else
#define EMU_DATA_DIR "citra-emu" #define EMU_DATA_DIR "citra-emu"
#endif #endif
#endif #endif
// Dirs in both User and Sys // Dirs in both User and Sys
@ -31,32 +31,32 @@
#define JAP_DIR "JAP" #define JAP_DIR "JAP"
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX) // Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
#define CONFIG_DIR "config" #define CONFIG_DIR "config"
#define GAMECONFIG_DIR "game_config" #define GAMECONFIG_DIR "game_config"
#define MAPS_DIR "maps" #define MAPS_DIR "maps"
#define CACHE_DIR "cache" #define CACHE_DIR "cache"
#define SDMC_DIR "sdmc" #define SDMC_DIR "sdmc"
#define NAND_DIR "nand" #define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata" #define SYSDATA_DIR "sysdata"
#define SHADERCACHE_DIR "shader_cache" #define SHADERCACHE_DIR "shader_cache"
#define STATESAVES_DIR "state_saves" #define STATESAVES_DIR "state_saves"
#define SCREENSHOTS_DIR "screenShots" #define SCREENSHOTS_DIR "screenShots"
#define DUMP_DIR "dump" #define DUMP_DIR "dump"
#define DUMP_TEXTURES_DIR "textures" #define DUMP_TEXTURES_DIR "textures"
#define DUMP_FRAMES_DIR "frames" #define DUMP_FRAMES_DIR "frames"
#define DUMP_AUDIO_DIR "audio" #define DUMP_AUDIO_DIR "audio"
#define LOGS_DIR "logs" #define LOGS_DIR "logs"
#define SHADERS_DIR "shaders" #define SHADERS_DIR "shaders"
#define SYSCONF_DIR "sysconf" #define SYSCONF_DIR "sysconf"
// Filenames // Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX) // Files in the directory returned by GetUserPath(D_CONFIG_IDX)
#define EMU_CONFIG "emu.ini" #define EMU_CONFIG "emu.ini"
#define DEBUGGER_CONFIG "debugger.ini" #define DEBUGGER_CONFIG "debugger.ini"
#define LOGGER_CONFIG "logger.ini" #define LOGGER_CONFIG "logger.ini"
// Sys files // Sys files
#define SHARED_FONT "shared_font.bin" #define SHARED_FONT "shared_font.bin"
// Files in the directory returned by GetUserPath(D_LOGS_IDX) // Files in the directory returned by GetUserPath(D_LOGS_IDX)
#define MAIN_LOG "emu.log" #define MAIN_LOG "emu.log"

View file

@ -32,18 +32,18 @@
#endif #endif
#endif #endif
typedef std::uint8_t u8; ///< 8-bit unsigned byte typedef std::uint8_t u8; ///< 8-bit unsigned byte
typedef std::uint16_t u16; ///< 16-bit unsigned short typedef std::uint16_t u16; ///< 16-bit unsigned short
typedef std::uint32_t u32; ///< 32-bit unsigned word typedef std::uint32_t u32; ///< 32-bit unsigned word
typedef std::uint64_t u64; ///< 64-bit unsigned int typedef std::uint64_t u64; ///< 64-bit unsigned int
typedef std::int8_t s8; ///< 8-bit signed byte typedef std::int8_t s8; ///< 8-bit signed byte
typedef std::int16_t s16; ///< 16-bit signed short typedef std::int16_t s16; ///< 16-bit signed short
typedef std::int32_t s32; ///< 32-bit signed word typedef std::int32_t s32; ///< 32-bit signed word
typedef std::int64_t s64; ///< 64-bit signed int typedef std::int64_t s64; ///< 64-bit signed int
typedef float f32; ///< 32-bit floating point typedef float f32; ///< 32-bit floating point
typedef double f64; ///< 64-bit floating point typedef double f64; ///< 64-bit floating point
// TODO: It would be nice to eventually replace these with strong types that prevent accidental // TODO: It would be nice to eventually replace these with strong types that prevent accidental
// conversion between each other. // conversion between each other.

View file

@ -44,18 +44,17 @@ void EmuWindow::CirclePadUpdated(float x, float y) {
*/ */
static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x, static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x,
unsigned framebuffer_y) { unsigned framebuffer_y) {
return (framebuffer_y >= layout.bottom_screen.top && return (
framebuffer_y < layout.bottom_screen.bottom && framebuffer_y >= layout.bottom_screen.top && framebuffer_y < layout.bottom_screen.bottom &&
framebuffer_x >= layout.bottom_screen.left && framebuffer_x >= layout.bottom_screen.left && framebuffer_x < layout.bottom_screen.right);
framebuffer_x < layout.bottom_screen.right);
} }
std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) { std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) {
new_x = std::max(new_x, framebuffer_layout.bottom_screen.left); new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1); new_x = std::min(new_x, framebuffer_layout.bottom_screen.right - 1);
new_y = std::max(new_y, framebuffer_layout.bottom_screen.top); new_y = std::max(new_y, framebuffer_layout.bottom_screen.top);
new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom-1); new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom - 1);
return std::make_tuple(new_x, new_y); return std::make_tuple(new_x, new_y);
} }
@ -64,10 +63,12 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return; return;
touch_x = VideoCore::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / touch_x = VideoCore::kScreenBottomWidth *
(framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); (framebuffer_x - framebuffer_layout.bottom_screen.left) /
touch_y = VideoCore::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); touch_y = VideoCore::kScreenBottomHeight *
(framebuffer_y - framebuffer_layout.bottom_screen.top) /
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
touch_pressed = true; touch_pressed = true;
pad_state.touch.Assign(1); pad_state.touch.Assign(1);
@ -90,16 +91,19 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
TouchPressed(framebuffer_x, framebuffer_y); TouchPressed(framebuffer_x, framebuffer_y);
} }
EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width,
unsigned height) {
// When hiding the widget, the function receives a size of 0 // When hiding the widget, the function receives a size of 0
if (width == 0) width = 1; if (width == 0)
if (height == 0) height = 1; width = 1;
if (height == 0)
height = 1;
EmuWindow::FramebufferLayout res = { width, height, {}, {} }; EmuWindow::FramebufferLayout res = {width, height, {}, {}};
float window_aspect_ratio = static_cast<float>(height) / width; float window_aspect_ratio = static_cast<float>(height) / width;
float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) / float emulation_aspect_ratio =
VideoCore::kScreenTopWidth; static_cast<float>(VideoCore::kScreenTopHeight * 2) / VideoCore::kScreenTopWidth;
if (window_aspect_ratio > emulation_aspect_ratio) { if (window_aspect_ratio > emulation_aspect_ratio) {
// Window is narrower than the emulation content => apply borders to the top and bottom // Window is narrower than the emulation content => apply borders to the top and bottom
@ -110,8 +114,9 @@ EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(u
res.top_screen.top = (height - viewport_height) / 2; res.top_screen.top = (height - viewport_height) / 2;
res.top_screen.bottom = res.top_screen.top + viewport_height / 2; res.top_screen.bottom = res.top_screen.top + viewport_height / 2;
int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) / int bottom_width = static_cast<int>(
VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left)); (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) *
(res.top_screen.right - res.top_screen.left));
int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
res.bottom_screen.left = bottom_border; res.bottom_screen.left = bottom_border;
@ -127,8 +132,9 @@ EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(u
res.top_screen.top = 0; res.top_screen.top = 0;
res.top_screen.bottom = res.top_screen.top + height / 2; res.top_screen.bottom = res.top_screen.top + height / 2;
int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) / int bottom_width = static_cast<int>(
VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left)); (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) *
(res.top_screen.right - res.top_screen.left));
int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
res.bottom_screen.left = res.top_screen.left + bottom_border; res.bottom_screen.left = res.top_screen.left + bottom_border;

View file

@ -30,15 +30,14 @@
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this. * re-read the upper points again and think about it if you don't see this.
*/ */
class EmuWindow class EmuWindow {
{
public: public:
/// Data structure to store emuwindow configuration /// Data structure to store emuwindow configuration
struct WindowConfig { struct WindowConfig {
bool fullscreen; bool fullscreen;
int res_width; int res_width;
int res_height; int res_height;
std::pair<unsigned,unsigned> min_client_area_size; std::pair<unsigned, unsigned> min_client_area_size;
}; };
/// Describes the layout of the window framebuffer (size and top/bottom screen positions) /// Describes the layout of the window framebuffer (size and top/bottom screen positions)
@ -193,15 +192,18 @@ public:
/** /**
* Returns currently active configuration. * Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in another thread * @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
*/ */
const WindowConfig& GetActiveConfig() const { const WindowConfig& GetActiveConfig() const {
return active_config; return active_config;
} }
/** /**
* Requests the internal configuration to be replaced by the specified argument at some point in the future. * Requests the internal configuration to be replaced by the specified argument at some point in
* @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active. * the future.
* @note This method is thread-safe, because it delays configuration changes to the GUI event
* loop. Hence there is no guarantee on when the requested configuration will be active.
*/ */
void SetConfig(const WindowConfig& val) { void SetConfig(const WindowConfig& val) {
config = val; config = val;
@ -227,7 +229,8 @@ protected:
circle_pad_y = 0; circle_pad_y = 0;
touch_pressed = false; touch_pressed = false;
} }
virtual ~EmuWindow() {} virtual ~EmuWindow() {
}
/** /**
* Processes any pending configuration changes from the last SetConfig call. * Processes any pending configuration changes from the last SetConfig call.
@ -258,7 +261,7 @@ protected:
* Update internal client area size with the given parameter. * Update internal client area size with the given parameter.
* @note EmuWindow implementations will usually use this in window resize event handlers. * @note EmuWindow implementations will usually use this in window resize event handlers.
*/ */
void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) { void NotifyClientAreaSizeChanged(const std::pair<unsigned, unsigned>& size) {
client_area_width = size.first; client_area_width = size.first;
client_area_height = size.second; client_area_height = size.second;
} }
@ -266,32 +269,35 @@ protected:
private: private:
/** /**
* Handler called when the minimal client area was requested to be changed via SetConfig. * Handler called when the minimal client area was requested to be changed via SetConfig.
* For the request to be honored, EmuWindow implementations will usually reimplement this function. * For the request to be honored, EmuWindow implementations will usually reimplement this
* function.
*/ */
virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { virtual void
OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) {
// By default, ignore this request and do nothing. // By default, ignore this request and do nothing.
} }
FramebufferLayout framebuffer_layout; ///< Current framebuffer layout FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
unsigned client_area_width; ///< Current client width, should be set by window impl. unsigned client_area_width; ///< Current client width, should be set by window impl.
unsigned client_area_height; ///< Current client height, should be set by window impl. unsigned client_area_height; ///< Current client height, should be set by window impl.
WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) WindowConfig config; ///< Internal configuration (changes pending for being applied in
WindowConfig active_config; ///< Internal active configuration /// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
/** /**
* Clip the provided coordinates to be inside the touchscreen area. * Clip the provided coordinates to be inside the touchscreen area.
*/ */
std::tuple<unsigned,unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
Service::HID::PadState pad_state; Service::HID::PadState pad_state;
}; };

View file

@ -2,73 +2,70 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/file_util.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <shlobj.h> // for SHGetFolderPath #include <commdlg.h> // for GetSaveFileName
#include <shellapi.h> #include <direct.h> // getcwd
#include <commdlg.h> // for GetSaveFileName #include <io.h>
#include <io.h> #include <shellapi.h>
#include <direct.h> // getcwd #include <shlobj.h> // for SHGetFolderPath
#include <tchar.h> #include <tchar.h>
#include "common/string_util.h" #include "common/string_util.h"
// 64 bit offsets for windows // 64 bit offsets for windows
#define fseeko _fseeki64 #define fseeko _fseeki64
#define ftello _ftelli64 #define ftello _ftelli64
#define atoll _atoi64 #define atoll _atoi64
#define stat64 _stat64 #define stat64 _stat64
#define fstat64 _fstat64 #define fstat64 _fstat64
#define fileno _fileno #define fileno _fileno
#else #else
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/param.h> #include <sys/param.h>
#endif #endif
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <dirent.h> #include <dirent.h>
#include <pwd.h> #include <pwd.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
#include <CoreFoundation/CFString.h> #include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFURL.h> #include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFBundle.h> #include <CoreFoundation/CFURL.h>
#endif #endif
#include <algorithm> #include <algorithm>
#include <sys/stat.h> #include <sys/stat.h>
#ifndef S_ISDIR #ifndef S_ISDIR
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
#endif #endif
#ifdef BSD4_4 #ifdef BSD4_4
#define stat64 stat #define stat64 stat
#define fstat64 fstat #define fstat64 fstat
#endif #endif
// This namespace has various generic functions related to files and paths. // This namespace has various generic functions related to files and paths.
// The code still needs a ton of cleanup. // The code still needs a ton of cleanup.
// REMEMBER: strdup considered harmful! // REMEMBER: strdup considered harmful!
namespace FileUtil namespace FileUtil {
{
// Remove any ending forward slashes from directory paths // Remove any ending forward slashes from directory paths
// Modifies argument. // Modifies argument.
static void StripTailDirSlashes(std::string &fname) static void StripTailDirSlashes(std::string& fname) {
{ if (fname.length() > 1) {
if (fname.length() > 1)
{
size_t i = fname.length(); size_t i = fname.length();
while (i > 0 && fname[i - 1] == DIR_SEP_CHR) while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
--i; --i;
@ -78,8 +75,7 @@ static void StripTailDirSlashes(std::string &fname)
} }
// Returns true if file filename exists // Returns true if file filename exists
bool Exists(const std::string &filename) bool Exists(const std::string& filename) {
{
struct stat64 file_info; struct stat64 file_info;
std::string copy(filename); std::string copy(filename);
@ -99,8 +95,7 @@ bool Exists(const std::string &filename)
} }
// Returns true if filename is a directory // Returns true if filename is a directory
bool IsDirectory(const std::string &filename) bool IsDirectory(const std::string& filename) {
{
struct stat64 file_info; struct stat64 file_info;
std::string copy(filename); std::string copy(filename);
@ -117,8 +112,8 @@ bool IsDirectory(const std::string &filename)
#endif #endif
if (result < 0) { if (result < 0) {
LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", filename.c_str(),
filename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
} }
@ -127,36 +122,32 @@ bool IsDirectory(const std::string &filename)
// Deletes a given filename, return true on success // Deletes a given filename, return true on success
// Doesn't supports deleting a directory // Doesn't supports deleting a directory
bool Delete(const std::string &filename) bool Delete(const std::string& filename) {
{
LOG_INFO(Common_Filesystem, "file %s", filename.c_str()); LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
// Return true because we care about the file no // Return true because we care about the file no
// being there, not the actual delete. // being there, not the actual delete.
if (!Exists(filename)) if (!Exists(filename)) {
{
LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str()); LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
return true; return true;
} }
// We can't delete a directory // We can't delete a directory
if (IsDirectory(filename)) if (IsDirectory(filename)) {
{
LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str()); LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
return false; return false;
} }
#ifdef _WIN32 #ifdef _WIN32
if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) {
{ LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", filename.c_str(),
LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", GetLastErrorMsg());
filename.c_str(), GetLastErrorMsg());
return false; return false;
} }
#else #else
if (unlink(filename.c_str()) == -1) { if (unlink(filename.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s", LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s", filename.c_str(),
filename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
} }
#endif #endif
@ -165,16 +156,15 @@ bool Delete(const std::string &filename)
} }
// Returns true if successful, or path already exists. // Returns true if successful, or path already exists.
bool CreateDir(const std::string &path) bool CreateDir(const std::string& path) {
{
LOG_TRACE(Common_Filesystem, "directory %s", path.c_str()); LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
#ifdef _WIN32 #ifdef _WIN32
if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr))
return true; return true;
DWORD error = GetLastError(); DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) if (error == ERROR_ALREADY_EXISTS) {
{ LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists",
LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str()); path.c_str());
return true; return true;
} }
LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error); LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
@ -185,8 +175,7 @@ bool CreateDir(const std::string &path)
int err = errno; int err = errno;
if (err == EEXIST) if (err == EEXIST) {
{
LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str()); LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
return true; return true;
} }
@ -197,20 +186,17 @@ bool CreateDir(const std::string &path)
} }
// Creates the full path of fullPath returns true on success // Creates the full path of fullPath returns true on success
bool CreateFullPath(const std::string &fullPath) bool CreateFullPath(const std::string& fullPath) {
{
int panicCounter = 100; int panicCounter = 100;
LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str()); LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
if (FileUtil::Exists(fullPath)) if (FileUtil::Exists(fullPath)) {
{
LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str()); LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
return true; return true;
} }
size_t position = 0; size_t position = 0;
while (true) while (true) {
{
// Find next sub path // Find next sub path
position = fullPath.find(DIR_SEP_CHR, position); position = fullPath.find(DIR_SEP_CHR, position);
@ -227,8 +213,7 @@ bool CreateFullPath(const std::string &fullPath)
// A safety check // A safety check
panicCounter--; panicCounter--;
if (panicCounter <= 0) if (panicCounter <= 0) {
{
LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
return false; return false;
} }
@ -236,15 +221,12 @@ bool CreateFullPath(const std::string &fullPath)
} }
} }
// Deletes a directory filename, returns true on success // Deletes a directory filename, returns true on success
bool DeleteDir(const std::string &filename) bool DeleteDir(const std::string& filename) {
{
LOG_INFO(Common_Filesystem, "directory %s", filename.c_str()); LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
// check if a directory // check if a directory
if (!FileUtil::IsDirectory(filename)) if (!FileUtil::IsDirectory(filename)) {
{
LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str()); LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
return false; return false;
} }
@ -262,83 +244,73 @@ bool DeleteDir(const std::string &filename)
} }
// renames file srcFilename to destFilename, returns true on success // renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string &srcFilename, const std::string &destFilename) bool Rename(const std::string& srcFilename, const std::string& destFilename) {
{ LOG_TRACE(Common_Filesystem, "%s --> %s", srcFilename.c_str(), destFilename.c_str());
LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
#ifdef _WIN32 #ifdef _WIN32
if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0) if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(),
Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
return true; return true;
#else #else
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
return true; return true;
#endif #endif
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(),
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
} }
// copies file srcFilename to destFilename, returns true on success // copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string &srcFilename, const std::string &destFilename) bool Copy(const std::string& srcFilename, const std::string& destFilename) {
{ LOG_TRACE(Common_Filesystem, "%s --> %s", srcFilename.c_str(), destFilename.c_str());
LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
#ifdef _WIN32 #ifdef _WIN32
if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(),
Common::UTF8ToUTF16W(destFilename).c_str(), FALSE))
return true; return true;
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(),
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); GetLastErrorMsg());
return false; return false;
#else #else
// buffer size // buffer size
#define BSIZE 1024 #define BSIZE 1024
char buffer[BSIZE]; char buffer[BSIZE];
// Open input file // Open input file
FILE *input = fopen(srcFilename.c_str(), "rb"); FILE* input = fopen(srcFilename.c_str(), "rb");
if (!input) if (!input) {
{ LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s", srcFilename.c_str(),
LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s", destFilename.c_str(), GetLastErrorMsg());
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
return false; return false;
} }
// open output file // open output file
FILE *output = fopen(destFilename.c_str(), "wb"); FILE* output = fopen(destFilename.c_str(), "wb");
if (!output) if (!output) {
{
fclose(input); fclose(input);
LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s", LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s", srcFilename.c_str(),
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); destFilename.c_str(), GetLastErrorMsg());
return false; return false;
} }
// copy loop // copy loop
while (!feof(input)) while (!feof(input)) {
{
// read input // read input
int rnum = fread(buffer, sizeof(char), BSIZE, input); int rnum = fread(buffer, sizeof(char), BSIZE, input);
if (rnum != BSIZE) if (rnum != BSIZE) {
{ if (ferror(input) != 0) {
if (ferror(input) != 0) LOG_ERROR(Common_Filesystem, "failed reading from source, %s --> %s: %s",
{ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
LOG_ERROR(Common_Filesystem,
"failed reading from source, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
goto bail; goto bail;
} }
} }
// write output // write output
int wnum = fwrite(buffer, sizeof(char), rnum, output); int wnum = fwrite(buffer, sizeof(char), rnum, output);
if (wnum != rnum) if (wnum != rnum) {
{ LOG_ERROR(Common_Filesystem, "failed writing to output, %s --> %s: %s",
LOG_ERROR(Common_Filesystem, srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
"failed writing to output, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
goto bail; goto bail;
} }
} }
@ -356,16 +328,13 @@ bail:
} }
// Returns the size of filename (64bit) // Returns the size of filename (64bit)
u64 GetSize(const std::string &filename) u64 GetSize(const std::string& filename) {
{ if (!Exists(filename)) {
if (!Exists(filename))
{
LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str()); LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
return 0; return 0;
} }
if (IsDirectory(filename)) if (IsDirectory(filename)) {
{
LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str()); LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
return 0; return 0;
} }
@ -377,65 +346,54 @@ u64 GetSize(const std::string &filename)
if (stat64(filename.c_str(), &buf) == 0) if (stat64(filename.c_str(), &buf) == 0)
#endif #endif
{ {
LOG_TRACE(Common_Filesystem, "%s: %lld", LOG_TRACE(Common_Filesystem, "%s: %lld", filename.c_str(), (long long)buf.st_size);
filename.c_str(), (long long)buf.st_size);
return buf.st_size; return buf.st_size;
} }
LOG_ERROR(Common_Filesystem, "Stat failed %s: %s", LOG_ERROR(Common_Filesystem, "Stat failed %s: %s", filename.c_str(), GetLastErrorMsg());
filename.c_str(), GetLastErrorMsg());
return 0; return 0;
} }
// Overloaded GetSize, accepts file descriptor // Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd) u64 GetSize(const int fd) {
{
struct stat64 buf; struct stat64 buf;
if (fstat64(fd, &buf) != 0) { if (fstat64(fd, &buf) != 0) {
LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s", LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s", fd, GetLastErrorMsg());
fd, GetLastErrorMsg());
return 0; return 0;
} }
return buf.st_size; return buf.st_size;
} }
// Overloaded GetSize, accepts FILE* // Overloaded GetSize, accepts FILE*
u64 GetSize(FILE *f) u64 GetSize(FILE* f) {
{
// can't use off_t here because it can be 32-bit // can't use off_t here because it can be 32-bit
u64 pos = ftello(f); u64 pos = ftello(f);
if (fseeko(f, 0, SEEK_END) != 0) { if (fseeko(f, 0, SEEK_END) != 0) {
LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", f, GetLastErrorMsg());
f, GetLastErrorMsg());
return 0; return 0;
} }
u64 size = ftello(f); u64 size = ftello(f);
if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", f, GetLastErrorMsg());
f, GetLastErrorMsg());
return 0; return 0;
} }
return size; return size;
} }
// creates an empty file filename, returns true on success // creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string &filename) bool CreateEmptyFile(const std::string& filename) {
{
LOG_TRACE(Common_Filesystem, "%s", filename.c_str()); LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
if (!FileUtil::IOFile(filename, "wb")) if (!FileUtil::IOFile(filename, "wb")) {
{ LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
LOG_ERROR(Common_Filesystem, "failed %s: %s",
filename.c_str(), GetLastErrorMsg());
return false; return false;
} }
return true; return true;
} }
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback) DirectoryEntryCallable callback) {
{
LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str()); LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
// How many files + directories we found // How many files + directories we found
@ -457,7 +415,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
do { do {
const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName)); const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName));
#else #else
DIR *dirp = opendir(directory.c_str()); DIR* dirp = opendir(directory.c_str());
if (!dirp) if (!dirp)
return false; return false;
@ -493,8 +451,8 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
return true; return true;
} }
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion) unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
{ unsigned int recursion) {
const auto callback = [recursion, &parent_entry](unsigned* num_entries_out, const auto callback = [recursion, &parent_entry](unsigned* num_entries_out,
const std::string& directory, const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
@ -526,11 +484,8 @@ unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry,
return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0; return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0;
} }
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
bool DeleteDirRecursively(const std::string &directory, unsigned int recursion) const auto callback = [recursion](unsigned* num_entries_out, const std::string& directory,
{
const auto callback = [recursion](unsigned* num_entries_out,
const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name; std::string new_path = directory + DIR_SEP_CHR + virtual_name;
@ -551,53 +506,53 @@ bool DeleteDirRecursively(const std::string &directory, unsigned int recursion)
} }
// Create directory and copy contents (does not overwrite existing files) // Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string &source_path, const std::string &dest_path) void CopyDir(const std::string& source_path, const std::string& dest_path) {
{
#ifndef _WIN32 #ifndef _WIN32
if (source_path == dest_path) return; if (source_path == dest_path)
if (!FileUtil::Exists(source_path)) return; return;
if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path); if (!FileUtil::Exists(source_path))
return;
if (!FileUtil::Exists(dest_path))
FileUtil::CreateFullPath(dest_path);
DIR *dirp = opendir(source_path.c_str()); DIR* dirp = opendir(source_path.c_str());
if (!dirp) return; if (!dirp)
return;
while (struct dirent* result = readdir(dirp)) { while (struct dirent* result = readdir(dirp)) {
const std::string virtualName(result->d_name); const std::string virtualName(result->d_name);
// check for "." and ".." // check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
((virtualName[0] == '.') && (virtualName[1] == '.') && ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0')))
(virtualName[2] == '\0')))
continue; continue;
std::string source, dest; std::string source, dest;
source = source_path + virtualName; source = source_path + virtualName;
dest = dest_path + virtualName; dest = dest_path + virtualName;
if (IsDirectory(source)) if (IsDirectory(source)) {
{
source += '/'; source += '/';
dest += '/'; dest += '/';
if (!FileUtil::Exists(dest)) FileUtil::CreateFullPath(dest); if (!FileUtil::Exists(dest))
FileUtil::CreateFullPath(dest);
CopyDir(source, dest); CopyDir(source, dest);
} } else if (!FileUtil::Exists(dest))
else if (!FileUtil::Exists(dest)) FileUtil::Copy(source, dest); FileUtil::Copy(source, dest);
} }
closedir(dirp); closedir(dirp);
#endif #endif
} }
// Returns the current directory // Returns the current directory
std::string GetCurrentDir() std::string GetCurrentDir() {
{ // Get the current working directory (getcwd uses malloc)
// Get the current working directory (getcwd uses malloc)
#ifdef _WIN32 #ifdef _WIN32
wchar_t *dir; wchar_t* dir;
if (!(dir = _wgetcwd(nullptr, 0))) { if (!(dir = _wgetcwd(nullptr, 0))) {
#else #else
char *dir; char* dir;
if (!(dir = getcwd(nullptr, 0))) { if (!(dir = getcwd(nullptr, 0))) {
#endif #endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", GetLastErrorMsg());
GetLastErrorMsg());
return nullptr; return nullptr;
} }
#ifdef _WIN32 #ifdef _WIN32
@ -610,8 +565,7 @@ std::string GetCurrentDir()
} }
// Sets the current directory to the given directory // Sets the current directory to the given directory
bool SetCurrentDir(const std::string &directory) bool SetCurrentDir(const std::string& directory) {
{
#ifdef _WIN32 #ifdef _WIN32
return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
#else #else
@ -620,8 +574,7 @@ bool SetCurrentDir(const std::string &directory)
} }
#if defined(__APPLE__) #if defined(__APPLE__)
std::string GetBundleDirectory() std::string GetBundleDirectory() {
{
CFURLRef BundleRef; CFURLRef BundleRef;
char AppBundlePath[MAXPATHLEN]; char AppBundlePath[MAXPATHLEN];
// Get the main bundle for the app // Get the main bundle for the app
@ -636,11 +589,9 @@ std::string GetBundleDirectory()
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
std::string& GetExeDirectory() std::string& GetExeDirectory() {
{
static std::string exe_path; static std::string exe_path;
if (exe_path.empty()) if (exe_path.empty()) {
{
wchar_t wchar_exe_path[2048]; wchar_t wchar_exe_path[2048];
GetModuleFileNameW(nullptr, wchar_exe_path, 2048); GetModuleFileNameW(nullptr, wchar_exe_path, 2048);
exe_path = Common::UTF16ToUTF8(wchar_exe_path); exe_path = Common::UTF16ToUTF8(wchar_exe_path);
@ -660,7 +611,8 @@ static const std::string& GetHomeDirectory() {
home_path = envvar; home_path = envvar;
} else { } else {
auto pw = getpwuid(getuid()); auto pw = getpwuid(getuid());
ASSERT_MSG(pw, "$HOME isnt defined, and the current user cant be found in /etc/passwd."); ASSERT_MSG(pw,
"$HOME isnt defined, and the current user cant be found in /etc/passwd.");
home_path = pw->pw_dir; home_path = pw->pw_dir;
} }
} }
@ -699,11 +651,10 @@ static const std::string GetUserDirectory(const std::string& envvar) {
} }
#endif #endif
std::string GetSysDirectory() std::string GetSysDirectory() {
{
std::string sysDir; std::string sysDir;
#if defined (__APPLE__) #if defined(__APPLE__)
sysDir = GetBundleDirectory(); sysDir = GetBundleDirectory();
sysDir += DIR_SEP; sysDir += DIR_SEP;
sysDir += SYSDATA_DIR; sysDir += SYSDATA_DIR;
@ -718,123 +669,114 @@ std::string GetSysDirectory()
// Returns a string with a Citra data dir or file in the user's home // Returns a string with a Citra data dir or file in the user's home
// directory. To be used in "multi-user" mode (that is, installed). // directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath) const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) {
{
static std::string paths[NUM_PATH_INDICES]; static std::string paths[NUM_PATH_INDICES];
// Set up all paths and files on the first run // Set up all paths and files on the first run
if (paths[D_USER_IDX].empty()) if (paths[D_USER_IDX].empty()) {
{
#ifdef _WIN32 #ifdef _WIN32
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
#else #else
if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
} else { } else {
std::string data_dir = GetUserDirectory("XDG_DATA_HOME"); std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME"); std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME"); std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
} }
#endif #endif
paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
} }
if (!newPath.empty()) if (!newPath.empty()) {
{ if (!FileUtil::IsDirectory(newPath)) {
if (!FileUtil::IsDirectory(newPath))
{
LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str()); LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
return paths[DirIDX]; return paths[DirIDX];
} } else {
else
{
paths[DirIDX] = newPath; paths[DirIDX] = newPath;
} }
switch (DirIDX) switch (DirIDX) {
{
case D_ROOT_IDX: case D_ROOT_IDX:
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP; paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP;
paths[F_SYSCONF_IDX] = paths[D_SYSCONF_IDX] + SYSCONF; paths[F_SYSCONF_IDX] = paths[D_SYSCONF_IDX] + SYSCONF;
break; break;
case D_USER_IDX: case D_USER_IDX:
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR DIR_SEP; paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR DIR_SEP;
paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG; paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG;
paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
break; break;
case D_CONFIG_IDX: case D_CONFIG_IDX:
paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG; paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG;
paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
break; break;
case D_DUMP_IDX: case D_DUMP_IDX:
paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
break; break;
case D_LOGS_IDX: case D_LOGS_IDX:
paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
} }
} }
return paths[DirIDX]; return paths[DirIDX];
} }
size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename) size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
{
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
} }
size_t ReadFileToString(bool text_file, const char *filename, std::string &str) size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
{
IOFile file(filename, text_file ? "r" : "rb"); IOFile file(filename, text_file ? "r" : "rb");
if (!file) if (!file)
@ -886,42 +828,36 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
} }
} }
IOFile::IOFile() IOFile::IOFile() {
{
} }
IOFile::IOFile(const std::string& filename, const char openmode[]) IOFile::IOFile(const std::string& filename, const char openmode[]) {
{
Open(filename, openmode); Open(filename, openmode);
} }
IOFile::~IOFile() IOFile::~IOFile() {
{
Close(); Close();
} }
IOFile::IOFile(IOFile&& other) IOFile::IOFile(IOFile&& other) {
{
Swap(other); Swap(other);
} }
IOFile& IOFile::operator=(IOFile&& other) IOFile& IOFile::operator=(IOFile&& other) {
{
Swap(other); Swap(other);
return *this; return *this;
} }
void IOFile::Swap(IOFile& other) void IOFile::Swap(IOFile& other) {
{
std::swap(m_file, other.m_file); std::swap(m_file, other.m_file);
std::swap(m_good, other.m_good); std::swap(m_good, other.m_good);
} }
bool IOFile::Open(const std::string& filename, const char openmode[]) bool IOFile::Open(const std::string& filename, const char openmode[]) {
{
Close(); Close();
#ifdef _WIN32 #ifdef _WIN32
_wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), Common::UTF8ToUTF16W(openmode).c_str()); _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
Common::UTF8ToUTF16W(openmode).c_str());
#else #else
m_file = fopen(filename.c_str(), openmode); m_file = fopen(filename.c_str(), openmode);
#endif #endif
@ -930,8 +866,7 @@ bool IOFile::Open(const std::string& filename, const char openmode[])
return m_good; return m_good;
} }
bool IOFile::Close() bool IOFile::Close() {
{
if (!IsOpen() || 0 != std::fclose(m_file)) if (!IsOpen() || 0 != std::fclose(m_file))
m_good = false; m_good = false;
@ -939,50 +874,46 @@ bool IOFile::Close()
return m_good; return m_good;
} }
u64 IOFile::GetSize() const u64 IOFile::GetSize() const {
{
if (IsOpen()) if (IsOpen())
return FileUtil::GetSize(m_file); return FileUtil::GetSize(m_file);
return 0; return 0;
} }
bool IOFile::Seek(s64 off, int origin) bool IOFile::Seek(s64 off, int origin) {
{
if (!IsOpen() || 0 != fseeko(m_file, off, origin)) if (!IsOpen() || 0 != fseeko(m_file, off, origin))
m_good = false; m_good = false;
return m_good; return m_good;
} }
u64 IOFile::Tell() const u64 IOFile::Tell() const {
{
if (IsOpen()) if (IsOpen())
return ftello(m_file); return ftello(m_file);
return -1; return -1;
} }
bool IOFile::Flush() bool IOFile::Flush() {
{
if (!IsOpen() || 0 != std::fflush(m_file)) if (!IsOpen() || 0 != std::fflush(m_file))
m_good = false; m_good = false;
return m_good; return m_good;
} }
bool IOFile::Resize(u64 size) bool IOFile::Resize(u64 size) {
{ if (!IsOpen() ||
if (!IsOpen() || 0 != 0 !=
#ifdef _WIN32 #ifdef _WIN32
// ector: _chsize sucks, not 64-bit safe // ector: _chsize sucks, not 64-bit safe
// F|RES: changed to _chsize_s. i think it is 64-bit safe // F|RES: changed to _chsize_s. i think it is 64-bit safe
_chsize_s(_fileno(m_file), size) _chsize_s(_fileno(m_file), size)
#else #else
// TODO: handle 64bit and growing // TODO: handle 64bit and growing
ftruncate(fileno(m_file), size) ftruncate(fileno(m_file), size)
#endif #endif
) )
m_good = false; m_good = false;
return m_good; return m_good;

View file

@ -5,9 +5,9 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstdio>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <cstdio>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
@ -51,75 +51,75 @@ enum {
NUM_PATH_INDICES NUM_PATH_INDICES
}; };
namespace FileUtil namespace FileUtil {
{
// FileSystem tree node/ // FileSystem tree node/
struct FSTEntry struct FSTEntry {
{
bool isDirectory; bool isDirectory;
u64 size; // file length or number of entries from children u64 size; // file length or number of entries from children
std::string physicalName; // name on disk std::string physicalName; // name on disk
std::string virtualName; // name in FST names table std::string virtualName; // name in FST names table
std::vector<FSTEntry> children; std::vector<FSTEntry> children;
}; };
// Returns true if file filename exists // Returns true if file filename exists
bool Exists(const std::string &filename); bool Exists(const std::string& filename);
// Returns true if filename is a directory // Returns true if filename is a directory
bool IsDirectory(const std::string &filename); bool IsDirectory(const std::string& filename);
// Returns the size of filename (64bit) // Returns the size of filename (64bit)
u64 GetSize(const std::string &filename); u64 GetSize(const std::string& filename);
// Overloaded GetSize, accepts file descriptor // Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd); u64 GetSize(const int fd);
// Overloaded GetSize, accepts FILE* // Overloaded GetSize, accepts FILE*
u64 GetSize(FILE *f); u64 GetSize(FILE* f);
// Returns true if successful, or path already exists. // Returns true if successful, or path already exists.
bool CreateDir(const std::string &filename); bool CreateDir(const std::string& filename);
// Creates the full path of fullPath returns true on success // Creates the full path of fullPath returns true on success
bool CreateFullPath(const std::string &fullPath); bool CreateFullPath(const std::string& fullPath);
// Deletes a given filename, return true on success // Deletes a given filename, return true on success
// Doesn't supports deleting a directory // Doesn't supports deleting a directory
bool Delete(const std::string &filename); bool Delete(const std::string& filename);
// Deletes a directory filename, returns true on success // Deletes a directory filename, returns true on success
bool DeleteDir(const std::string &filename); bool DeleteDir(const std::string& filename);
// renames file srcFilename to destFilename, returns true on success // renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string &srcFilename, const std::string &destFilename); bool Rename(const std::string& srcFilename, const std::string& destFilename);
// copies file srcFilename to destFilename, returns true on success // copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string &srcFilename, const std::string &destFilename); bool Copy(const std::string& srcFilename, const std::string& destFilename);
// creates an empty file filename, returns true on success // creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string &filename); bool CreateEmptyFile(const std::string& filename);
/** /**
* @param num_entries_out to be assigned by the callable with the number of iterated directory entries, never null * @param num_entries_out to be assigned by the callable with the number of iterated directory
* entries, never null
* @param directory the path to the enclosing directory * @param directory the path to the enclosing directory
* @param virtual_name the entry name, without any preceding directory info * @param virtual_name the entry name, without any preceding directory info
* @return whether handling the entry succeeded * @return whether handling the entry succeeded
*/ */
using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out, using DirectoryEntryCallable = std::function<bool(
const std::string& directory, unsigned* num_entries_out, const std::string& directory, const std::string& virtual_name)>;
const std::string& virtual_name)>;
/** /**
* Scans a directory, calling the callback for each file/directory contained within. * Scans a directory, calling the callback for each file/directory contained within.
* If the callback returns failure, scanning halts and this function returns failure as well * If the callback returns failure, scanning halts and this function returns failure as well
* @param num_entries_out assigned by the function with the number of iterated directory entries, can be null * @param num_entries_out assigned by the function with the number of iterated directory entries,
* can be null
* @param directory the directory to scan * @param directory the directory to scan
* @param callback The callback which will be called for each entry * @param callback The callback which will be called for each entry
* @return whether scanning the directory succeeded * @return whether scanning the directory succeeded
*/ */
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback); bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
DirectoryEntryCallable callback);
/** /**
* Scans the directory tree, storing the results. * Scans the directory tree, storing the results.
@ -128,23 +128,24 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
* @param recursion Number of children directories to read before giving up. * @param recursion Number of children directories to read before giving up.
* @return the total number of files/directories found * @return the total number of files/directories found
*/ */
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion = 0); unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
unsigned int recursion = 0);
// deletes the given directory and anything under it. Returns true on success. // deletes the given directory and anything under it. Returns true on success.
bool DeleteDirRecursively(const std::string &directory, unsigned int recursion = 256); bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
// Returns the current directory // Returns the current directory
std::string GetCurrentDir(); std::string GetCurrentDir();
// Create directory and copy contents (does not overwrite existing files) // Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string &source_path, const std::string &dest_path); void CopyDir(const std::string& source_path, const std::string& dest_path);
// Set the current directory to given directory // Set the current directory to given directory
bool SetCurrentDir(const std::string &directory); bool SetCurrentDir(const std::string& directory);
// Returns a pointer to a string with a Citra data dir in the user's home // Returns a pointer to a string with a Citra data dir in the user's home
// directory. To be used in "multi-user" mode (that is, installed). // directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath=""); const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath = "");
// Returns the path to where the sys file are // Returns the path to where the sys file are
std::string GetSysDirectory(); std::string GetSysDirectory();
@ -154,11 +155,11 @@ std::string GetBundleDirectory();
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
std::string &GetExeDirectory(); std::string& GetExeDirectory();
#endif #endif
size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename); size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
size_t ReadFileToString(bool text_file, const char *filename, std::string &str); size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
/** /**
* Splits the filename into 8.3 format * Splits the filename into 8.3 format
@ -173,8 +174,7 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
// simple wrapper for cstdlib file functions to // simple wrapper for cstdlib file functions to
// hopefully will make error checking easier // hopefully will make error checking easier
// and make forgetting an fclose() harder // and make forgetting an fclose() harder
class IOFile : public NonCopyable class IOFile : public NonCopyable {
{
public: public:
IOFile(); IOFile();
IOFile(const std::string& filename, const char openmode[]); IOFile(const std::string& filename, const char openmode[]);
@ -190,11 +190,12 @@ public:
bool Close(); bool Close();
template <typename T> template <typename T>
size_t ReadArray(T* data, size_t length) size_t ReadArray(T* data, size_t length) {
{ static_assert(std::is_standard_layout<T>(),
static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects"); "Given array does not consist of standard layout objects");
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects"); static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
#endif #endif
if (!IsOpen()) { if (!IsOpen()) {
@ -210,11 +211,12 @@ public:
} }
template <typename T> template <typename T>
size_t WriteArray(const T* data, size_t length) size_t WriteArray(const T* data, size_t length) {
{ static_assert(std::is_standard_layout<T>(),
static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects"); "Given array does not consist of standard layout objects");
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects"); static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
#endif #endif
if (!IsOpen()) { if (!IsOpen()) {
@ -229,27 +231,31 @@ public:
return items_written; return items_written;
} }
size_t ReadBytes(void* data, size_t length) size_t ReadBytes(void* data, size_t length) {
{
return ReadArray(reinterpret_cast<char*>(data), length); return ReadArray(reinterpret_cast<char*>(data), length);
} }
size_t WriteBytes(const void* data, size_t length) size_t WriteBytes(const void* data, size_t length) {
{
return WriteArray(reinterpret_cast<const char*>(data), length); return WriteArray(reinterpret_cast<const char*>(data), length);
} }
template<typename T> template <typename T>
size_t WriteObject(const T& object) { size_t WriteObject(const T& object) {
static_assert(!std::is_pointer<T>::value, "Given object is a pointer"); static_assert(!std::is_pointer<T>::value, "Given object is a pointer");
return WriteArray(&object, 1); return WriteArray(&object, 1);
} }
bool IsOpen() const { return nullptr != m_file; } bool IsOpen() const {
return nullptr != m_file;
}
// m_good is set to false when a read, write or other function fails // m_good is set to false when a read, write or other function fails
bool IsGood() const { return m_good; } bool IsGood() const {
explicit operator bool() const { return IsGood(); } return m_good;
}
explicit operator bool() const {
return IsGood();
}
bool Seek(s64 off, int origin); bool Seek(s64 off, int origin);
u64 Tell() const; u64 Tell() const;
@ -258,19 +264,21 @@ public:
bool Flush(); bool Flush();
// clear error state // clear error state
void Clear() { m_good = true; std::clearerr(m_file); } void Clear() {
m_good = true;
std::clearerr(m_file);
}
private: private:
std::FILE* m_file = nullptr; std::FILE* m_file = nullptr;
bool m_good = true; bool m_good = true;
}; };
} // namespace } // namespace
// To deal with Windows being dumb at unicode: // To deal with Windows being dumb at unicode:
template <typename T> template <typename T>
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
{
#ifdef _MSC_VER #ifdef _MSC_VER
fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode); fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
#else #else

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