summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis-deps.sh2
-rwxr-xr-x.travis-upload.sh3
-rw-r--r--externals/cmake-modules/FindSDL2.cmake8
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/codec.cpp122
-rw-r--r--src/audio_core/codec.h50
-rw-r--r--src/common/emu_window.h52
-rw-r--r--src/common/vector_math.h5
-rw-r--r--src/core/arm/skyeye_common/armstate.cpp7
-rw-r--r--src/core/file_sys/archive_backend.h29
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp48
-rw-r--r--src/core/file_sys/archive_extsavedata.h13
-rw-r--r--src/core/file_sys/archive_romfs.cpp8
-rw-r--r--src/core/file_sys/archive_romfs.h3
-rw-r--r--src/core/file_sys/archive_savedata.cpp36
-rw-r--r--src/core/file_sys/archive_savedata.h4
-rw-r--r--src/core/file_sys/archive_savedatacheck.cpp8
-rw-r--r--src/core/file_sys/archive_savedatacheck.h3
-rw-r--r--src/core/file_sys/archive_sdmc.cpp7
-rw-r--r--src/core/file_sys/archive_sdmc.h3
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp8
-rw-r--r--src/core/file_sys/archive_systemsavedata.h3
-rw-r--r--src/core/file_sys/disk_archive.cpp80
-rw-r--r--src/core/file_sys/disk_archive.h12
-rw-r--r--src/core/file_sys/file_backend.h13
-rw-r--r--src/core/file_sys/ivfc_archive.cpp21
-rw-r--r--src/core/file_sys/ivfc_archive.h12
-rw-r--r--src/core/gdbstub/gdbstub.h2
-rw-r--r--src/core/hle/kernel/session.h12
-rw-r--r--src/core/hle/result.h6
-rw-r--r--src/core/hle/service/cfg/cfg.cpp5
-rw-r--r--src/core/hle/service/fs/archive.cpp90
-rw-r--r--src/core/hle/service/fs/archive.h17
-rw-r--r--src/core/hle/service/fs/fs_user.cpp139
-rw-r--r--src/core/hle/service/hid/hid.cpp110
-rw-r--r--src/core/hle/service/hid/hid.h78
-rw-r--r--src/core/hle/service/hid/hid_spvr.cpp20
-rw-r--r--src/core/hle/service/hid/hid_user.cpp20
-rw-r--r--src/core/hle/service/ptm/ptm.cpp2
-rw-r--r--src/core/hle/service/soc_u.cpp13
-rw-r--r--src/core/loader/loader.h2
-rw-r--r--src/video_core/command_processor.cpp69
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp24
-rw-r--r--src/video_core/pica.cpp15
-rw-r--r--src/video_core/pica.h15
-rw-r--r--src/video_core/pica_state.h15
-rw-r--r--src/video_core/primitive_assembly.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/shader/shader.cpp18
-rw-r--r--src/video_core/shader/shader_interpreter.cpp10
50 files changed, 984 insertions, 265 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh
index bab90d307..eb99ead4f 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -20,6 +20,6 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
)
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update > /dev/null # silence the very verbose output
- brew install qt5 sdl2
+ brew install qt5 sdl2 dylibbundler
gem install xcpretty
fi
diff --git a/.travis-upload.sh b/.travis-upload.sh
index e508386dd..d86775cb9 100755
--- a/.travis-upload.sh
+++ b/.travis-upload.sh
@@ -20,6 +20,9 @@ if [ "$TRAVIS_BRANCH" = "master" ]; then
# move qt libs into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/citra-qt.app"
+
+ # move SDL2 libs into folder for deployment
+ dylibbundler -b -x "${REV_NAME}/citra" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/"
fi
ARCHIVE_NAME="${REV_NAME}.tar.xz"
diff --git a/externals/cmake-modules/FindSDL2.cmake b/externals/cmake-modules/FindSDL2.cmake
index 0af86840a..9b8daa0d1 100644
--- a/externals/cmake-modules/FindSDL2.cmake
+++ b/externals/cmake-modules/FindSDL2.cmake
@@ -134,11 +134,17 @@ SET(SDL2_SEARCH_PATHS
${SDL2_PATH}
)
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(VC_LIB_PATH_SUFFIX lib/x64)
+else()
+ set(VC_LIB_PATH_SUFFIX lib/x86)
+endif()
+
FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2
HINTS
$ENV{SDL2DIR}
- PATH_SUFFIXES lib64 lib
+ PATH_SUFFIXES lib64 lib ${VC_LIB_PATH_SUFFIX}
PATHS ${SDL2_SEARCH_PATHS}
)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index b0d1c7eb6..c4bad8cb0 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -1,11 +1,13 @@
set(SRCS
audio_core.cpp
+ codec.cpp
hle/dsp.cpp
hle/pipe.cpp
)
set(HEADERS
audio_core.h
+ codec.h
hle/dsp.h
hle/pipe.h
sink.h
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
new file mode 100644
index 000000000..ab65514b7
--- /dev/null
+++ b/src/audio_core/codec.cpp
@@ -0,0 +1,122 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstddef>
+#include <cstring>
+#include <vector>
+
+#include "audio_core/codec.h"
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/math_util.h"
+
+namespace Codec {
+
+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.
+ // Frames are 8 bytes long containing 14 samples each.
+ // Samples are 4 bits (one nibble) long.
+
+ constexpr size_t FRAME_LEN = 8;
+ 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 }};
+
+ const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
+ StereoBuffer16 ret(ret_size);
+
+ int yn1 = state.yn1,
+ yn2 = state.yn2;
+
+ 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++) {
+ const int frame_header = data[framei * FRAME_LEN];
+ const int scale = 1 << (frame_header & 0xF);
+ const int idx = (frame_header >> 4) & 0x7;
+
+ // Coefficients are fixed point with 11 bits fractional part.
+ const int coef1 = adpcm_coeff[idx * 2 + 0];
+ const int coef2 = adpcm_coeff[idx * 2 + 1];
+
+ // Decodes an audio sample. One nibble produces one sample.
+ const auto decode_sample = [&](const int nibble) -> s16 {
+ const int xn = nibble * scale;
+ // 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.
+ // 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;
+ // Clamp to output range.
+ val = MathUtil::Clamp(val, -32768, 32767);
+ // Advance output feedback.
+ yn2 = yn1;
+ yn1 = val;
+ return (s16)val;
+ };
+
+ size_t outputi = framei * SAMPLES_PER_FRAME;
+ size_t datai = framei * FRAME_LEN + 1;
+ for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
+ const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
+ ret[outputi].fill(sample1);
+ outputi++;
+
+ const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
+ ret[outputi].fill(sample2);
+ outputi++;
+
+ datai++;
+ }
+ }
+
+ state.yn1 = yn1;
+ state.yn2 = yn2;
+
+ return ret;
+}
+
+static s16 SignExtendS8(u8 x) {
+ // The data is actually signed PCM8.
+ // We sign extend this to signed PCM16.
+ return static_cast<s16>(static_cast<s8>(x));
+}
+
+StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) {
+ ASSERT(num_channels == 1 || num_channels == 2);
+
+ StereoBuffer16 ret(sample_count);
+
+ if (num_channels == 1) {
+ for (size_t i = 0; i < sample_count; i++) {
+ ret[i].fill(SignExtendS8(data[i]));
+ }
+ } else {
+ for (size_t i = 0; i < sample_count; i++) {
+ ret[i][0] = SignExtendS8(data[i * 2 + 0]);
+ ret[i][1] = SignExtendS8(data[i * 2 + 1]);
+ }
+ }
+
+ return ret;
+}
+
+StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) {
+ ASSERT(num_channels == 1 || num_channels == 2);
+
+ StereoBuffer16 ret(sample_count);
+
+ if (num_channels == 1) {
+ for (size_t i = 0; i < sample_count; i++) {
+ s16 sample;
+ std::memcpy(&sample, data + i * sizeof(s16), sizeof(s16));
+ ret[i].fill(sample);
+ }
+ } else {
+ std::memcpy(ret.data(), data, sample_count * 2 * sizeof(u16));
+ }
+
+ return ret;
+}
+
+};
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
new file mode 100644
index 000000000..e695f2edc
--- /dev/null
+++ b/src/audio_core/codec.h
@@ -0,0 +1,50 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace Codec {
+
+/// A variable length buffer of signed PCM16 stereo samples.
+using StereoBuffer16 = std::vector<std::array<s16, 2>>;
+
+/// See: Codec::DecodeADPCM
+struct ADPCMState {
+ // Two historical samples from previous processed buffer,
+ // required for ADPCM decoding
+ s16 yn1; ///< y[n-1]
+ s16 yn2; ///< y[n-2]
+};
+
+/**
+ * @param data Pointer to buffer that contains ADPCM data to decode
+ * @param sample_count Length of buffer in terms of number of samples
+ * @param adpcm_coeff ADPCM coefficients
+ * @param state ADPCM state, this is updated with new state
+ * @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);
+
+/**
+ * @param num_channels Number of channels
+ * @param data Pointer to buffer that contains PCM8 data to decode
+ * @param sample_count Length of buffer in terms of number of samples
+ * @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);
+
+/**
+ * @param num_channels Number of channels
+ * @param data Pointer to buffer that contains PCM16 data to decode
+ * @param sample_count Length of buffer in terms of number of samples
+ * @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);
+
+};
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index a0ae4c9fa..7c3486dea 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -105,7 +105,7 @@ public:
* @todo Fix this function to be thread-safe.
* @return PadState object indicating the current pad state
*/
- const Service::HID::PadState GetPadState() const {
+ Service::HID::PadState GetPadState() const {
return pad_state;
}
@@ -116,11 +116,59 @@ public:
* @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
* `pressed` is true if the touch screen is currently being pressed
*/
- const std::tuple<u16, u16, bool> GetTouchState() const {
+ std::tuple<u16, u16, bool> GetTouchState() const {
return std::make_tuple(touch_x, touch_y, touch_pressed);
}
/**
+ * Gets the current accelerometer state (acceleration along each three axis).
+ * Axis explained:
+ * +x is the same direction as LEFT on D-pad.
+ * +y is normal to the touch screen, pointing outward.
+ * +z is the same direction as UP on D-pad.
+ * Units:
+ * 1 unit of return value = 1/512 g (measured by hw test),
+ * where g is the gravitational acceleration (9.8 m/sec2).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Implement accelerometer input in front-end.
+ * @return std::tuple of (x, y, z)
+ */
+ std::tuple<s16, s16, s16> GetAccelerometerState() const {
+ // stubbed
+ return std::make_tuple(0, -512, 0);
+ }
+
+ /**
+ * Gets the current gyroscope state (angular rates about each three axis).
+ * Axis explained:
+ * +x is the same direction as LEFT on D-pad.
+ * +y is normal to the touch screen, pointing outward.
+ * +z is the same direction as UP on D-pad.
+ * Orientation is determined by right-hand rule.
+ * Units:
+ * 1 unit of return value = (1/coef) deg/sec,
+ * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Implement gyroscope input in front-end.
+ * @return std::tuple of (x, y, z)
+ */
+ std::tuple<s16, s16, s16> GetGyroscopeState() const {
+ // stubbed
+ return std::make_tuple(0, 0, 0);
+ }
+
+ /**
+ * Gets the coefficient for units conversion of gyroscope state.
+ * The conversion formula is r = coefficient * v,
+ * where v is angular rate in deg/sec,
+ * and r is the gyroscope state.
+ * @return float-type coefficient
+ */
+ f32 GetGyroscopeRawToDpsCoefficient() const {
+ return 14.375f; // taken from hw test, and gyroscope's document
+ }
+
+ /**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in another thread
*/
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 02688e35e..cfb9481b6 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -447,7 +447,10 @@ public:
void SetZero()
{
- x=0; y=0; z=0;
+ x = 0;
+ y = 0;
+ z = 0;
+ w = 0;
}
// Common alias: RGBA (colors)
diff --git a/src/core/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp
index 2d814345a..5550c112e 100644
--- a/src/core/arm/skyeye_common/armstate.cpp
+++ b/src/core/arm/skyeye_common/armstate.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include "common/swap.h"
#include "common/logging/log.h"
#include "core/memory.h"
@@ -48,8 +49,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode)
Spsr[UNDEFBANK] = Spsr_copy;
break;
case FIQ32MODE:
- Reg_firq[0] = Reg[13];
- Reg_firq[1] = Reg[14];
+ std::copy(Reg.begin() + 8, Reg.end() - 1, Reg_firq.begin());
Spsr[FIQBANK] = Spsr_copy;
break;
}
@@ -85,8 +85,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode)
Bank = UNDEFBANK;
break;
case FIQ32MODE:
- Reg[13] = Reg_firq[0];
- Reg[14] = Reg_firq[1];
+ std::copy(Reg_firq.begin(), Reg_firq.end(), Reg.begin() + 8);
Spsr_copy = Spsr[FIQBANK];
Bank = FIQBANK;
break;
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index 601e95d8c..5d91e47f3 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -11,6 +11,7 @@
#include "common/bit_field.h"
#include "common/common_types.h"
+#include "common/swap.h"
#include "core/hle/result.h"
@@ -62,6 +63,14 @@ private:
std::u16string u16str;
};
+struct ArchiveFormatInfo {
+ u32_le total_size; ///< The pre-defined size of the archive, as specified in the Create or Format call
+ u32_le number_directories; ///< The pre-defined number of directories in the archive, as specified in the Create or Format call
+ u32_le number_files; ///< The pre-defined number of files in the archive, as specified in the Create or Format call
+ u8 duplicate_data; ///< Whether the archive should duplicate the data, as specified in the Create or Format call
+};
+static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
+
class ArchiveBackend : NonCopyable {
public:
virtual ~ArchiveBackend() {
@@ -76,16 +85,16 @@ public:
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
- * @return Opened file, or nullptr
+ * @return Opened file, or error code
*/
- virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0;
+ virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const = 0;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
- * @return Whether the file could be deleted
+ * @return Result of the operation
*/
- virtual bool DeleteFile(const Path& path) const = 0;
+ virtual ResultCode DeleteFile(const Path& path) const = 0;
/**
* Rename a File specified by its path
@@ -108,7 +117,7 @@ public:
* @param size The size of the new file, filled with zeroes
* @return File creation result code
*/
- virtual ResultCode CreateFile(const Path& path, u32 size) const = 0;
+ virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
/**
* Create a directory specified by its path
@@ -159,9 +168,17 @@ public:
/**
* Deletes the archive contents and then re-creates the base folder
* @param path Path to the archive
+ * @param format_info Format information for the new archive
* @return ResultCode of the operation, 0 on success
*/
- virtual ResultCode Format(const Path& path) = 0;
+ virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0;
+
+ /**
+ * Retrieves the format info about the archive with the specified path
+ * @param path Path to the archive
+ * @return Format information about the archive or error code
+ */
+ virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0;
};
} // namespace FileSys
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index 92dad8e6f..961264fe5 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -58,7 +58,7 @@ Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
}
ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared)
- : mount_point(GetExtDataContainerPath(mount_location, shared)) {
+ : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) {
LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
}
@@ -74,21 +74,59 @@ bool ArchiveFactory_ExtSaveData::Initialize() {
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) {
std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/";
if (!FileUtil::Exists(fullpath)) {
- // TODO(Subv): Check error code, this one is probably wrong
- return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
- ErrorSummary::InvalidState, ErrorLevel::Status);
+ // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
+ // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
+ if (!shared) {
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
+ } else {
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
}
auto archive = Common::make_unique<DiskArchive>(fullpath);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path) {
+ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
// These folders are always created with the ExtSaveData
std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/";
std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/";
FileUtil::CreateFullPath(user_path);
FileUtil::CreateFullPath(boss_path);
+
+ // Write the format metadata
+ std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
+ FileUtil::IOFile file(metadata_path, "wb");
+
+ if (!file.IsOpen()) {
+ // TODO(Subv): Find the correct error code
+ return ResultCode(-1);
+ }
+
+ file.WriteBytes(&format_info, sizeof(format_info));
return RESULT_SUCCESS;
}
+ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path) const {
+ std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
+ FileUtil::IOFile file(metadata_path, "rb");
+
+ if (!file.IsOpen()) {
+ LOG_ERROR(Service_FS, "Could not open metadata information for archive");
+ // TODO(Subv): Verify error code
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
+
+ ArchiveFormatInfo info = {};
+ file.ReadBytes(&info, sizeof(info));
+ return MakeResult<ArchiveFormatInfo>(info);
+}
+
+void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data, size_t icon_size) {
+ std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path);
+ FileUtil::IOFile icon_file(game_path + "icon", "wb");
+ icon_file.WriteBytes(icon_data, icon_size);
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h
index ec8d770fc..e9a72850d 100644
--- a/src/core/file_sys/archive_extsavedata.h
+++ b/src/core/file_sys/archive_extsavedata.h
@@ -31,11 +31,22 @@ public:
std::string GetName() const override { return "ExtSaveData"; }
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
- ResultCode Format(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
const std::string& GetMountPoint() const { return mount_point; }
+ /**
+ * Writes the SMDH icon of the ExtSaveData to file
+ * @param path Path of this ExtSaveData
+ * @param icon_data Binary data of the icon
+ * @param icon_size Size of the icon data
+ */
+ void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size);
+
private:
+ bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive
+
/**
* This holds the full directory path for this archive, it is only set after a successful call
* to Open, this is formed as <base extsavedatapath>/<type>/<high>/<low>.
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
index 696b51a94..a9a29ebde 100644
--- a/src/core/file_sys/archive_romfs.cpp
+++ b/src/core/file_sys/archive_romfs.cpp
@@ -29,11 +29,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_RomFS::Format(const Path& path) {
+ResultCode ArchiveFactory_RomFS::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
LOG_ERROR(Service_FS, "Attempted to format a RomFS archive.");
// TODO: Verify error code
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS,
ErrorSummary::NotSupported, ErrorLevel::Permanent);
}
+ResultVal<ArchiveFormatInfo> ArchiveFactory_RomFS::GetFormatInfo(const Path& path) const {
+ // TODO(Subv): Implement
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+ return ResultCode(-1);
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
index 2bedfa9c6..c5a329122 100644
--- a/src/core/file_sys/archive_romfs.h
+++ b/src/core/file_sys/archive_romfs.h
@@ -26,7 +26,8 @@ public:
std::string GetName() const override { return "RomFS"; }
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
- ResultCode Format(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::shared_ptr<FileUtil::IOFile> romfs_file;
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
index 12876899f..fe020d21c 100644
--- a/src/core/file_sys/archive_savedata.cpp
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -26,11 +26,17 @@ static std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
}
static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
- u32 high = program_id >> 32;
- u32 low = program_id & 0xFFFFFFFF;
+ u32 high = (u32)(program_id >> 32);
+ u32 low = (u32)(program_id & 0xFFFFFFFF);
return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low);
}
+static std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) {
+ u32 high = (u32)(program_id >> 32);
+ u32 low = (u32)(program_id & 0xFFFFFFFF);
+ return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(), high, low);
+}
+
ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory)
: mount_point(GetSaveDataContainerPath(sdmc_directory)) {
LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
@@ -51,11 +57,35 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_SaveData::Format(const Path& path) {
+ResultCode ArchiveFactory_SaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id);
FileUtil::DeleteDirRecursively(concrete_mount_point);
FileUtil::CreateFullPath(concrete_mount_point);
+
+ // Write the format metadata
+ std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
+ FileUtil::IOFile file(metadata_path, "wb");
+
+ if (file.IsOpen()) {
+ file.WriteBytes(&format_info, sizeof(format_info));
+ return RESULT_SUCCESS;
+ }
return RESULT_SUCCESS;
}
+ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
+ std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
+ FileUtil::IOFile file(metadata_path, "rb");
+
+ if (!file.IsOpen()) {
+ LOG_ERROR(Service_FS, "Could not open metadata information for archive");
+ // TODO(Subv): Verify error code
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
+
+ ArchiveFormatInfo info = {};
+ file.ReadBytes(&info, sizeof(info));
+ return MakeResult<ArchiveFormatInfo>(info);
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h
index 1f65297dd..7a5a24089 100644
--- a/src/core/file_sys/archive_savedata.h
+++ b/src/core/file_sys/archive_savedata.h
@@ -23,7 +23,9 @@ public:
std::string GetName() const override { return "SaveData"; }
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
- ResultCode Format(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string mount_point;
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp
index ea1dfe2c7..3db11c500 100644
--- a/src/core/file_sys/archive_savedatacheck.cpp
+++ b/src/core/file_sys/archive_savedatacheck.cpp
@@ -48,11 +48,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path) {
+ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive.");
// TODO: Verify error code
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS,
ErrorSummary::NotSupported, ErrorLevel::Permanent);
}
+ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const {
+ // TODO(Subv): Implement
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+ return ResultCode(-1);
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_savedatacheck.h
index b14aefe8b..ea2624d64 100644
--- a/src/core/file_sys/archive_savedatacheck.h
+++ b/src/core/file_sys/archive_savedatacheck.h
@@ -23,7 +23,8 @@ public:
std::string GetName() const override { return "SaveDataCheck"; }
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
- ResultCode Format(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string mount_point;
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 5c825f429..657221cbf 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -40,9 +40,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path&
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_SDMC::Format(const Path& path) {
+ResultCode ArchiveFactory_SDMC::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
// This is kind of an undesirable operation, so let's just ignore it. :)
return RESULT_SUCCESS;
}
+ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path) const {
+ // TODO(Subv): Implement
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+ return ResultCode(-1);
+}
} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 10b273bdb..35c0f3725 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -29,7 +29,8 @@ public:
std::string GetName() const override { return "SDMC"; }
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
- ResultCode Format(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string sdmc_directory;
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 896f89529..e1780de2f 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -63,11 +63,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path) {
+ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
std::string fullpath = GetSystemSaveDataPath(base_path, path);
FileUtil::DeleteDirRecursively(fullpath);
FileUtil::CreateFullPath(fullpath);
return RESULT_SUCCESS;
}
+ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path) const {
+ // TODO(Subv): Implement
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+ return ResultCode(-1);
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h
index afc689848..2bc13d4ee 100644
--- a/src/core/file_sys/archive_systemsavedata.h
+++ b/src/core/file_sys/archive_systemsavedata.h
@@ -23,7 +23,8 @@ public:
ArchiveFactory_SystemSaveData(const std::string& mount_point);
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
- ResultCode Format(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
std::string GetName() const override { return "SystemSaveData"; }
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index a51416774..8e4ea01c5 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -17,16 +17,28 @@
namespace FileSys {
-std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
+ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
auto file = Common::make_unique<DiskFile>(*this, path, mode);
- if (!file->Open())
- return nullptr;
- return std::move(file);
+ ResultCode result = file->Open();
+ if (result.IsError())
+ return result;
+ return MakeResult<std::unique_ptr<FileBackend>>(std::move(file));
}
-bool DiskArchive::DeleteFile(const Path& path) const {
- return FileUtil::Delete(mount_point + path.AsString());
+ResultCode DiskArchive::DeleteFile(const Path& path) const {
+ std::string file_path = mount_point + path.AsString();
+
+ if (FileUtil::IsDirectory(file_path))
+ return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
+
+ if (!FileUtil::Exists(file_path))
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
+
+ if (FileUtil::Delete(file_path))
+ return RESULT_SUCCESS;
+
+ return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
}
bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
@@ -37,11 +49,14 @@ bool DiskArchive::DeleteDirectory(const Path& path) const {
return FileUtil::DeleteDir(mount_point + path.AsString());
}
-ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const {
+ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const {
std::string full_path = mount_point + path.AsString();
+ if (FileUtil::IsDirectory(full_path))
+ return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
+
if (FileUtil::Exists(full_path))
- return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info);
+ return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Status);
if (size == 0) {
FileUtil::CreateEmptyFile(full_path);
@@ -89,38 +104,57 @@ DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode
this->mode.hex = mode.hex;
}
-bool DiskFile::Open() {
- if (!mode.create_flag && !FileUtil::Exists(path)) {
- LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str());
- return false;
+ResultCode DiskFile::Open() {
+ if (FileUtil::IsDirectory(path))
+ return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
+
+ // Specifying only the Create flag is invalid
+ if (mode.create_flag && !mode.read_flag && !mode.write_flag) {
+ return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
+ }
+
+ if (!FileUtil::Exists(path)) {
+ if (!mode.create_flag) {
+ LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str());
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
+ } else {
+ // Create the file
+ FileUtil::CreateEmptyFile(path);
+ }
}
- std::string mode_string;
- if (mode.create_flag)
- mode_string = "w+";
- else if (mode.write_flag)
- mode_string = "r+"; // Files opened with Write access can be read from
+ std::string mode_string = "";
+ if (mode.write_flag)
+ mode_string += "r+"; // Files opened with Write access can be read from
else if (mode.read_flag)
- mode_string = "r";
+ mode_string += "r";
// Open the file in binary mode, to avoid problems with CR/LF on Windows systems
mode_string += "b";
file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
- return file->IsOpen();
+ if (file->IsOpen())
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
}
-size_t DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
+ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
+ if (!mode.read_flag && !mode.write_flag)
+ return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
+
file->Seek(offset, SEEK_SET);
- return file->ReadBytes(buffer, length);
+ return MakeResult<size_t>(file->ReadBytes(buffer, length));
}
-size_t DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
+ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
+ if (!mode.write_flag)
+ return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
+
file->Seek(offset, SEEK_SET);
size_t written = file->WriteBytes(buffer, length);
if (flush)
file->Flush();
- return written;
+ return MakeResult<size_t>(written);
}
u64 DiskFile::GetSize() const {
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index ef9a98057..b4cc2f702 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -33,11 +33,11 @@ public:
virtual std::string GetName() const override { return "DiskArchive: " + mount_point; }
- std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
- bool DeleteFile(const Path& path) const override;
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override;
+ ResultCode DeleteFile(const Path& path) const override;
bool RenameFile(const Path& src_path, const Path& dest_path) const override;
bool DeleteDirectory(const Path& path) const override;
- ResultCode CreateFile(const Path& path, u32 size) const override;
+ ResultCode CreateFile(const Path& path, u64 size) const override;
bool CreateDirectory(const Path& path) const override;
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
@@ -54,9 +54,9 @@ class DiskFile : public FileBackend {
public:
DiskFile(const DiskArchive& archive, const Path& path, const Mode mode);
- bool Open() override;
- size_t Read(u64 offset, size_t length, u8* buffer) const override;
- size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
+ ResultCode Open() override;
+ ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
+ ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() const override;
diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h
index df7165df3..9137bbbad 100644
--- a/src/core/file_sys/file_backend.h
+++ b/src/core/file_sys/file_backend.h
@@ -7,6 +7,7 @@
#include <cstddef>
#include "common/common_types.h"
+#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
@@ -20,18 +21,18 @@ public:
/**
* Open the file
- * @return true if the file opened correctly
+ * @return Result of the file operation
*/
- virtual bool Open() = 0;
+ virtual ResultCode Open() = 0;
/**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
* @param buffer Buffer to read data into
- * @return Number of bytes read
+ * @return Number of bytes read, or error code
*/
- virtual size_t Read(u64 offset, size_t length, u8* buffer) const = 0;
+ virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0;
/**
* Write data to the file
@@ -39,9 +40,9 @@ public:
* @param length Length in bytes of data to write to file
* @param flush The flush parameters (0 == do not flush)
* @param buffer Buffer to read data from
- * @return Number of bytes written
+ * @return Number of bytes written, or error code
*/
- virtual size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0;
+ virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0;
/**
* Get the size of the file in bytes
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp
index 2efc31a8c..a8e9a72ef 100644
--- a/src/core/file_sys/ivfc_archive.cpp
+++ b/src/core/file_sys/ivfc_archive.cpp
@@ -20,13 +20,15 @@ std::string IVFCArchive::GetName() const {
return "IVFC";
}
-std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const {
- return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size);
+ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const {
+ return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
}
-bool IVFCArchive::DeleteFile(const Path& path) const {
+ResultCode IVFCArchive::DeleteFile(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", GetName().c_str());
- return false;
+ // TODO(Subv): Verify error code
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS,
+ ErrorSummary::Canceled, ErrorLevel::Status);
}
bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
@@ -39,7 +41,7 @@ bool IVFCArchive::DeleteDirectory(const Path& path) const {
return false;
}
-ResultCode IVFCArchive::CreateFile(const Path& path, u32 size) const {
+ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", GetName().c_str());
// TODO: Verify error code
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent);
@@ -66,17 +68,18 @@ u64 IVFCArchive::GetFreeBytes() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
-size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
+ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
romfs_file->Seek(data_offset + offset, SEEK_SET);
size_t read_length = (size_t)std::min((u64)length, data_size - offset);
- return romfs_file->ReadBytes(buffer, read_length);
+ return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
}
-size_t IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
+ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
- return 0;
+ // TODO(Subv): Find error code
+ return MakeResult<size_t>(0);
}
u64 IVFCFile::GetSize() const {
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h
index f3fd82de4..19d32dcca 100644
--- a/src/core/file_sys/ivfc_archive.h
+++ b/src/core/file_sys/ivfc_archive.h
@@ -34,11 +34,11 @@ public:
std::string GetName() const override;
- std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
- bool DeleteFile(const Path& path) const override;
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override;
+ ResultCode DeleteFile(const Path& path) const override;
bool RenameFile(const Path& src_path, const Path& dest_path) const override;
bool DeleteDirectory(const Path& path) const override;
- ResultCode CreateFile(const Path& path, u32 size) const override;
+ ResultCode CreateFile(const Path& path, u64 size) const override;
bool CreateDirectory(const Path& path) const override;
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
@@ -55,9 +55,9 @@ public:
IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
- bool Open() override { return true; }
- size_t Read(u64 offset, size_t length, u8* buffer) const override;
- size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
+ ResultCode Open() override { return RESULT_SUCCESS; }
+ ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
+ ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() const override { return false; }
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
index aff705a32..4f21da23b 100644
--- a/src/core/gdbstub/gdbstub.h
+++ b/src/core/gdbstub/gdbstub.h
@@ -7,6 +7,8 @@
#pragma once
#include <atomic>
+#include "common/common_types.h"
+
namespace GDBStub {
/// Breakpoint Method
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
index adaffcafe..6ddaf970e 100644
--- a/src/core/hle/kernel/session.h
+++ b/src/core/hle/kernel/session.h
@@ -16,23 +16,23 @@
namespace IPC {
-inline u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) {
+constexpr u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) {
return ((u32)command_id << 16) | (((u32)regular_params & 0x3F) << 6) | (((u32)translate_params & 0x3F) << 0);
}
-inline u32 MoveHandleDesc(unsigned int num_handles = 1) {
+constexpr u32 MoveHandleDesc(unsigned int num_handles = 1) {
return 0x0 | ((num_handles - 1) << 26);
}
-inline u32 CopyHandleDesc(unsigned int num_handles = 1) {
+constexpr u32 CopyHandleDesc(unsigned int num_handles = 1) {
return 0x10 | ((num_handles - 1) << 26);
}
-inline u32 CallingPidDesc() {
+constexpr u32 CallingPidDesc() {
return 0x20;
}
-inline u32 StaticBufferDesc(u32 size, unsigned int buffer_id) {
+constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) {
return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10);
}
@@ -42,7 +42,7 @@ enum MappedBufferPermissions {
RW = R | W,
};
-inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
+constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
return 0x8 | (size << 4) | (u32)perms;
}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 69613fbbb..0cb76ba1c 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -19,8 +19,12 @@
enum class ErrorDescription : u32 {
Success = 0,
WrongAddress = 53,
- FS_NotFound = 100,
+ FS_NotFound = 120,
+ FS_AlreadyExists = 190,
+ FS_InvalidOpenFlags = 230,
+ FS_NotAFile = 250,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
+ FS_InvalidPath = 702,
InvalidSection = 1000,
TooLarge = 1001,
NotAuthorized = 1002,
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 4c82a58e4..525432957 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -310,7 +310,8 @@ ResultCode UpdateConfigNANDSavegame() {
ResultCode FormatConfig() {
ResultCode res = DeleteConfigNANDSaveFile();
- if (!res.IsSuccess())
+ // The delete command fails if the file doesn't exist, so we have to check that too
+ if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound)
return res;
// Delete the old data
cfg_config_file_buffer.fill(0);
@@ -407,7 +408,7 @@ void Init() {
// If the archive didn't exist, create the files inside
if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
// Format the archive to create the directories
- Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
+ Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, FileSys::ArchiveFormatInfo(), archive_path);
// Open it again to get a valid archive now that the folder exists
archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index d64b3656a..590697e76 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -103,7 +103,18 @@ ResultVal<bool> File::SyncRequest() {
u32 address = cmd_buff[5];
LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
GetTypeName().c_str(), GetName().c_str(), offset, length, address);
- cmd_buff[2] = static_cast<u32>(backend->Read(offset, length, Memory::GetPointer(address)));
+
+ if (offset + length > backend->GetSize()) {
+ LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX",
+ offset, length, backend->GetSize());
+ }
+
+ ResultVal<size_t> read = backend->Read(offset, length, Memory::GetPointer(address));
+ if (read.Failed()) {
+ cmd_buff[1] = read.Code().raw;
+ return read.Code();
+ }
+ cmd_buff[2] = static_cast<u32>(*read);
break;
}
@@ -116,7 +127,13 @@ ResultVal<bool> File::SyncRequest() {
u32 address = cmd_buff[6];
LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
- cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush != 0, Memory::GetPointer(address)));
+
+ ResultVal<size_t> written = backend->Write(offset, length, flush != 0, Memory::GetPointer(address));
+ if (written.Failed()) {
+ cmd_buff[1] = written.Code().raw;
+ return written.Code();
+ }
+ cmd_buff[2] = static_cast<u32>(*written);
break;
}
@@ -294,13 +311,11 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
if (archive == nullptr)
return ERR_INVALID_HANDLE;
- std::unique_ptr<FileSys::FileBackend> backend = archive->OpenFile(path, mode);
- if (backend == nullptr) {
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
- ErrorSummary::NotFound, ErrorLevel::Status);
- }
+ auto backend = archive->OpenFile(path, mode);
+ if (backend.Failed())
+ return backend.Code();
- auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path));
+ auto file = Kernel::SharedPtr<File>(new File(backend.MoveFrom(), path));
return MakeResult<Kernel::SharedPtr<File>>(std::move(file));
}
@@ -309,10 +324,7 @@ ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Pa
if (archive == nullptr)
return ERR_INVALID_HANDLE;
- if (archive->DeleteFile(path))
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return archive->DeleteFile(path);
}
ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
@@ -347,7 +359,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
ErrorSummary::Canceled, ErrorLevel::Status);
}
-ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) {
+ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
return ERR_INVALID_HANDLE;
@@ -395,7 +407,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
if (backend == nullptr) {
- return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
ErrorSummary::NotFound, ErrorLevel::Permanent);
}
@@ -410,49 +422,45 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
return MakeResult<u64>(archive->GetFreeBytes());
}
-ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) {
+ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path) {
auto archive_itr = id_code_map.find(id_code);
if (archive_itr == id_code_map.end()) {
return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
}
- return archive_itr->second->Format(path);
+ return archive_itr->second->Format(path, format_info);
+}
+
+ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path) {
+ auto archive = id_code_map.find(id_code);
+ if (archive == id_code_map.end()) {
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
+ }
+
+ return archive->second->GetFormatInfo(archive_path);
}
-ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size) {
+ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info) {
// Construct the binary path to the archive first
FileSys::Path path = FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low);
- std::string media_type_directory;
- if (media_type == MediaType::NAND) {
- media_type_directory = FileUtil::GetUserPath(D_NAND_IDX);
- } else if (media_type == MediaType::SDMC) {
- media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX);
- } else {
- LOG_ERROR(Service_FS, "Unsupported media type %u", media_type);
- return ResultCode(-1); // TODO(Subv): Find the right error code
+ auto archive = id_code_map.find(media_type == MediaType::NAND ? ArchiveIdCode::SharedExtSaveData : ArchiveIdCode::ExtSaveData);
+
+ if (archive == id_code_map.end()) {
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
}
- std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND);
- std::string game_path = FileSys::GetExtSaveDataPath(base_path, path);
- // These two folders are always created with the ExtSaveData
- std::string user_path = game_path + "user/";
- std::string boss_path = game_path + "boss/";
- if (!FileUtil::CreateFullPath(user_path))
- return ResultCode(-1); // TODO(Subv): Find the right error code
- if (!FileUtil::CreateFullPath(boss_path))
- return ResultCode(-1); // TODO(Subv): Find the right error code
+ auto ext_savedata = static_cast<FileSys::ArchiveFactory_ExtSaveData*>(archive->second.get());
+
+ ResultCode result = ext_savedata->Format(path, format_info);
+ if (result.IsError())
+ return result;
u8* smdh_icon = Memory::GetPointer(icon_buffer);
if (!smdh_icon)
return ResultCode(-1); // TODO(Subv): Find the right error code
- // Create the icon
- FileUtil::IOFile icon_file(game_path + "icon", "wb+");
- if (!icon_file.IsGood())
- return ResultCode(-1); // TODO(Subv): Find the right error code
-
- icon_file.WriteBytes(smdh_icon, icon_size);
+ ext_savedata->WriteIcon(path, smdh_icon, icon_size);
return RESULT_SUCCESS;
}
@@ -473,7 +481,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) {
// Delete all directories (/user, /boss) and the icon file.
std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND);
std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path);
- if (!FileUtil::DeleteDirRecursively(extsavedata_path))
+ if (FileUtil::Exists(extsavedata_path) && !FileUtil::DeleteDirRecursively(extsavedata_path))
return ResultCode(-1); // TODO(Subv): Find the right error code
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 952deb4d4..006606740 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -136,7 +136,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
* @param file_size The size of the new file, filled with zeroes
* @return File creation result code
*/
-ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size);
+ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size);
/**
* Create a Directory from an Archive
@@ -177,10 +177,20 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle);
* Erases the contents of the physical folder that contains the archive
* identified by the specified id code and path
* @param id_code The id of the archive to format
+ * @param format_info Format information about the new archive
* @param path The path to the archive, if relevant.
* @return ResultCode 0 on success or the corresponding code on error
*/
-ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = FileSys::Path());
+ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path = FileSys::Path());
+
+/**
+ * Retrieves the format info about the archive of the specified type and path.
+ * The format info is supplied by the client code when creating archives.
+ * @param id_code The id of the archive
+ * @param archive_path The path of the archive, if relevant
+ * @return The format info of the archive, or the corresponding error code if failed.
+ */
+ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path);
/**
* Creates a blank SharedExtSaveData archive for the specified extdata ID
@@ -189,9 +199,10 @@ ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = File
* @param low The low word of the extdata id to create
* @param icon_buffer VAddr of the SMDH icon for this ExtSaveData
* @param icon_size Size of the SMDH icon
+ * @param format_info Format information about the new archive
* @return ResultCode 0 on success or the corresponding code on error
*/
-ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size);
+ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info);
/**
* Deletes the SharedExtSaveData archive for the specified extdata ID
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 632620a56..3ec7ceb30 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -234,7 +234,7 @@ static void DeleteDirectory(Service::Interface* self) {
* 3 : Archive handle upper word
* 4 : File path string type
* 5 : File path string size
- * 7 : File size (filled with zeroes)
+ * 7-8 : File size
* 10: File path string data
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
@@ -245,12 +245,12 @@ static void CreateFile(Service::Interface* self) {
ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
u32 filename_size = cmd_buff[5];
- u32 file_size = cmd_buff[7];
+ u64 file_size = ((u64)cmd_buff[8] << 32) | cmd_buff[7];
u32 filename_ptr = cmd_buff[10];
FileSys::Path file_path(filename_type, filename_size, filename_ptr);
- LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str());
+ LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str());
cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw;
}
@@ -443,17 +443,22 @@ static void IsSdmcWriteable(Service::Interface* self) {
* Inputs:
* 0 : 0x084C0242
* 1 : Archive ID
- * 2 : Archive low path type
- * 3 : Archive low path size
- * 10 : (LowPathSize << 14) | 2
+ * 2 : Archive path type
+ * 3 : Archive path size
+ * 4 : Size in Blocks (1 block = 512 bytes)
+ * 5 : Number of directories
+ * 6 : Number of files
+ * 7 : Directory bucket count
+ * 8 : File bucket count
+ * 9 : Duplicate data
+ * 10 : (PathSize << 14) | 2
* 11 : Archive low path
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void FormatSaveData(Service::Interface* self) {
- // TODO(Subv): Find out what the other inputs and outputs of this function are
u32* cmd_buff = Kernel::GetCommandBuffer();
- LOG_DEBUG(Service_FS, "(STUBBED)");
+ LOG_WARNING(Service_FS, "(STUBBED)");
auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
@@ -464,9 +469,9 @@ static void FormatSaveData(Service::Interface* self) {
LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
if (archive_id != FS::ArchiveIdCode::SaveData) {
- // TODO(Subv): What should happen if somebody attempts to format a different archive?
- LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]);
- cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
+ LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", archive_id);
+ cmd_buff[1] = ResultCode(ErrorDescription::FS_InvalidPath, ErrorModule::FS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw;
return;
}
@@ -477,23 +482,40 @@ static void FormatSaveData(Service::Interface* self) {
return;
}
- cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw;
+ FileSys::ArchiveFormatInfo format_info;
+ format_info.duplicate_data = cmd_buff[9] & 0xFF;
+ format_info.number_directories = cmd_buff[5];
+ format_info.number_files = cmd_buff[6];
+ format_info.total_size = cmd_buff[4] * 512;
+
+ cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw;
}
/**
* FS_User::FormatThisUserSaveData service function
* Inputs:
* 0: 0x080F0180
+ * 1 : Size in Blocks (1 block = 512 bytes)
+ * 2 : Number of directories
+ * 3 : Number of files
+ * 4 : Directory bucket count
+ * 5 : File bucket count
+ * 6 : Duplicate data
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void FormatThisUserSaveData(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- LOG_DEBUG(Service_FS, "(STUBBED)");
- // TODO(Subv): Find out what the inputs and outputs of this function are
+ FileSys::ArchiveFormatInfo format_info;
+ format_info.duplicate_data = cmd_buff[6] & 0xFF;
+ format_info.number_directories = cmd_buff[2];
+ format_info.number_files = cmd_buff[3];
+ format_info.total_size = cmd_buff[1] * 512;
+
+ cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw;
- cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw;
+ LOG_TRACE(Service_FS, "called");
}
/**
@@ -531,10 +553,9 @@ static void GetFreeBytes(Service::Interface* self) {
* 2 : Low word of the saveid to create
* 3 : High word of the saveid to create
* 4 : Unknown
- * 5 : Unknown
- * 6 : Unknown
- * 7 : Unknown
- * 8 : Unknown
+ * 5 : Number of directories
+ * 6 : Number of files
+ * 7-8 : Size limit
* 9 : Size of the SMDH icon
* 10: (SMDH Size << 4) | 0x0000000A
* 11: Pointer to the SMDH icon for the new ExtSaveData
@@ -556,7 +577,12 @@ static void CreateExtSaveData(Service::Interface* self) {
cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], icon_size,
cmd_buff[10], icon_buffer);
- cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size).raw;
+ FileSys::ArchiveFormatInfo format_info;
+ format_info.number_directories = cmd_buff[5];
+ format_info.number_files = cmd_buff[6];
+ format_info.duplicate_data = false;
+ format_info.total_size = 0;
+ cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size, format_info).raw;
}
/**
@@ -707,6 +733,75 @@ static void GetPriority(Service::Interface* self) {
LOG_DEBUG(Service_FS, "called priority=0x%X", priority);
}
+/**
+ * FS_User::GetArchiveResource service function.
+ * Inputs:
+ * 0 : 0x08490040
+ * 1 : Media type
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Sector byte-size
+ * 3 : Cluster byte-size
+ * 4 : Partition capacity in clusters
+ * 5 : Available free space in clusters
+ */
+static void GetArchiveResource(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ LOG_WARNING(Service_FS, "(STUBBED) called Media type=0x%08X", cmd_buff[1]);
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 512;
+ cmd_buff[3] = 16384;
+ cmd_buff[4] = 0x80000; // 8GiB capacity
+ cmd_buff[5] = 0x80000; // 8GiB free
+}
+
+/**
+ * FS_User::GetFormatInfo service function.
+ * Inputs:
+ * 0 : 0x084500C2
+ * 1 : Archive ID
+ * 2 : Archive path type
+ * 3 : Archive path size
+ * 4 : (PathSize << 14) | 2
+ * 5 : Archive low path
+ * Outputs:
+ * 0 : 0x08450140
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Total size
+ * 3 : Number of directories
+ * 4 : Number of files
+ * 5 : Duplicate data
+ */
+static void GetFormatInfo(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
+ auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
+ u32 archivename_size = cmd_buff[3];
+ u32 archivename_ptr = cmd_buff[5];
+ FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
+
+ LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
+
+ cmd_buff[0] = IPC::MakeHeader(0x0845, 5, 0);
+
+ auto format_info = GetArchiveFormatInfo(archive_id, archive_path);
+
+ if (format_info.Failed()) {
+ LOG_ERROR(Service_FS, "Failed to retrieve the format info");
+ cmd_buff[1] = format_info.Code().raw;
+ return;
+ }
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = format_info->total_size;
+ cmd_buff[3] = format_info->number_directories;
+ cmd_buff[4] = format_info->number_files;
+ cmd_buff[5] = format_info->duplicate_data;
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x000100C6, nullptr, "Dummy1"},
{0x040100C4, nullptr, "Control"},
@@ -778,11 +873,11 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
{0x08430000, nullptr, "InitializeCtrFileSystem"},
{0x08440000, nullptr, "CreateSeed"},
- {0x084500C2, nullptr, "GetFormatInfo"},
+ {0x084500C2, GetFormatInfo, "GetFormatInfo"},
{0x08460102, nullptr, "GetLegacyRomHeader2"},
{0x08470180, nullptr, "FormatCtrCardUserSaveData"},
{0x08480042, nullptr, "GetSdmcCtrRootPath"},
- {0x08490040, nullptr, "GetArchiveResource"},
+ {0x08490040, GetArchiveResource, "GetArchiveResource"},
{0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
{0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
{0x084C0242, FormatSaveData, "FormatSaveData"},
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index cb4fd38e2..1053d0f40 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -33,6 +33,11 @@ static Kernel::SharedPtr<Kernel::Event> event_debug_pad;
static u32 next_pad_index;
static u32 next_touch_index;
+static u32 next_accelerometer_index;
+static u32 next_gyroscope_index;
+
+static int enable_accelerometer_count = 0; // positive means enabled
+static int enable_gyroscope_count = 0; // positive means enabled
const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping = {{
Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y,
@@ -78,17 +83,17 @@ void Update() {
PadState changed = { { (state.hex ^ old_state.hex) } };
// Get the current Pad entry
- PadDataEntry* pad_entry = &mem->pad.entries[mem->pad.index];
+ PadDataEntry& pad_entry = mem->pad.entries[mem->pad.index];
// Update entry properties
- pad_entry->current_state.hex = state.hex;
- pad_entry->delta_additions.hex = changed.hex & state.hex;
- pad_entry->delta_removals.hex = changed.hex & old_state.hex;;
+ pad_entry.current_state.hex = state.hex;
+ pad_entry.delta_additions.hex = changed.hex & state.hex;
+ pad_entry.delta_removals.hex = changed.hex & old_state.hex;;
// Set circle Pad
- pad_entry->circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
+ pad_entry.circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
state.circle_right ? MAX_CIRCLEPAD_POS : 0x0;
- pad_entry->circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS :
+ pad_entry.circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS :
state.circle_up ? MAX_CIRCLEPAD_POS : 0x0;
// If we just updated index 0, provide a new timestamp
@@ -101,11 +106,11 @@ void Update() {
next_touch_index = (next_touch_index + 1) % mem->touch.entries.size();
// Get the current touch entry
- TouchDataEntry* touch_entry = &mem->touch.entries[mem->touch.index];
+ TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
bool pressed = false;
- std::tie(touch_entry->x, touch_entry->y, pressed) = VideoCore::g_emu_window->GetTouchState();
- touch_entry->valid.Assign(pressed ? 1 : 0);
+ std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState();
+ touch_entry.valid.Assign(pressed ? 1 : 0);
// TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
// supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being
@@ -120,6 +125,58 @@ void Update() {
// Signal both handles when there's an update to Pad or touch
event_pad_or_touch_1->Signal();
event_pad_or_touch_2->Signal();
+
+ // Update accelerometer
+ if (enable_accelerometer_count > 0) {
+ mem->accelerometer.index = next_accelerometer_index;
+ next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
+
+ AccelerometerDataEntry& accelerometer_entry = mem->accelerometer.entries[mem->accelerometer.index];
+ std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z)
+ = VideoCore::g_emu_window->GetAccelerometerState();
+
+ // Make up "raw" entry
+ // TODO(wwylele):
+ // From hardware testing, the raw_entry values are approximately,
+ // but not exactly, as twice as corresponding entries (or with a minus sign).
+ // It may caused by system calibration to the accelerometer.
+ // Figure out how it works, or, if no game reads raw_entry,
+ // the following three lines can be removed and leave raw_entry unimplemented.
+ mem->accelerometer.raw_entry.x = -2 * accelerometer_entry.x;
+ mem->accelerometer.raw_entry.z = 2 * accelerometer_entry.y;
+ mem->accelerometer.raw_entry.y = -2 * accelerometer_entry.z;
+
+ // If we just updated index 0, provide a new timestamp
+ if (mem->accelerometer.index == 0) {
+ mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks;
+ mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks();
+ }
+
+ event_accelerometer->Signal();
+ }
+
+ // Update gyroscope
+ if (enable_gyroscope_count > 0) {
+ mem->gyroscope.index = next_gyroscope_index;
+ next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
+
+ GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
+ std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z)
+ = VideoCore::g_emu_window->GetGyroscopeState();
+
+ // Make up "raw" entry
+ mem->gyroscope.raw_entry.x = gyroscope_entry.x;
+ mem->gyroscope.raw_entry.z = -gyroscope_entry.y;
+ mem->gyroscope.raw_entry.y = gyroscope_entry.z;
+
+ // If we just updated index 0, provide a new timestamp
+ if (mem->gyroscope.index == 0) {
+ mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks;
+ mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks();
+ }
+
+ event_gyroscope->Signal();
+ }
}
void GetIPCHandles(Service::Interface* self) {
@@ -139,40 +196,69 @@ void GetIPCHandles(Service::Interface* self) {
void EnableAccelerometer(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ ++enable_accelerometer_count;
event_accelerometer->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void DisableAccelerometer(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ --enable_accelerometer_count;
event_accelerometer->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void EnableGyroscopeLow(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ ++enable_gyroscope_count;
event_gyroscope->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void DisableGyroscopeLow(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
+ --enable_gyroscope_count;
event_gyroscope->Signal();
cmd_buff[1] = RESULT_SUCCESS.raw;
+ LOG_DEBUG(Service_HID, "called");
+}
+
+void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient();
+ memcpy(&cmd_buff[2], &coef, 4);
+}
+
+void GetGyroscopeLowCalibrateParam(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ const s16 param_unit = 6700; // an approximate value taken from hw
+ GyroscopeCalibrateParam param = {
+ { 0, param_unit, -param_unit },
+ { 0, param_unit, -param_unit },
+ { 0, param_unit, -param_unit },
+ };
+ memcpy(&cmd_buff[2], &param, sizeof(param));
+
LOG_WARNING(Service_HID, "(STUBBED) called");
}
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 517f4f2ae..170d19ea8 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -78,6 +78,24 @@ struct TouchDataEntry {
};
/**
+ * Structure of a single entry of accelerometer state history within HID shared memory
+ */
+struct AccelerometerDataEntry {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+/**
+ * Structure of a single entry of gyroscope state history within HID shared memory
+ */
+struct GyroscopeDataEntry {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+/**
* Structure of data stored in HID shared memory
*/
struct SharedMem {
@@ -112,6 +130,46 @@ struct SharedMem {
std::array<TouchDataEntry, 8> entries; ///< Last 8 touch entries, in pixel coordinates
} touch;
+
+ /// Accelerometer data
+ struct {
+ s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0
+ s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks`
+ u32 index; ///< Index of the last updated accelerometer entry
+
+ INSERT_PADDING_WORDS(0x1);
+
+ AccelerometerDataEntry raw_entry;
+ INSERT_PADDING_BYTES(2);
+
+ std::array<AccelerometerDataEntry, 8> entries;
+ } accelerometer;
+
+ /// Gyroscope data
+ struct {
+ s64 index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0
+ s64 index_reset_ticks_previous; ///< Previous `index_reset_ticks`
+ u32 index; ///< Index of the last updated accelerometer entry
+
+ INSERT_PADDING_WORDS(0x1);
+
+ GyroscopeDataEntry raw_entry;
+ INSERT_PADDING_BYTES(2);
+
+ std::array<GyroscopeDataEntry, 32> entries;
+ } gyroscope;
+};
+
+/**
+ * Structure of calibrate params that GetGyroscopeLowCalibrateParam returns
+ */
+struct GyroscopeCalibrateParam {
+ struct {
+ // TODO (wwylele): figure out the exact meaning of these params
+ s16 zero_point;
+ s16 positive_unit_point;
+ s16 negative_unit_point;
+ } x, y, z;
};
// TODO: MSVC does not support using offsetof() on non-static data members even though this
@@ -222,6 +280,26 @@ void DisableGyroscopeLow(Interface* self);
*/
void GetSoundVolume(Interface* self);
+/**
+ * HID::GetGyroscopeLowRawToDpsCoefficient service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : float output value
+ */
+void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self);
+
+/**
+ * HID::GetGyroscopeLowCalibrateParam service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2~6 (18 bytes) : struct GyroscopeCalibrateParam
+ */
+void GetGyroscopeLowCalibrateParam(Service::Interface* self);
+
/// Checks for user input updates
void Update();
diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp
index c50f597eb..046e65b11 100644
--- a/src/core/hle/service/hid/hid_spvr.cpp
+++ b/src/core/hle/service/hid/hid_spvr.cpp
@@ -9,16 +9,16 @@ namespace Service {
namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, GetIPCHandles, "GetIPCHandles"},
- {0x000B0000, nullptr, "StartAnalogStickCalibration"},
- {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
- {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
- {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
- {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
- {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
- {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
- {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
- {0x00170000, GetSoundVolume, "GetSoundVolume"},
+ {0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x000B0000, nullptr, "StartAnalogStickCalibration"},
+ {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
+ {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
+ {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
+ {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
+ {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
+ {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
+ {0x00170000, GetSoundVolume, "GetSoundVolume"},
};
HID_SPVR_Interface::HID_SPVR_Interface() {
diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp
index bbdde2abb..bb157b83d 100644
--- a/src/core/hle/service/hid/hid_user.cpp
+++ b/src/core/hle/service/hid/hid_user.cpp
@@ -9,16 +9,16 @@ namespace Service {
namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, GetIPCHandles, "GetIPCHandles"},
- {0x000B0000, nullptr, "StartAnalogStickCalibration"},
- {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
- {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
- {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
- {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
- {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
- {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
- {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
- {0x00170000, GetSoundVolume, "GetSoundVolume"},
+ {0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x000B0000, nullptr, "StartAnalogStickCalibration"},
+ {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
+ {0x00110000, EnableAccelerometer, "EnableAccelerometer"},
+ {0x00120000, DisableAccelerometer, "DisableAccelerometer"},
+ {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
+ {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
+ {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
+ {0x00170000, GetSoundVolume, "GetSoundVolume"},
};
HID_U_Interface::HID_U_Interface() {
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 6bdee4d9e..94f494690 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -103,7 +103,7 @@ void Init() {
// If the archive didn't exist, create the files inside
if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
// Format the archive to create the directories
- Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
+ Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, FileSys::ArchiveFormatInfo(), archive_path);
// Open it again to get a valid archive now that the folder exists
archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index b52e52d4a..ff0af8f12 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -5,6 +5,7 @@
#include <algorithm>
#include <cstring>
#include <unordered_map>
+#include <vector>
#include "common/assert.h"
#include "common/bit_field.h"
@@ -593,17 +594,13 @@ static void Poll(Service::Interface* self) {
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
// so we have to copy the data
- pollfd* platform_pollfd = new pollfd[nfds];
- for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
- platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]);
+ std::vector<pollfd> platform_pollfd(nfds);
+ std::transform(input_fds, input_fds + nfds, platform_pollfd.begin(), CTRPollFD::ToPlatform);
- int ret = ::poll(platform_pollfd, nfds, timeout);
+ const int ret = ::poll(platform_pollfd.data(), nfds, timeout);
// Now update the output pollfd structure
- for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
- output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]);
-
- delete[] platform_pollfd;
+ std::transform(platform_pollfd.begin(), platform_pollfd.end(), output_fds, CTRPollFD::FromPlatform);
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index a7f2715ba..84a4ce5fc 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -74,7 +74,7 @@ enum class ResultStatus {
ErrorEncrypted,
};
-static inline u32 MakeMagic(char a, char b, char c, char d) {
+constexpr u32 MakeMagic(char a, char b, char c, char d) {
return a | b << 8 | c << 16 | d << 24;
}
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 54721561e..028b59348 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -75,12 +75,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
break;
+ case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E):
+ g_state.primitive_assembler.Reconfigure(regs.triangle_topology);
+ break;
+
+ case PICA_REG_INDEX_WORKAROUND(restart_primitive, 0x25F):
+ g_state.primitive_assembler.Reset();
+ break;
+
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
- if (regs.vs_default_attributes_setup.index == 15) {
- // Reset immediate primitive state
- g_state.immediate.primitive_assembler.Reconfigure(regs.triangle_topology);
- g_state.immediate.attribute_id = 0;
- }
+ g_state.immediate.current_attribute = 0;
+ default_attr_counter = 0;
break;
// Load default vertex input attributes
@@ -105,7 +110,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
break;
}
- Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index];
+ Math::Vec4<float24> attribute;
// NOTE: The destination component order indeed is "backwards"
attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
@@ -119,26 +124,29 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// TODO: Verify that this actually modifies the register!
if (setup.index < 15) {
+ g_state.vs.default_attributes[setup.index] = attribute;
setup.index++;
} else {
// Put each attribute into an immediate input buffer.
// When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
// sent to the primitive assembler.
- auto& immediate_input = g_state.immediate.input;
- auto& immediate_attribute_id = g_state.immediate.attribute_id;
- const auto& attribute_config = regs.vertex_attributes;
+ auto& immediate_input = g_state.immediate.input_vertex;
+ auto& immediate_attribute_id = g_state.immediate.current_attribute;
immediate_input.attr[immediate_attribute_id++] = attribute;
- if (immediate_attribute_id >= attribute_config.GetNumTotalAttributes()) {
+ if (immediate_attribute_id >= regs.vs.num_input_attributes+1) {
immediate_attribute_id = 0;
Shader::UnitState<false> shader_unit;
Shader::Setup(shader_unit);
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input));
+
// Send to vertex shader
- Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, attribute_config.GetNumTotalAttributes());
+ Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
// Send to renderer
using Pica::Shader::OutputVertex;
@@ -146,7 +154,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
};
- g_state.immediate.primitive_assembler.SubmitVertex(output, AddTriangle);
+ g_state.primitive_assembler.SubmitVertex(output, AddTriangle);
}
}
}
@@ -154,9 +162,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
}
case PICA_REG_INDEX(gpu_mode):
- if (regs.gpu_mode == Regs::GPUMode::Configuring && regs.vs_default_attributes_setup.index == 15) {
+ if (regs.gpu_mode == Regs::GPUMode::Configuring) {
// Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
VideoCore::g_renderer->Rasterizer()->DrawTriangles();
+
+ if (g_debug_context) {
+ g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
+ }
}
break;
@@ -200,7 +212,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
for (int loader = 0; loader < 12; ++loader) {
const auto& loader_config = attribute_config.attribute_loaders[loader];
- u32 load_address = base_address + loader_config.data_offset;
+ u32 offset = 0;
// TODO: What happens if a loader overwrites a previous one's data?
for (unsigned component = 0; component < loader_config.component_count; ++component) {
@@ -212,17 +224,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
u32 attribute_index = loader_config.GetComponent(component);
if (attribute_index < 12) {
int element_size = attribute_config.GetElementSizeInBytes(attribute_index);
- load_address = Common::AlignUp(load_address, element_size);
- vertex_attribute_sources[attribute_index] = load_address;
+ offset = Common::AlignUp(offset, element_size);
+ vertex_attribute_sources[attribute_index] = base_address + loader_config.data_offset + offset;
vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index);
vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index);
vertex_attribute_element_size[attribute_index] = element_size;
- load_address += attribute_config.GetStride(attribute_index);
+ offset += attribute_config.GetStride(attribute_index);
} else if (attribute_index < 16) {
// Attribute ids 12, 13, 14 and 15 signify 4, 8, 12 and 16-byte paddings, respectively
- load_address = Common::AlignUp(load_address, 4);
- load_address += (attribute_index - 11) * 4;
+ offset = Common::AlignUp(offset, 4);
+ offset += (attribute_index - 11) * 4;
} else {
UNREACHABLE(); // This is truly unreachable due to the number of bits for each component
}
@@ -234,14 +246,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
const auto& index_info = regs.index_array;
const u8* index_address_8 = Memory::GetPhysicalPointer(base_address + index_info.offset);
- const u16* index_address_16 = (u16*)index_address_8;
+ const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8);
bool index_u16 = index_info.format != 0;
#if PICA_DUMP_GEOMETRY
DebugUtils::GeometryDumper geometry_dumper;
PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
#endif
- PrimitiveAssembler<Shader::OutputVertex> primitive_assembler(regs.triangle_topology.Value());
+ PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
if (g_debug_context) {
for (int i = 0; i < 3; ++i) {
@@ -345,10 +357,11 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
: (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? 2 : 1);
}
- const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata :
- (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata :
- (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? *(s16*)srcdata :
- *(float*)srcdata;
+ const float srcval =
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *reinterpret_cast<const s8*>(srcdata) :
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *reinterpret_cast<const u8*>(srcdata) :
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? *reinterpret_cast<const s16*>(srcdata) :
+ *reinterpret_cast<const float*>(srcdata);
input.attr[i][comp] = float24::FromFloat32(srcval);
LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08x + 0x%04x: %f",
@@ -411,16 +424,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
range.second, range.first);
}
- VideoCore::g_renderer->Rasterizer()->DrawTriangles();
-
#if PICA_DUMP_GEOMETRY
geometry_dumper.Dump();
#endif
- if (g_debug_context) {
- g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
- }
-
break;
}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 271e81ca1..bac6d69c7 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -117,13 +117,13 @@ void GeometryDumper::Dump() {
void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
{
struct StuffToWrite {
- u8* pointer;
+ const u8* pointer;
u32 size;
};
std::vector<StuffToWrite> writing_queue;
u32 write_offset = 0;
- auto QueueForWriting = [&writing_queue,&write_offset](u8* pointer, u32 size) {
+ auto QueueForWriting = [&writing_queue,&write_offset](const u8* pointer, u32 size) {
writing_queue.push_back({pointer, size});
u32 old_write_offset = write_offset;
write_offset += size;
@@ -228,27 +228,27 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
DVLPHeader dvlp{ DVLPHeader::MAGIC_WORD };
DVLEHeader dvle{ DVLEHeader::MAGIC_WORD };
- QueueForWriting((u8*)&dvlb, sizeof(dvlb));
- u32 dvlp_offset = QueueForWriting((u8*)&dvlp, sizeof(dvlp));
- dvlb.dvle_offset = QueueForWriting((u8*)&dvle, sizeof(dvle));
+ QueueForWriting(reinterpret_cast<const u8*>(&dvlb), sizeof(dvlb));
+ u32 dvlp_offset = QueueForWriting(reinterpret_cast<const u8*>(&dvlp), sizeof(dvlp));
+ dvlb.dvle_offset = QueueForWriting(reinterpret_cast<const u8*>(&dvle), sizeof(dvle));
// TODO: Reduce the amount of binary code written to relevant portions
dvlp.binary_offset = write_offset - dvlp_offset;
dvlp.binary_size_words = setup.program_code.size();
- QueueForWriting((u8*)setup.program_code.data(), setup.program_code.size() * sizeof(u32));
+ QueueForWriting(reinterpret_cast<const u8*>(setup.program_code.data()), setup.program_code.size() * sizeof(u32));
dvlp.swizzle_info_offset = write_offset - dvlp_offset;
dvlp.swizzle_info_num_entries = setup.swizzle_data.size();
u32 dummy = 0;
for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) {
- QueueForWriting((u8*)&setup.swizzle_data[i], sizeof(setup.swizzle_data[i]));
- QueueForWriting((u8*)&dummy, sizeof(dummy));
+ QueueForWriting(reinterpret_cast<const u8*>(&setup.swizzle_data[i]), sizeof(setup.swizzle_data[i]));
+ QueueForWriting(reinterpret_cast<const u8*>(&dummy), sizeof(dummy));
}
dvle.main_offset_words = config.main_offset;
dvle.output_register_table_offset = write_offset - dvlb.dvle_offset;
dvle.output_register_table_size = static_cast<u32>(output_info_table.size());
- QueueForWriting((u8*)output_info_table.data(), static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo)));
+ QueueForWriting(reinterpret_cast<const u8*>(output_info_table.data()), static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo)));
// TODO: Create a label table for "main"
@@ -292,14 +292,14 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
dvle.constant_table_offset = write_offset - dvlb.dvle_offset;
dvle.constant_table_size = constant_table.size();
for (const auto& constant : constant_table) {
- QueueForWriting((uint8_t*)&constant, sizeof(constant));
+ QueueForWriting(reinterpret_cast<const u8*>(&constant), sizeof(constant));
}
// Write data to file
std::ofstream file(filename, std::ios_base::out | std::ios_base::binary);
- for (auto& chunk : writing_queue) {
- file.write((char*)chunk.pointer, chunk.size);
+ for (const auto& chunk : writing_queue) {
+ file.write(reinterpret_cast<const char*>(chunk.pointer), chunk.size);
}
}
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index 32ad72674..ccbaf071b 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -493,12 +493,25 @@ std::string Regs::GetCommandName(int index) {
}
void Init() {
+ g_state.Reset();
}
void Shutdown() {
Shader::Shutdown();
+}
+
+template <typename T>
+void Zero(T& o) {
+ memset(&o, 0, sizeof(o));
+}
- memset(&g_state, 0, sizeof(State));
+void State::Reset() {
+ Zero(regs);
+ Zero(vs);
+ Zero(gs);
+ Zero(cmd_list);
+ Zero(immediate);
+ primitive_assembler.Reconfigure(Regs::TriangleTopology::List);
}
}
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 337cff8ce..16f9e4006 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -71,7 +71,7 @@ struct Regs {
BitField<0, 24, u32> viewport_depth_range; // float24
BitField<0, 24, u32> viewport_depth_far_plane; // float24
- INSERT_PADDING_WORDS(0x1);
+ BitField<0, 3, u32> vs_output_total;
union VSOutputAttributes {
// Maps components of output vertex attributes to semantics
@@ -1123,7 +1123,12 @@ struct Regs {
BitField<24, 8, u32> w;
} int_uniforms[4];
- INSERT_PADDING_WORDS(0x5);
+ INSERT_PADDING_WORDS(0x4);
+
+ union {
+ // Number of input attributes to shader unit - 1
+ BitField<0, 4, u32> num_input_attributes;
+ };
// Offset to shader program entry point (in words)
BitField<0, 16, u32> main_offset;
@@ -1157,8 +1162,10 @@ struct Regs {
}
} input_register_map;
- // OUTMAP_MASK, 0x28E, CODETRANSFER_END
- INSERT_PADDING_WORDS(0x3);
+ BitField<0, 16, u32> output_mask;
+
+ // 0x28E, CODETRANSFER_END
+ INSERT_PADDING_WORDS(0x2);
struct {
enum Format : u32
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index c7616bc55..323290054 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -12,6 +12,8 @@ namespace Pica {
/// Struct used to describe current Pica state
struct State {
+ void Reset();
+
/// Pica registers
Regs regs;
@@ -46,13 +48,14 @@ struct State {
/// Struct used to describe immediate mode rendering state
struct ImmediateModeState {
- Shader::InputVertex input;
- // This is constructed with a dummy triangle topology
- PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
- int attribute_id = 0;
-
- ImmediateModeState() : primitive_assembler(Regs::TriangleTopology::List) {}
+ // Used to buffer partial vertices for immediate-mode rendering.
+ Shader::InputVertex input_vertex;
+ // Index of the next attribute to be loaded into `input_vertex`.
+ int current_attribute = 0;
} immediate;
+
+ // This is constructed with a dummy triangle topology
+ PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
};
extern State g_state; ///< Current Pica state
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h
index cc6e5fde5..9396b4c85 100644
--- a/src/video_core/primitive_assembly.h
+++ b/src/video_core/primitive_assembly.h
@@ -20,7 +20,7 @@ struct PrimitiveAssembler {
VertexType& v1,
VertexType& v2)>;
- PrimitiveAssembler(Regs::TriangleTopology topology);
+ PrimitiveAssembler(Regs::TriangleTopology topology = Regs::TriangleTopology::List);
/*
* Queues a vertex, builds primitives from the vertex queue according to the given
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b3dc6aa19..1fadcf5ae 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -190,6 +190,9 @@ void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
}
void RasterizerOpenGL::DrawTriangles() {
+ if (vertex_batch.empty())
+ return;
+
SyncFramebuffer();
SyncDrawState();
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 509558fc0..eb1db0778 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -121,15 +121,23 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
OutputVertex ret;
// TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
// figure out what those circumstances are and enable the remaining outputs then.
- for (int i = 0; i < 7; ++i) {
- const auto& output_register_map = g_state.regs.vs_output_attributes[i]; // TODO: Don't hardcode VS here
+ unsigned index = 0;
+ for (unsigned i = 0; i < 7; ++i) {
+
+ if (index >= g_state.regs.vs_output_total)
+ break;
+
+ if ((g_state.regs.vs.output_mask & (1 << i)) == 0)
+ continue;
+
+ const auto& output_register_map = g_state.regs.vs_output_attributes[index]; // TODO: Don't hardcode VS here
u32 semantics[4] = {
output_register_map.map_x, output_register_map.map_y,
output_register_map.map_z, output_register_map.map_w
};
- for (int comp = 0; comp < 4; ++comp) {
+ for (unsigned comp = 0; comp < 4; ++comp) {
float24* out = ((float24*)&ret) + semantics[comp];
if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
*out = state.registers.output[i][comp];
@@ -139,10 +147,12 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
memset(out, 0, sizeof(*out));
}
}
+
+ index++;
}
// The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation
- for (int i = 0; i < 4; ++i) {
+ for (unsigned i = 0; i < 4; ++i) {
ret.color[i] = float24::FromFloat32(
std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
}
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp
index 295a2466b..9b978583e 100644
--- a/src/video_core/shader/shader_interpreter.cpp
+++ b/src/video_core/shader/shader_interpreter.cpp
@@ -2,10 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <common/file_util.h>
-
+#include <numeric>
#include <nihstro/shader_bytecode.h>
+#include "common/file_util.h"
#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/shader/shader.h"
@@ -214,10 +214,8 @@ void RunInterpreter(UnitState<Debug>& state) {
if (opcode == OpCode::Id::DPH || opcode == OpCode::Id::DPHI)
src1[3] = float24::FromFloat32(1.0f);
- float24 dot = float24::FromFloat32(0.f);
int num_components = (opcode == OpCode::Id::DP3) ? 3 : 4;
- for (int i = 0; i < num_components; ++i)
- dot = dot + src1[i] * src2[i];
+ float24 dot = std::inner_product(src1, src1 + num_components, src2, float24::FromFloat32(0.f));
for (int i = 0; i < 4; ++i) {
if (!swizzle.DestComponentEnabled(i))
@@ -409,7 +407,7 @@ void RunInterpreter(UnitState<Debug>& state) {
{
if ((instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD) ||
(instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI)) {
- const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.mad.operand_desc_id];
+ const SwizzlePattern& swizzle = *reinterpret_cast<const SwizzlePattern*>(&swizzle_data[instr.mad.operand_desc_id]);
bool is_inverted = (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI);