summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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/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.cpp113
-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.cpp46
-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.cpp8
40 files changed, 673 insertions, 203 deletions
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 @@
1set(SRCS 1set(SRCS
2 audio_core.cpp 2 audio_core.cpp
3 codec.cpp
3 hle/dsp.cpp 4 hle/dsp.cpp
4 hle/pipe.cpp 5 hle/pipe.cpp
5 ) 6 )
6 7
7set(HEADERS 8set(HEADERS
8 audio_core.h 9 audio_core.h
10 codec.h
9 hle/dsp.h 11 hle/dsp.h
10 hle/pipe.h 12 hle/pipe.h
11 sink.h 13 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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstddef>
7#include <cstring>
8#include <vector>
9
10#include "audio_core/codec.h"
11
12#include "common/assert.h"
13#include "common/common_types.h"
14#include "common/math_util.h"
15
16namespace Codec {
17
18StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) {
19 // GC-ADPCM with scale factor and variable coefficients.
20 // Frames are 8 bytes long containing 14 samples each.
21 // Samples are 4 bits (one nibble) long.
22
23 constexpr size_t FRAME_LEN = 8;
24 constexpr size_t SAMPLES_PER_FRAME = 14;
25 constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }};
26
27 const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
28 StereoBuffer16 ret(ret_size);
29
30 int yn1 = state.yn1,
31 yn2 = state.yn2;
32
33 const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
34 for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
35 const int frame_header = data[framei * FRAME_LEN];
36 const int scale = 1 << (frame_header & 0xF);
37 const int idx = (frame_header >> 4) & 0x7;
38
39 // Coefficients are fixed point with 11 bits fractional part.
40 const int coef1 = adpcm_coeff[idx * 2 + 0];
41 const int coef2 = adpcm_coeff[idx * 2 + 1];
42
43 // Decodes an audio sample. One nibble produces one sample.
44 const auto decode_sample = [&](const int nibble) -> s16 {
45 const int xn = nibble * scale;
46 // We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back.
47 // 0x400 == 0.5 in 11 bit fixed point.
48 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
49 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
50 // Clamp to output range.
51 val = MathUtil::Clamp(val, -32768, 32767);
52 // Advance output feedback.
53 yn2 = yn1;
54 yn1 = val;
55 return (s16)val;
56 };
57
58 size_t outputi = framei * SAMPLES_PER_FRAME;
59 size_t datai = framei * FRAME_LEN + 1;
60 for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
61 const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
62 ret[outputi].fill(sample1);
63 outputi++;
64
65 const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
66 ret[outputi].fill(sample2);
67 outputi++;
68
69 datai++;
70 }
71 }
72
73 state.yn1 = yn1;
74 state.yn2 = yn2;
75
76 return ret;
77}
78
79static s16 SignExtendS8(u8 x) {
80 // The data is actually signed PCM8.
81 // We sign extend this to signed PCM16.
82 return static_cast<s16>(static_cast<s8>(x));
83}
84
85StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) {
86 ASSERT(num_channels == 1 || num_channels == 2);
87
88 StereoBuffer16 ret(sample_count);
89
90 if (num_channels == 1) {
91 for (size_t i = 0; i < sample_count; i++) {
92 ret[i].fill(SignExtendS8(data[i]));
93 }
94 } else {
95 for (size_t i = 0; i < sample_count; i++) {
96 ret[i][0] = SignExtendS8(data[i * 2 + 0]);
97 ret[i][1] = SignExtendS8(data[i * 2 + 1]);
98 }
99 }
100
101 return ret;
102}
103
104StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) {
105 ASSERT(num_channels == 1 || num_channels == 2);
106
107 StereoBuffer16 ret(sample_count);
108
109 if (num_channels == 1) {
110 for (size_t i = 0; i < sample_count; i++) {
111 s16 sample;
112 std::memcpy(&sample, data + i * sizeof(s16), sizeof(s16));
113 ret[i].fill(sample);
114 }
115 } else {
116 std::memcpy(ret.data(), data, sample_count * 2 * sizeof(u16));
117 }
118
119 return ret;
120}
121
122};
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace Codec {
13
14/// A variable length buffer of signed PCM16 stereo samples.
15using StereoBuffer16 = std::vector<std::array<s16, 2>>;
16
17/// See: Codec::DecodeADPCM
18struct ADPCMState {
19 // Two historical samples from previous processed buffer,
20 // required for ADPCM decoding
21 s16 yn1; ///< y[n-1]
22 s16 yn2; ///< y[n-2]
23};
24
25/**
26 * @param data Pointer to buffer that contains ADPCM data to decode
27 * @param sample_count Length of buffer in terms of number of samples
28 * @param adpcm_coeff ADPCM coefficients
29 * @param state ADPCM state, this is updated with new state
30 * @return Decoded stereo signed PCM16 data, sample_count in length
31 */
32StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state);
33
34/**
35 * @param num_channels Number of channels
36 * @param data Pointer to buffer that contains PCM8 data to decode
37 * @param sample_count Length of buffer in terms of number of samples
38 * @return Decoded stereo signed PCM16 data, sample_count in length
39 */
40StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count);
41
42/**
43 * @param num_channels Number of channels
44 * @param data Pointer to buffer that contains PCM16 data to decode
45 * @param sample_count Length of buffer in terms of number of samples
46 * @return Decoded stereo signed PCM16 data, sample_count in length
47 */
48StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count);
49
50};
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include "common/swap.h" 6#include "common/swap.h"
6#include "common/logging/log.h" 7#include "common/logging/log.h"
7#include "core/memory.h" 8#include "core/memory.h"
@@ -48,8 +49,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode)
48 Spsr[UNDEFBANK] = Spsr_copy; 49 Spsr[UNDEFBANK] = Spsr_copy;
49 break; 50 break;
50 case FIQ32MODE: 51 case FIQ32MODE:
51 Reg_firq[0] = Reg[13]; 52 std::copy(Reg.begin() + 8, Reg.end() - 1, Reg_firq.begin());
52 Reg_firq[1] = Reg[14];
53 Spsr[FIQBANK] = Spsr_copy; 53 Spsr[FIQBANK] = Spsr_copy;
54 break; 54 break;
55 } 55 }
@@ -85,8 +85,7 @@ void ARMul_State::ChangePrivilegeMode(u32 new_mode)
85 Bank = UNDEFBANK; 85 Bank = UNDEFBANK;
86 break; 86 break;
87 case FIQ32MODE: 87 case FIQ32MODE:
88 Reg[13] = Reg_firq[0]; 88 std::copy(Reg_firq.begin(), Reg_firq.end(), Reg.begin() + 8);
89 Reg[14] = Reg_firq[1];
90 Spsr_copy = Spsr[FIQBANK]; 89 Spsr_copy = Spsr[FIQBANK];
91 Bank = FIQBANK; 90 Bank = FIQBANK;
92 break; 91 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 @@
11 11
12#include "common/bit_field.h" 12#include "common/bit_field.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/swap.h"
14 15
15#include "core/hle/result.h" 16#include "core/hle/result.h"
16 17
@@ -62,6 +63,14 @@ private:
62 std::u16string u16str; 63 std::u16string u16str;
63}; 64};
64 65
66struct ArchiveFormatInfo {
67 u32_le total_size; ///< The pre-defined size of the archive, as specified in the Create or Format call
68 u32_le number_directories; ///< The pre-defined number of directories in the archive, as specified in the Create or Format call
69 u32_le number_files; ///< The pre-defined number of files in the archive, as specified in the Create or Format call
70 u8 duplicate_data; ///< Whether the archive should duplicate the data, as specified in the Create or Format call
71};
72static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
73
65class ArchiveBackend : NonCopyable { 74class ArchiveBackend : NonCopyable {
66public: 75public:
67 virtual ~ArchiveBackend() { 76 virtual ~ArchiveBackend() {
@@ -76,16 +85,16 @@ public:
76 * Open a file specified by its path, using the specified mode 85 * Open a file specified by its path, using the specified mode
77 * @param path Path relative to the archive 86 * @param path Path relative to the archive
78 * @param mode Mode to open the file with 87 * @param mode Mode to open the file with
79 * @return Opened file, or nullptr 88 * @return Opened file, or error code
80 */ 89 */
81 virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0; 90 virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const = 0;
82 91
83 /** 92 /**
84 * Delete a file specified by its path 93 * Delete a file specified by its path
85 * @param path Path relative to the archive 94 * @param path Path relative to the archive
86 * @return Whether the file could be deleted 95 * @return Result of the operation
87 */ 96 */
88 virtual bool DeleteFile(const Path& path) const = 0; 97 virtual ResultCode DeleteFile(const Path& path) const = 0;
89 98
90 /** 99 /**
91 * Rename a File specified by its path 100 * Rename a File specified by its path
@@ -108,7 +117,7 @@ public:
108 * @param size The size of the new file, filled with zeroes 117 * @param size The size of the new file, filled with zeroes
109 * @return File creation result code 118 * @return File creation result code
110 */ 119 */
111 virtual ResultCode CreateFile(const Path& path, u32 size) const = 0; 120 virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
112 121
113 /** 122 /**
114 * Create a directory specified by its path 123 * Create a directory specified by its path
@@ -159,9 +168,17 @@ public:
159 /** 168 /**
160 * Deletes the archive contents and then re-creates the base folder 169 * Deletes the archive contents and then re-creates the base folder
161 * @param path Path to the archive 170 * @param path Path to the archive
171 * @param format_info Format information for the new archive
162 * @return ResultCode of the operation, 0 on success 172 * @return ResultCode of the operation, 0 on success
163 */ 173 */
164 virtual ResultCode Format(const Path& path) = 0; 174 virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0;
175
176 /**
177 * Retrieves the format info about the archive with the specified path
178 * @param path Path to the archive
179 * @return Format information about the archive or error code
180 */
181 virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0;
165}; 182};
166 183
167} // namespace FileSys 184} // 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) {
58} 58}
59 59
60ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared) 60ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared)
61 : mount_point(GetExtDataContainerPath(mount_location, shared)) { 61 : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) {
62 LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str()); 62 LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
63} 63}
64 64
@@ -74,21 +74,59 @@ bool ArchiveFactory_ExtSaveData::Initialize() {
74ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) { 74ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) {
75 std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/"; 75 std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/";
76 if (!FileUtil::Exists(fullpath)) { 76 if (!FileUtil::Exists(fullpath)) {
77 // TODO(Subv): Check error code, this one is probably wrong 77 // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
78 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 78 // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
79 ErrorSummary::InvalidState, ErrorLevel::Status); 79 if (!shared) {
80 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
81 ErrorSummary::InvalidState, ErrorLevel::Status);
82 } else {
83 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
84 ErrorSummary::InvalidState, ErrorLevel::Status);
85 }
80 } 86 }
81 auto archive = Common::make_unique<DiskArchive>(fullpath); 87 auto archive = Common::make_unique<DiskArchive>(fullpath);
82 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 88 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
83} 89}
84 90
85ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path) { 91ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
86 // These folders are always created with the ExtSaveData 92 // These folders are always created with the ExtSaveData
87 std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/"; 93 std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/";
88 std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/"; 94 std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/";
89 FileUtil::CreateFullPath(user_path); 95 FileUtil::CreateFullPath(user_path);
90 FileUtil::CreateFullPath(boss_path); 96 FileUtil::CreateFullPath(boss_path);
97
98 // Write the format metadata
99 std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
100 FileUtil::IOFile file(metadata_path, "wb");
101
102 if (!file.IsOpen()) {
103 // TODO(Subv): Find the correct error code
104 return ResultCode(-1);
105 }
106
107 file.WriteBytes(&format_info, sizeof(format_info));
91 return RESULT_SUCCESS; 108 return RESULT_SUCCESS;
92} 109}
93 110
111ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path) const {
112 std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
113 FileUtil::IOFile file(metadata_path, "rb");
114
115 if (!file.IsOpen()) {
116 LOG_ERROR(Service_FS, "Could not open metadata information for archive");
117 // TODO(Subv): Verify error code
118 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status);
119 }
120
121 ArchiveFormatInfo info = {};
122 file.ReadBytes(&info, sizeof(info));
123 return MakeResult<ArchiveFormatInfo>(info);
124}
125
126void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data, size_t icon_size) {
127 std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path);
128 FileUtil::IOFile icon_file(game_path + "icon", "wb");
129 icon_file.WriteBytes(icon_data, icon_size);
130}
131
94} // namespace FileSys 132} // 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:
31 std::string GetName() const override { return "ExtSaveData"; } 31 std::string GetName() const override { return "ExtSaveData"; }
32 32
33 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; 33 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
34 ResultCode Format(const Path& path) override; 34 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
35 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
35 36
36 const std::string& GetMountPoint() const { return mount_point; } 37 const std::string& GetMountPoint() const { return mount_point; }
37 38
39 /**
40 * Writes the SMDH icon of the ExtSaveData to file
41 * @param path Path of this ExtSaveData
42 * @param icon_data Binary data of the icon
43 * @param icon_size Size of the icon data
44 */
45 void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size);
46
38private: 47private:
48 bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive
49
39 /** 50 /**
40 * This holds the full directory path for this archive, it is only set after a successful call 51 * This holds the full directory path for this archive, it is only set after a successful call
41 * to Open, this is formed as <base extsavedatapath>/<type>/<high>/<low>. 52 * 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
29 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 29 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
30} 30}
31 31
32ResultCode ArchiveFactory_RomFS::Format(const Path& path) { 32ResultCode ArchiveFactory_RomFS::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
33 LOG_ERROR(Service_FS, "Attempted to format a RomFS archive."); 33 LOG_ERROR(Service_FS, "Attempted to format a RomFS archive.");
34 // TODO: Verify error code 34 // TODO: Verify error code
35 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, 35 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS,
36 ErrorSummary::NotSupported, ErrorLevel::Permanent); 36 ErrorSummary::NotSupported, ErrorLevel::Permanent);
37} 37}
38 38
39ResultVal<ArchiveFormatInfo> ArchiveFactory_RomFS::GetFormatInfo(const Path& path) const {
40 // TODO(Subv): Implement
41 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
42 return ResultCode(-1);
43}
44
39} // namespace FileSys 45} // 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:
26 26
27 std::string GetName() const override { return "RomFS"; } 27 std::string GetName() const override { return "RomFS"; }
28 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; 28 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
29 ResultCode Format(const Path& path) override; 29 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
30 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
30 31
31private: 32private:
32 std::shared_ptr<FileUtil::IOFile> romfs_file; 33 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) {
26} 26}
27 27
28static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) { 28static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
29 u32 high = program_id >> 32; 29 u32 high = (u32)(program_id >> 32);
30 u32 low = program_id & 0xFFFFFFFF; 30 u32 low = (u32)(program_id & 0xFFFFFFFF);
31 return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low); 31 return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low);
32} 32}
33 33
34static std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) {
35 u32 high = (u32)(program_id >> 32);
36 u32 low = (u32)(program_id & 0xFFFFFFFF);
37 return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(), high, low);
38}
39
34ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory) 40ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory)
35 : mount_point(GetSaveDataContainerPath(sdmc_directory)) { 41 : mount_point(GetSaveDataContainerPath(sdmc_directory)) {
36 LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); 42 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
51 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 57 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
52} 58}
53 59
54ResultCode ArchiveFactory_SaveData::Format(const Path& path) { 60ResultCode ArchiveFactory_SaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
55 std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); 61 std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id);
56 FileUtil::DeleteDirRecursively(concrete_mount_point); 62 FileUtil::DeleteDirRecursively(concrete_mount_point);
57 FileUtil::CreateFullPath(concrete_mount_point); 63 FileUtil::CreateFullPath(concrete_mount_point);
64
65 // Write the format metadata
66 std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
67 FileUtil::IOFile file(metadata_path, "wb");
68
69 if (file.IsOpen()) {
70 file.WriteBytes(&format_info, sizeof(format_info));
71 return RESULT_SUCCESS;
72 }
58 return RESULT_SUCCESS; 73 return RESULT_SUCCESS;
59} 74}
60 75
76ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
77 std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
78 FileUtil::IOFile file(metadata_path, "rb");
79
80 if (!file.IsOpen()) {
81 LOG_ERROR(Service_FS, "Could not open metadata information for archive");
82 // TODO(Subv): Verify error code
83 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status);
84 }
85
86 ArchiveFormatInfo info = {};
87 file.ReadBytes(&info, sizeof(info));
88 return MakeResult<ArchiveFormatInfo>(info);
89}
90
61} // namespace FileSys 91} // 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:
23 std::string GetName() const override { return "SaveData"; } 23 std::string GetName() const override { return "SaveData"; }
24 24
25 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; 25 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
26 ResultCode Format(const Path& path) override; 26 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
27
28 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
27 29
28private: 30private:
29 std::string mount_point; 31 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
48 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 48 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
49} 49}
50 50
51ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path) { 51ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
52 LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive."); 52 LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive.");
53 // TODO: Verify error code 53 // TODO: Verify error code
54 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, 54 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS,
55 ErrorSummary::NotSupported, ErrorLevel::Permanent); 55 ErrorSummary::NotSupported, ErrorLevel::Permanent);
56} 56}
57 57
58ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const {
59 // TODO(Subv): Implement
60 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
61 return ResultCode(-1);
62}
63
58} // namespace FileSys 64} // 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:
23 std::string GetName() const override { return "SaveDataCheck"; } 23 std::string GetName() const override { return "SaveDataCheck"; }
24 24
25 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; 25 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
26 ResultCode Format(const Path& path) override; 26 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
27 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
27 28
28private: 29private:
29 std::string mount_point; 30 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&
40 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 40 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
41} 41}
42 42
43ResultCode ArchiveFactory_SDMC::Format(const Path& path) { 43ResultCode ArchiveFactory_SDMC::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
44 // This is kind of an undesirable operation, so let's just ignore it. :) 44 // This is kind of an undesirable operation, so let's just ignore it. :)
45 return RESULT_SUCCESS; 45 return RESULT_SUCCESS;
46} 46}
47 47
48ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path) const {
49 // TODO(Subv): Implement
50 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
51 return ResultCode(-1);
52}
48} // namespace FileSys 53} // 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:
29 std::string GetName() const override { return "SDMC"; } 29 std::string GetName() const override { return "SDMC"; }
30 30
31 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; 31 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
32 ResultCode Format(const Path& path) override; 32 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
33 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
33 34
34private: 35private:
35 std::string sdmc_directory; 36 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
63 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 63 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
64} 64}
65 65
66ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path) { 66ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
67 std::string fullpath = GetSystemSaveDataPath(base_path, path); 67 std::string fullpath = GetSystemSaveDataPath(base_path, path);
68 FileUtil::DeleteDirRecursively(fullpath); 68 FileUtil::DeleteDirRecursively(fullpath);
69 FileUtil::CreateFullPath(fullpath); 69 FileUtil::CreateFullPath(fullpath);
70 return RESULT_SUCCESS; 70 return RESULT_SUCCESS;
71} 71}
72 72
73ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path) const {
74 // TODO(Subv): Implement
75 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
76 return ResultCode(-1);
77}
78
73} // namespace FileSys 79} // 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:
23 ArchiveFactory_SystemSaveData(const std::string& mount_point); 23 ArchiveFactory_SystemSaveData(const std::string& mount_point);
24 24
25 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; 25 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
26 ResultCode Format(const Path& path) override; 26 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
27 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
27 28
28 std::string GetName() const override { return "SystemSaveData"; } 29 std::string GetName() const override { return "SystemSaveData"; }
29 30
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 @@
17 17
18namespace FileSys { 18namespace FileSys {
19 19
20std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { 20ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
21 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); 21 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
22 auto file = Common::make_unique<DiskFile>(*this, path, mode); 22 auto file = Common::make_unique<DiskFile>(*this, path, mode);
23 if (!file->Open()) 23 ResultCode result = file->Open();
24 return nullptr; 24 if (result.IsError())
25 return std::move(file); 25 return result;
26 return MakeResult<std::unique_ptr<FileBackend>>(std::move(file));
26} 27}
27 28
28bool DiskArchive::DeleteFile(const Path& path) const { 29ResultCode DiskArchive::DeleteFile(const Path& path) const {
29 return FileUtil::Delete(mount_point + path.AsString()); 30 std::string file_path = mount_point + path.AsString();
31
32 if (FileUtil::IsDirectory(file_path))
33 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
34
35 if (!FileUtil::Exists(file_path))
36 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
37
38 if (FileUtil::Delete(file_path))
39 return RESULT_SUCCESS;
40
41 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
30} 42}
31 43
32bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { 44bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
@@ -37,11 +49,14 @@ bool DiskArchive::DeleteDirectory(const Path& path) const {
37 return FileUtil::DeleteDir(mount_point + path.AsString()); 49 return FileUtil::DeleteDir(mount_point + path.AsString());
38} 50}
39 51
40ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { 52ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const {
41 std::string full_path = mount_point + path.AsString(); 53 std::string full_path = mount_point + path.AsString();
42 54
55 if (FileUtil::IsDirectory(full_path))
56 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
57
43 if (FileUtil::Exists(full_path)) 58 if (FileUtil::Exists(full_path))
44 return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info); 59 return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Status);
45 60
46 if (size == 0) { 61 if (size == 0) {
47 FileUtil::CreateEmptyFile(full_path); 62 FileUtil::CreateEmptyFile(full_path);
@@ -89,38 +104,57 @@ DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode
89 this->mode.hex = mode.hex; 104 this->mode.hex = mode.hex;
90} 105}
91 106
92bool DiskFile::Open() { 107ResultCode DiskFile::Open() {
93 if (!mode.create_flag && !FileUtil::Exists(path)) { 108 if (FileUtil::IsDirectory(path))
94 LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str()); 109 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
95 return false; 110
111 // Specifying only the Create flag is invalid
112 if (mode.create_flag && !mode.read_flag && !mode.write_flag) {
113 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
114 }
115
116 if (!FileUtil::Exists(path)) {
117 if (!mode.create_flag) {
118 LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str());
119 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
120 } else {
121 // Create the file
122 FileUtil::CreateEmptyFile(path);
123 }
96 } 124 }
97 125
98 std::string mode_string; 126 std::string mode_string = "";
99 if (mode.create_flag) 127 if (mode.write_flag)
100 mode_string = "w+"; 128 mode_string += "r+"; // Files opened with Write access can be read from
101 else if (mode.write_flag)
102 mode_string = "r+"; // Files opened with Write access can be read from
103 else if (mode.read_flag) 129 else if (mode.read_flag)
104 mode_string = "r"; 130 mode_string += "r";
105 131
106 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems 132 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
107 mode_string += "b"; 133 mode_string += "b";
108 134
109 file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); 135 file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
110 return file->IsOpen(); 136 if (file->IsOpen())
137 return RESULT_SUCCESS;
138 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
111} 139}
112 140
113size_t DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { 141ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
142 if (!mode.read_flag && !mode.write_flag)
143 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
144
114 file->Seek(offset, SEEK_SET); 145 file->Seek(offset, SEEK_SET);
115 return file->ReadBytes(buffer, length); 146 return MakeResult<size_t>(file->ReadBytes(buffer, length));
116} 147}
117 148
118size_t DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { 149ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
150 if (!mode.write_flag)
151 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
152
119 file->Seek(offset, SEEK_SET); 153 file->Seek(offset, SEEK_SET);
120 size_t written = file->WriteBytes(buffer, length); 154 size_t written = file->WriteBytes(buffer, length);
121 if (flush) 155 if (flush)
122 file->Flush(); 156 file->Flush();
123 return written; 157 return MakeResult<size_t>(written);
124} 158}
125 159
126u64 DiskFile::GetSize() const { 160u64 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:
33 33
34 virtual std::string GetName() const override { return "DiskArchive: " + mount_point; } 34 virtual std::string GetName() const override { return "DiskArchive: " + mount_point; }
35 35
36 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; 36 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override;
37 bool DeleteFile(const Path& path) const override; 37 ResultCode DeleteFile(const Path& path) const override;
38 bool RenameFile(const Path& src_path, const Path& dest_path) const override; 38 bool RenameFile(const Path& src_path, const Path& dest_path) const override;
39 bool DeleteDirectory(const Path& path) const override; 39 bool DeleteDirectory(const Path& path) const override;
40 ResultCode CreateFile(const Path& path, u32 size) const override; 40 ResultCode CreateFile(const Path& path, u64 size) const override;
41 bool CreateDirectory(const Path& path) const override; 41 bool CreateDirectory(const Path& path) const override;
42 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; 42 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
43 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; 43 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
@@ -54,9 +54,9 @@ class DiskFile : public FileBackend {
54public: 54public:
55 DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); 55 DiskFile(const DiskArchive& archive, const Path& path, const Mode mode);
56 56
57 bool Open() override; 57 ResultCode Open() override;
58 size_t Read(u64 offset, size_t length, u8* buffer) const override; 58 ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
59 size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; 59 ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
60 u64 GetSize() const override; 60 u64 GetSize() const override;
61 bool SetSize(u64 size) const override; 61 bool SetSize(u64 size) const override;
62 bool Close() const override; 62 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 @@
7#include <cstddef> 7#include <cstddef>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/result.h"
10 11
11//////////////////////////////////////////////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////////////////////////////////////////////
12// FileSys namespace 13// FileSys namespace
@@ -20,18 +21,18 @@ public:
20 21
21 /** 22 /**
22 * Open the file 23 * Open the file
23 * @return true if the file opened correctly 24 * @return Result of the file operation
24 */ 25 */
25 virtual bool Open() = 0; 26 virtual ResultCode Open() = 0;
26 27
27 /** 28 /**
28 * Read data from the file 29 * Read data from the file
29 * @param offset Offset in bytes to start reading data from 30 * @param offset Offset in bytes to start reading data from
30 * @param length Length in bytes of data to read from file 31 * @param length Length in bytes of data to read from file
31 * @param buffer Buffer to read data into 32 * @param buffer Buffer to read data into
32 * @return Number of bytes read 33 * @return Number of bytes read, or error code
33 */ 34 */
34 virtual size_t Read(u64 offset, size_t length, u8* buffer) const = 0; 35 virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0;
35 36
36 /** 37 /**
37 * Write data to the file 38 * Write data to the file
@@ -39,9 +40,9 @@ public:
39 * @param length Length in bytes of data to write to file 40 * @param length Length in bytes of data to write to file
40 * @param flush The flush parameters (0 == do not flush) 41 * @param flush The flush parameters (0 == do not flush)
41 * @param buffer Buffer to read data from 42 * @param buffer Buffer to read data from
42 * @return Number of bytes written 43 * @return Number of bytes written, or error code
43 */ 44 */
44 virtual size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0; 45 virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0;
45 46
46 /** 47 /**
47 * Get the size of the file in bytes 48 * 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 {
20 return "IVFC"; 20 return "IVFC";
21} 21}
22 22
23std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { 23ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const {
24 return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size); 24 return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
25} 25}
26 26
27bool IVFCArchive::DeleteFile(const Path& path) const { 27ResultCode IVFCArchive::DeleteFile(const Path& path) const {
28 LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", GetName().c_str()); 28 LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", GetName().c_str());
29 return false; 29 // TODO(Subv): Verify error code
30 return ResultCode(ErrorDescription::NoData, ErrorModule::FS,
31 ErrorSummary::Canceled, ErrorLevel::Status);
30} 32}
31 33
32bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { 34bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
@@ -39,7 +41,7 @@ bool IVFCArchive::DeleteDirectory(const Path& path) const {
39 return false; 41 return false;
40} 42}
41 43
42ResultCode IVFCArchive::CreateFile(const Path& path, u32 size) const { 44ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
43 LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", GetName().c_str()); 45 LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", GetName().c_str());
44 // TODO: Verify error code 46 // TODO: Verify error code
45 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent); 47 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent);
@@ -66,17 +68,18 @@ u64 IVFCArchive::GetFreeBytes() const {
66 68
67//////////////////////////////////////////////////////////////////////////////////////////////////// 69////////////////////////////////////////////////////////////////////////////////////////////////////
68 70
69size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { 71ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
70 LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); 72 LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
71 romfs_file->Seek(data_offset + offset, SEEK_SET); 73 romfs_file->Seek(data_offset + offset, SEEK_SET);
72 size_t read_length = (size_t)std::min((u64)length, data_size - offset); 74 size_t read_length = (size_t)std::min((u64)length, data_size - offset);
73 75
74 return romfs_file->ReadBytes(buffer, read_length); 76 return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
75} 77}
76 78
77size_t IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { 79ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
78 LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); 80 LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
79 return 0; 81 // TODO(Subv): Find error code
82 return MakeResult<size_t>(0);
80} 83}
81 84
82u64 IVFCFile::GetSize() const { 85u64 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:
34 34
35 std::string GetName() const override; 35 std::string GetName() const override;
36 36
37 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; 37 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override;
38 bool DeleteFile(const Path& path) const override; 38 ResultCode DeleteFile(const Path& path) const override;
39 bool RenameFile(const Path& src_path, const Path& dest_path) const override; 39 bool RenameFile(const Path& src_path, const Path& dest_path) const override;
40 bool DeleteDirectory(const Path& path) const override; 40 bool DeleteDirectory(const Path& path) const override;
41 ResultCode CreateFile(const Path& path, u32 size) const override; 41 ResultCode CreateFile(const Path& path, u64 size) const override;
42 bool CreateDirectory(const Path& path) const override; 42 bool CreateDirectory(const Path& path) const override;
43 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; 43 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
44 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; 44 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
@@ -55,9 +55,9 @@ public:
55 IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) 55 IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
56 : romfs_file(file), data_offset(offset), data_size(size) {} 56 : romfs_file(file), data_offset(offset), data_size(size) {}
57 57
58 bool Open() override { return true; } 58 ResultCode Open() override { return RESULT_SUCCESS; }
59 size_t Read(u64 offset, size_t length, u8* buffer) const override; 59 ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
60 size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; 60 ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
61 u64 GetSize() const override; 61 u64 GetSize() const override;
62 bool SetSize(u64 size) const override; 62 bool SetSize(u64 size) const override;
63 bool Close() const override { return false; } 63 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 @@
7#pragma once 7#pragma once
8#include <atomic> 8#include <atomic>
9 9
10#include "common/common_types.h"
11
10namespace GDBStub { 12namespace GDBStub {
11 13
12/// Breakpoint Method 14/// 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 @@
16 16
17namespace IPC { 17namespace IPC {
18 18
19inline u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) { 19constexpr u32 MakeHeader(u16 command_id, unsigned int regular_params, unsigned int translate_params) {
20 return ((u32)command_id << 16) | (((u32)regular_params & 0x3F) << 6) | (((u32)translate_params & 0x3F) << 0); 20 return ((u32)command_id << 16) | (((u32)regular_params & 0x3F) << 6) | (((u32)translate_params & 0x3F) << 0);
21} 21}
22 22
23inline u32 MoveHandleDesc(unsigned int num_handles = 1) { 23constexpr u32 MoveHandleDesc(unsigned int num_handles = 1) {
24 return 0x0 | ((num_handles - 1) << 26); 24 return 0x0 | ((num_handles - 1) << 26);
25} 25}
26 26
27inline u32 CopyHandleDesc(unsigned int num_handles = 1) { 27constexpr u32 CopyHandleDesc(unsigned int num_handles = 1) {
28 return 0x10 | ((num_handles - 1) << 26); 28 return 0x10 | ((num_handles - 1) << 26);
29} 29}
30 30
31inline u32 CallingPidDesc() { 31constexpr u32 CallingPidDesc() {
32 return 0x20; 32 return 0x20;
33} 33}
34 34
35inline u32 StaticBufferDesc(u32 size, unsigned int buffer_id) { 35constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) {
36 return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10); 36 return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10);
37} 37}
38 38
@@ -42,7 +42,7 @@ enum MappedBufferPermissions {
42 RW = R | W, 42 RW = R | W,
43}; 43};
44 44
45inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { 45constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
46 return 0x8 | (size << 4) | (u32)perms; 46 return 0x8 | (size << 4) | (u32)perms;
47} 47}
48 48
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 @@
19enum class ErrorDescription : u32 { 19enum class ErrorDescription : u32 {
20 Success = 0, 20 Success = 0,
21 WrongAddress = 53, 21 WrongAddress = 53,
22 FS_NotFound = 100, 22 FS_NotFound = 120,
23 FS_AlreadyExists = 190,
24 FS_InvalidOpenFlags = 230,
25 FS_NotAFile = 250,
23 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive 26 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
27 FS_InvalidPath = 702,
24 InvalidSection = 1000, 28 InvalidSection = 1000,
25 TooLarge = 1001, 29 TooLarge = 1001,
26 NotAuthorized = 1002, 30 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() {
310 310
311ResultCode FormatConfig() { 311ResultCode FormatConfig() {
312 ResultCode res = DeleteConfigNANDSaveFile(); 312 ResultCode res = DeleteConfigNANDSaveFile();
313 if (!res.IsSuccess()) 313 // The delete command fails if the file doesn't exist, so we have to check that too
314 if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound)
314 return res; 315 return res;
315 // Delete the old data 316 // Delete the old data
316 cfg_config_file_buffer.fill(0); 317 cfg_config_file_buffer.fill(0);
@@ -407,7 +408,7 @@ void Init() {
407 // If the archive didn't exist, create the files inside 408 // If the archive didn't exist, create the files inside
408 if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { 409 if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
409 // Format the archive to create the directories 410 // Format the archive to create the directories
410 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); 411 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, FileSys::ArchiveFormatInfo(), archive_path);
411 412
412 // Open it again to get a valid archive now that the folder exists 413 // Open it again to get a valid archive now that the folder exists
413 archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); 414 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() {
103 u32 address = cmd_buff[5]; 103 u32 address = cmd_buff[5];
104 LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", 104 LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
105 GetTypeName().c_str(), GetName().c_str(), offset, length, address); 105 GetTypeName().c_str(), GetName().c_str(), offset, length, address);
106 cmd_buff[2] = static_cast<u32>(backend->Read(offset, length, Memory::GetPointer(address))); 106
107 if (offset + length > backend->GetSize()) {
108 LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX",
109 offset, length, backend->GetSize());
110 }
111
112 ResultVal<size_t> read = backend->Read(offset, length, Memory::GetPointer(address));
113 if (read.Failed()) {
114 cmd_buff[1] = read.Code().raw;
115 return read.Code();
116 }
117 cmd_buff[2] = static_cast<u32>(*read);
107 break; 118 break;
108 } 119 }
109 120
@@ -116,7 +127,13 @@ ResultVal<bool> File::SyncRequest() {
116 u32 address = cmd_buff[6]; 127 u32 address = cmd_buff[6];
117 LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", 128 LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
118 GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); 129 GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
119 cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush != 0, Memory::GetPointer(address))); 130
131 ResultVal<size_t> written = backend->Write(offset, length, flush != 0, Memory::GetPointer(address));
132 if (written.Failed()) {
133 cmd_buff[1] = written.Code().raw;
134 return written.Code();
135 }
136 cmd_buff[2] = static_cast<u32>(*written);
120 break; 137 break;
121 } 138 }
122 139
@@ -294,13 +311,11 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
294 if (archive == nullptr) 311 if (archive == nullptr)
295 return ERR_INVALID_HANDLE; 312 return ERR_INVALID_HANDLE;
296 313
297 std::unique_ptr<FileSys::FileBackend> backend = archive->OpenFile(path, mode); 314 auto backend = archive->OpenFile(path, mode);
298 if (backend == nullptr) { 315 if (backend.Failed())
299 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, 316 return backend.Code();
300 ErrorSummary::NotFound, ErrorLevel::Status);
301 }
302 317
303 auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path)); 318 auto file = Kernel::SharedPtr<File>(new File(backend.MoveFrom(), path));
304 return MakeResult<Kernel::SharedPtr<File>>(std::move(file)); 319 return MakeResult<Kernel::SharedPtr<File>>(std::move(file));
305} 320}
306 321
@@ -309,10 +324,7 @@ ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Pa
309 if (archive == nullptr) 324 if (archive == nullptr)
310 return ERR_INVALID_HANDLE; 325 return ERR_INVALID_HANDLE;
311 326
312 if (archive->DeleteFile(path)) 327 return archive->DeleteFile(path);
313 return RESULT_SUCCESS;
314 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
315 ErrorSummary::Canceled, ErrorLevel::Status);
316} 328}
317 329
318ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, 330ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
@@ -347,7 +359,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
347 ErrorSummary::Canceled, ErrorLevel::Status); 359 ErrorSummary::Canceled, ErrorLevel::Status);
348} 360}
349 361
350ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) { 362ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) {
351 ArchiveBackend* archive = GetArchive(archive_handle); 363 ArchiveBackend* archive = GetArchive(archive_handle);
352 if (archive == nullptr) 364 if (archive == nullptr)
353 return ERR_INVALID_HANDLE; 365 return ERR_INVALID_HANDLE;
@@ -395,7 +407,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
395 407
396 std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); 408 std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
397 if (backend == nullptr) { 409 if (backend == nullptr) {
398 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, 410 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
399 ErrorSummary::NotFound, ErrorLevel::Permanent); 411 ErrorSummary::NotFound, ErrorLevel::Permanent);
400 } 412 }
401 413
@@ -410,49 +422,45 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
410 return MakeResult<u64>(archive->GetFreeBytes()); 422 return MakeResult<u64>(archive->GetFreeBytes());
411} 423}
412 424
413ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) { 425ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path) {
414 auto archive_itr = id_code_map.find(id_code); 426 auto archive_itr = id_code_map.find(id_code);
415 if (archive_itr == id_code_map.end()) { 427 if (archive_itr == id_code_map.end()) {
416 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error 428 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
417 } 429 }
418 430
419 return archive_itr->second->Format(path); 431 return archive_itr->second->Format(path, format_info);
432}
433
434ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path) {
435 auto archive = id_code_map.find(id_code);
436 if (archive == id_code_map.end()) {
437 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
438 }
439
440 return archive->second->GetFormatInfo(archive_path);
420} 441}
421 442
422ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size) { 443ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info) {
423 // Construct the binary path to the archive first 444 // Construct the binary path to the archive first
424 FileSys::Path path = FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low); 445 FileSys::Path path = FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low);
425 446
426 std::string media_type_directory; 447 auto archive = id_code_map.find(media_type == MediaType::NAND ? ArchiveIdCode::SharedExtSaveData : ArchiveIdCode::ExtSaveData);
427 if (media_type == MediaType::NAND) { 448
428 media_type_directory = FileUtil::GetUserPath(D_NAND_IDX); 449 if (archive == id_code_map.end()) {
429 } else if (media_type == MediaType::SDMC) { 450 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
430 media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX);
431 } else {
432 LOG_ERROR(Service_FS, "Unsupported media type %u", media_type);
433 return ResultCode(-1); // TODO(Subv): Find the right error code
434 } 451 }
435 452
436 std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND); 453 auto ext_savedata = static_cast<FileSys::ArchiveFactory_ExtSaveData*>(archive->second.get());
437 std::string game_path = FileSys::GetExtSaveDataPath(base_path, path); 454
438 // These two folders are always created with the ExtSaveData 455 ResultCode result = ext_savedata->Format(path, format_info);
439 std::string user_path = game_path + "user/"; 456 if (result.IsError())
440 std::string boss_path = game_path + "boss/"; 457 return result;
441 if (!FileUtil::CreateFullPath(user_path))
442 return ResultCode(-1); // TODO(Subv): Find the right error code
443 if (!FileUtil::CreateFullPath(boss_path))
444 return ResultCode(-1); // TODO(Subv): Find the right error code
445 458
446 u8* smdh_icon = Memory::GetPointer(icon_buffer); 459 u8* smdh_icon = Memory::GetPointer(icon_buffer);
447 if (!smdh_icon) 460 if (!smdh_icon)
448 return ResultCode(-1); // TODO(Subv): Find the right error code 461 return ResultCode(-1); // TODO(Subv): Find the right error code
449 462
450 // Create the icon 463 ext_savedata->WriteIcon(path, smdh_icon, icon_size);
451 FileUtil::IOFile icon_file(game_path + "icon", "wb+");
452 if (!icon_file.IsGood())
453 return ResultCode(-1); // TODO(Subv): Find the right error code
454
455 icon_file.WriteBytes(smdh_icon, icon_size);
456 return RESULT_SUCCESS; 464 return RESULT_SUCCESS;
457} 465}
458 466
@@ -473,7 +481,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) {
473 // Delete all directories (/user, /boss) and the icon file. 481 // Delete all directories (/user, /boss) and the icon file.
474 std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND); 482 std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND);
475 std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path); 483 std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path);
476 if (!FileUtil::DeleteDirRecursively(extsavedata_path)) 484 if (FileUtil::Exists(extsavedata_path) && !FileUtil::DeleteDirRecursively(extsavedata_path))
477 return ResultCode(-1); // TODO(Subv): Find the right error code 485 return ResultCode(-1); // TODO(Subv): Find the right error code
478 return RESULT_SUCCESS; 486 return RESULT_SUCCESS;
479} 487}
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
136 * @param file_size The size of the new file, filled with zeroes 136 * @param file_size The size of the new file, filled with zeroes
137 * @return File creation result code 137 * @return File creation result code
138 */ 138 */
139ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size); 139ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size);
140 140
141/** 141/**
142 * Create a Directory from an Archive 142 * Create a Directory from an Archive
@@ -177,10 +177,20 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle);
177 * Erases the contents of the physical folder that contains the archive 177 * Erases the contents of the physical folder that contains the archive
178 * identified by the specified id code and path 178 * identified by the specified id code and path
179 * @param id_code The id of the archive to format 179 * @param id_code The id of the archive to format
180 * @param format_info Format information about the new archive
180 * @param path The path to the archive, if relevant. 181 * @param path The path to the archive, if relevant.
181 * @return ResultCode 0 on success or the corresponding code on error 182 * @return ResultCode 0 on success or the corresponding code on error
182 */ 183 */
183ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = FileSys::Path()); 184ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path = FileSys::Path());
185
186/**
187 * Retrieves the format info about the archive of the specified type and path.
188 * The format info is supplied by the client code when creating archives.
189 * @param id_code The id of the archive
190 * @param archive_path The path of the archive, if relevant
191 * @return The format info of the archive, or the corresponding error code if failed.
192 */
193ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path);
184 194
185/** 195/**
186 * Creates a blank SharedExtSaveData archive for the specified extdata ID 196 * Creates a blank SharedExtSaveData archive for the specified extdata ID
@@ -189,9 +199,10 @@ ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = File
189 * @param low The low word of the extdata id to create 199 * @param low The low word of the extdata id to create
190 * @param icon_buffer VAddr of the SMDH icon for this ExtSaveData 200 * @param icon_buffer VAddr of the SMDH icon for this ExtSaveData
191 * @param icon_size Size of the SMDH icon 201 * @param icon_size Size of the SMDH icon
202 * @param format_info Format information about the new archive
192 * @return ResultCode 0 on success or the corresponding code on error 203 * @return ResultCode 0 on success or the corresponding code on error
193 */ 204 */
194ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size); 205ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info);
195 206
196/** 207/**
197 * Deletes the SharedExtSaveData archive for the specified extdata ID 208 * 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 e6c1f3616..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) {
234 * 3 : Archive handle upper word 234 * 3 : Archive handle upper word
235 * 4 : File path string type 235 * 4 : File path string type
236 * 5 : File path string size 236 * 5 : File path string size
237 * 7 : File size (filled with zeroes) 237 * 7-8 : File size
238 * 10: File path string data 238 * 10: File path string data
239 * Outputs: 239 * Outputs:
240 * 1 : Result of function, 0 on success, otherwise error code 240 * 1 : Result of function, 0 on success, otherwise error code
@@ -245,12 +245,12 @@ static void CreateFile(Service::Interface* self) {
245 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); 245 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
246 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); 246 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
247 u32 filename_size = cmd_buff[5]; 247 u32 filename_size = cmd_buff[5];
248 u32 file_size = cmd_buff[7]; 248 u64 file_size = ((u64)cmd_buff[8] << 32) | cmd_buff[7];
249 u32 filename_ptr = cmd_buff[10]; 249 u32 filename_ptr = cmd_buff[10];
250 250
251 FileSys::Path file_path(filename_type, filename_size, filename_ptr); 251 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
252 252
253 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); 253 LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str());
254 254
255 cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; 255 cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw;
256} 256}
@@ -443,17 +443,22 @@ static void IsSdmcWriteable(Service::Interface* self) {
443 * Inputs: 443 * Inputs:
444 * 0 : 0x084C0242 444 * 0 : 0x084C0242
445 * 1 : Archive ID 445 * 1 : Archive ID
446 * 2 : Archive low path type 446 * 2 : Archive path type
447 * 3 : Archive low path size 447 * 3 : Archive path size
448 * 10 : (LowPathSize << 14) | 2 448 * 4 : Size in Blocks (1 block = 512 bytes)
449 * 5 : Number of directories
450 * 6 : Number of files
451 * 7 : Directory bucket count
452 * 8 : File bucket count
453 * 9 : Duplicate data
454 * 10 : (PathSize << 14) | 2
449 * 11 : Archive low path 455 * 11 : Archive low path
450 * Outputs: 456 * Outputs:
451 * 1 : Result of function, 0 on success, otherwise error code 457 * 1 : Result of function, 0 on success, otherwise error code
452 */ 458 */
453static void FormatSaveData(Service::Interface* self) { 459static void FormatSaveData(Service::Interface* self) {
454 // TODO(Subv): Find out what the other inputs and outputs of this function are
455 u32* cmd_buff = Kernel::GetCommandBuffer(); 460 u32* cmd_buff = Kernel::GetCommandBuffer();
456 LOG_DEBUG(Service_FS, "(STUBBED)"); 461 LOG_WARNING(Service_FS, "(STUBBED)");
457 462
458 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); 463 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
459 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); 464 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
@@ -464,9 +469,9 @@ static void FormatSaveData(Service::Interface* self) {
464 LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); 469 LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
465 470
466 if (archive_id != FS::ArchiveIdCode::SaveData) { 471 if (archive_id != FS::ArchiveIdCode::SaveData) {
467 // TODO(Subv): What should happen if somebody attempts to format a different archive? 472 LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", archive_id);
468 LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]); 473 cmd_buff[1] = ResultCode(ErrorDescription::FS_InvalidPath, ErrorModule::FS,
469 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; 474 ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw;
470 return; 475 return;
471 } 476 }
472 477
@@ -477,23 +482,40 @@ static void FormatSaveData(Service::Interface* self) {
477 return; 482 return;
478 } 483 }
479 484
480 cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw; 485 FileSys::ArchiveFormatInfo format_info;
486 format_info.duplicate_data = cmd_buff[9] & 0xFF;
487 format_info.number_directories = cmd_buff[5];
488 format_info.number_files = cmd_buff[6];
489 format_info.total_size = cmd_buff[4] * 512;
490
491 cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw;
481} 492}
482 493
483/** 494/**
484 * FS_User::FormatThisUserSaveData service function 495 * FS_User::FormatThisUserSaveData service function
485 * Inputs: 496 * Inputs:
486 * 0: 0x080F0180 497 * 0: 0x080F0180
498 * 1 : Size in Blocks (1 block = 512 bytes)
499 * 2 : Number of directories
500 * 3 : Number of files
501 * 4 : Directory bucket count
502 * 5 : File bucket count
503 * 6 : Duplicate data
487 * Outputs: 504 * Outputs:
488 * 1 : Result of function, 0 on success, otherwise error code 505 * 1 : Result of function, 0 on success, otherwise error code
489 */ 506 */
490static void FormatThisUserSaveData(Service::Interface* self) { 507static void FormatThisUserSaveData(Service::Interface* self) {
491 u32* cmd_buff = Kernel::GetCommandBuffer(); 508 u32* cmd_buff = Kernel::GetCommandBuffer();
492 LOG_DEBUG(Service_FS, "(STUBBED)");
493 509
494 // TODO(Subv): Find out what the inputs and outputs of this function are 510 FileSys::ArchiveFormatInfo format_info;
511 format_info.duplicate_data = cmd_buff[6] & 0xFF;
512 format_info.number_directories = cmd_buff[2];
513 format_info.number_files = cmd_buff[3];
514 format_info.total_size = cmd_buff[1] * 512;
495 515
496 cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw; 516 cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw;
517
518 LOG_TRACE(Service_FS, "called");
497} 519}
498 520
499/** 521/**
@@ -531,10 +553,9 @@ static void GetFreeBytes(Service::Interface* self) {
531 * 2 : Low word of the saveid to create 553 * 2 : Low word of the saveid to create
532 * 3 : High word of the saveid to create 554 * 3 : High word of the saveid to create
533 * 4 : Unknown 555 * 4 : Unknown
534 * 5 : Unknown 556 * 5 : Number of directories
535 * 6 : Unknown 557 * 6 : Number of files
536 * 7 : Unknown 558 * 7-8 : Size limit
537 * 8 : Unknown
538 * 9 : Size of the SMDH icon 559 * 9 : Size of the SMDH icon
539 * 10: (SMDH Size << 4) | 0x0000000A 560 * 10: (SMDH Size << 4) | 0x0000000A
540 * 11: Pointer to the SMDH icon for the new ExtSaveData 561 * 11: Pointer to the SMDH icon for the new ExtSaveData
@@ -556,7 +577,12 @@ static void CreateExtSaveData(Service::Interface* self) {
556 cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], icon_size, 577 cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], icon_size,
557 cmd_buff[10], icon_buffer); 578 cmd_buff[10], icon_buffer);
558 579
559 cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size).raw; 580 FileSys::ArchiveFormatInfo format_info;
581 format_info.number_directories = cmd_buff[5];
582 format_info.number_files = cmd_buff[6];
583 format_info.duplicate_data = false;
584 format_info.total_size = 0;
585 cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size, format_info).raw;
560} 586}
561 587
562/** 588/**
@@ -731,6 +757,51 @@ static void GetArchiveResource(Service::Interface* self) {
731 cmd_buff[5] = 0x80000; // 8GiB free 757 cmd_buff[5] = 0x80000; // 8GiB free
732} 758}
733 759
760/**
761 * FS_User::GetFormatInfo service function.
762 * Inputs:
763 * 0 : 0x084500C2
764 * 1 : Archive ID
765 * 2 : Archive path type
766 * 3 : Archive path size
767 * 4 : (PathSize << 14) | 2
768 * 5 : Archive low path
769 * Outputs:
770 * 0 : 0x08450140
771 * 1 : Result of function, 0 on success, otherwise error code
772 * 2 : Total size
773 * 3 : Number of directories
774 * 4 : Number of files
775 * 5 : Duplicate data
776 */
777static void GetFormatInfo(Service::Interface* self) {
778 u32* cmd_buff = Kernel::GetCommandBuffer();
779
780 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
781 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
782 u32 archivename_size = cmd_buff[3];
783 u32 archivename_ptr = cmd_buff[5];
784 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
785
786 LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
787
788 cmd_buff[0] = IPC::MakeHeader(0x0845, 5, 0);
789
790 auto format_info = GetArchiveFormatInfo(archive_id, archive_path);
791
792 if (format_info.Failed()) {
793 LOG_ERROR(Service_FS, "Failed to retrieve the format info");
794 cmd_buff[1] = format_info.Code().raw;
795 return;
796 }
797
798 cmd_buff[1] = RESULT_SUCCESS.raw;
799 cmd_buff[2] = format_info->total_size;
800 cmd_buff[3] = format_info->number_directories;
801 cmd_buff[4] = format_info->number_files;
802 cmd_buff[5] = format_info->duplicate_data;
803}
804
734const Interface::FunctionInfo FunctionTable[] = { 805const Interface::FunctionInfo FunctionTable[] = {
735 {0x000100C6, nullptr, "Dummy1"}, 806 {0x000100C6, nullptr, "Dummy1"},
736 {0x040100C4, nullptr, "Control"}, 807 {0x040100C4, nullptr, "Control"},
@@ -802,7 +873,7 @@ const Interface::FunctionInfo FunctionTable[] = {
802 {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"}, 873 {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
803 {0x08430000, nullptr, "InitializeCtrFileSystem"}, 874 {0x08430000, nullptr, "InitializeCtrFileSystem"},
804 {0x08440000, nullptr, "CreateSeed"}, 875 {0x08440000, nullptr, "CreateSeed"},
805 {0x084500C2, nullptr, "GetFormatInfo"}, 876 {0x084500C2, GetFormatInfo, "GetFormatInfo"},
806 {0x08460102, nullptr, "GetLegacyRomHeader2"}, 877 {0x08460102, nullptr, "GetLegacyRomHeader2"},
807 {0x08470180, nullptr, "FormatCtrCardUserSaveData"}, 878 {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
808 {0x08480042, nullptr, "GetSdmcCtrRootPath"}, 879 {0x08480042, nullptr, "GetSdmcCtrRootPath"},
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() {
103 // If the archive didn't exist, create the files inside 103 // If the archive didn't exist, create the files inside
104 if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { 104 if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
105 // Format the archive to create the directories 105 // Format the archive to create the directories
106 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); 106 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, FileSys::ArchiveFormatInfo(), archive_path);
107 // Open it again to get a valid archive now that the folder exists 107 // Open it again to get a valid archive now that the folder exists
108 archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); 108 archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
109 ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); 109 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 @@
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <unordered_map> 7#include <unordered_map>
8#include <vector>
8 9
9#include "common/assert.h" 10#include "common/assert.h"
10#include "common/bit_field.h" 11#include "common/bit_field.h"
@@ -593,17 +594,13 @@ static void Poll(Service::Interface* self) {
593 594
594 // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes) 595 // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
595 // so we have to copy the data 596 // so we have to copy the data
596 pollfd* platform_pollfd = new pollfd[nfds]; 597 std::vector<pollfd> platform_pollfd(nfds);
597 for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) 598 std::transform(input_fds, input_fds + nfds, platform_pollfd.begin(), CTRPollFD::ToPlatform);
598 platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]);
599 599
600 int ret = ::poll(platform_pollfd, nfds, timeout); 600 const int ret = ::poll(platform_pollfd.data(), nfds, timeout);
601 601
602 // Now update the output pollfd structure 602 // Now update the output pollfd structure
603 for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) 603 std::transform(platform_pollfd.begin(), platform_pollfd.end(), output_fds, CTRPollFD::FromPlatform);
604 output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]);
605
606 delete[] platform_pollfd;
607 604
608 int result = 0; 605 int result = 0;
609 if (ret == SOCKET_ERROR_VALUE) 606 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 {
74 ErrorEncrypted, 74 ErrorEncrypted,
75}; 75};
76 76
77static inline u32 MakeMagic(char a, char b, char c, char d) { 77constexpr u32 MakeMagic(char a, char b, char c, char d) {
78 return a | b << 8 | c << 16 | d << 24; 78 return a | b << 8 | c << 16 | d << 24;
79} 79}
80 80
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 4b59984ad..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) {
75 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); 75 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
76 break; 76 break;
77 77
78 case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E):
79 g_state.primitive_assembler.Reconfigure(regs.triangle_topology);
80 break;
81
82 case PICA_REG_INDEX_WORKAROUND(restart_primitive, 0x25F):
83 g_state.primitive_assembler.Reset();
84 break;
85
78 case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232): 86 case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
79 if (regs.vs_default_attributes_setup.index == 15) { 87 g_state.immediate.current_attribute = 0;
80 // Reset immediate primitive state 88 default_attr_counter = 0;
81 g_state.immediate.primitive_assembler.Reconfigure(regs.triangle_topology);
82 g_state.immediate.attribute_id = 0;
83 }
84 break; 89 break;
85 90
86 // Load default vertex input attributes 91 // Load default vertex input attributes
@@ -105,7 +110,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
105 break; 110 break;
106 } 111 }
107 112
108 Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index]; 113 Math::Vec4<float24> attribute;
109 114
110 // NOTE: The destination component order indeed is "backwards" 115 // NOTE: The destination component order indeed is "backwards"
111 attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8); 116 attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
@@ -119,26 +124,29 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
119 124
120 // TODO: Verify that this actually modifies the register! 125 // TODO: Verify that this actually modifies the register!
121 if (setup.index < 15) { 126 if (setup.index < 15) {
127 g_state.vs.default_attributes[setup.index] = attribute;
122 setup.index++; 128 setup.index++;
123 } else { 129 } else {
124 // Put each attribute into an immediate input buffer. 130 // Put each attribute into an immediate input buffer.
125 // When all specified immediate attributes are present, the Vertex Shader is invoked and everything is 131 // When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
126 // sent to the primitive assembler. 132 // sent to the primitive assembler.
127 133
128 auto& immediate_input = g_state.immediate.input; 134 auto& immediate_input = g_state.immediate.input_vertex;
129 auto& immediate_attribute_id = g_state.immediate.attribute_id; 135 auto& immediate_attribute_id = g_state.immediate.current_attribute;
130 const auto& attribute_config = regs.vertex_attributes;
131 136
132 immediate_input.attr[immediate_attribute_id++] = attribute; 137 immediate_input.attr[immediate_attribute_id++] = attribute;
133 138
134 if (immediate_attribute_id >= attribute_config.GetNumTotalAttributes()) { 139 if (immediate_attribute_id >= regs.vs.num_input_attributes+1) {
135 immediate_attribute_id = 0; 140 immediate_attribute_id = 0;
136 141
137 Shader::UnitState<false> shader_unit; 142 Shader::UnitState<false> shader_unit;
138 Shader::Setup(shader_unit); 143 Shader::Setup(shader_unit);
139 144
145 if (g_debug_context)
146 g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input));
147
140 // Send to vertex shader 148 // Send to vertex shader
141 Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, attribute_config.GetNumTotalAttributes()); 149 Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
142 150
143 // Send to renderer 151 // Send to renderer
144 using Pica::Shader::OutputVertex; 152 using Pica::Shader::OutputVertex;
@@ -146,7 +154,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
146 VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2); 154 VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
147 }; 155 };
148 156
149 g_state.immediate.primitive_assembler.SubmitVertex(output, AddTriangle); 157 g_state.primitive_assembler.SubmitVertex(output, AddTriangle);
150 } 158 }
151 } 159 }
152 } 160 }
@@ -154,9 +162,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
154 } 162 }
155 163
156 case PICA_REG_INDEX(gpu_mode): 164 case PICA_REG_INDEX(gpu_mode):
157 if (regs.gpu_mode == Regs::GPUMode::Configuring && regs.vs_default_attributes_setup.index == 15) { 165 if (regs.gpu_mode == Regs::GPUMode::Configuring) {
158 // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring 166 // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
159 VideoCore::g_renderer->Rasterizer()->DrawTriangles(); 167 VideoCore::g_renderer->Rasterizer()->DrawTriangles();
168
169 if (g_debug_context) {
170 g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
171 }
160 } 172 }
161 break; 173 break;
162 174
@@ -241,7 +253,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
241 DebugUtils::GeometryDumper geometry_dumper; 253 DebugUtils::GeometryDumper geometry_dumper;
242 PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value()); 254 PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
243#endif 255#endif
244 PrimitiveAssembler<Shader::OutputVertex> primitive_assembler(regs.triangle_topology.Value()); 256 PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
245 257
246 if (g_debug_context) { 258 if (g_debug_context) {
247 for (int i = 0; i < 3; ++i) { 259 for (int i = 0; i < 3; ++i) {
@@ -412,16 +424,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
412 range.second, range.first); 424 range.second, range.first);
413 } 425 }
414 426
415 VideoCore::g_renderer->Rasterizer()->DrawTriangles();
416
417#if PICA_DUMP_GEOMETRY 427#if PICA_DUMP_GEOMETRY
418 geometry_dumper.Dump(); 428 geometry_dumper.Dump();
419#endif 429#endif
420 430
421 if (g_debug_context) {
422 g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
423 }
424
425 break; 431 break;
426 } 432 }
427 433
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) {
493} 493}
494 494
495void Init() { 495void Init() {
496 g_state.Reset();
496} 497}
497 498
498void Shutdown() { 499void Shutdown() {
499 Shader::Shutdown(); 500 Shader::Shutdown();
501}
502
503template <typename T>
504void Zero(T& o) {
505 memset(&o, 0, sizeof(o));
506}
500 507
501 memset(&g_state, 0, sizeof(State)); 508void State::Reset() {
509 Zero(regs);
510 Zero(vs);
511 Zero(gs);
512 Zero(cmd_list);
513 Zero(immediate);
514 primitive_assembler.Reconfigure(Regs::TriangleTopology::List);
502} 515}
503 516
504} 517}
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 {
71 BitField<0, 24, u32> viewport_depth_range; // float24 71 BitField<0, 24, u32> viewport_depth_range; // float24
72 BitField<0, 24, u32> viewport_depth_far_plane; // float24 72 BitField<0, 24, u32> viewport_depth_far_plane; // float24
73 73
74 INSERT_PADDING_WORDS(0x1); 74 BitField<0, 3, u32> vs_output_total;
75 75
76 union VSOutputAttributes { 76 union VSOutputAttributes {
77 // Maps components of output vertex attributes to semantics 77 // Maps components of output vertex attributes to semantics
@@ -1123,7 +1123,12 @@ struct Regs {
1123 BitField<24, 8, u32> w; 1123 BitField<24, 8, u32> w;
1124 } int_uniforms[4]; 1124 } int_uniforms[4];
1125 1125
1126 INSERT_PADDING_WORDS(0x5); 1126 INSERT_PADDING_WORDS(0x4);
1127
1128 union {
1129 // Number of input attributes to shader unit - 1
1130 BitField<0, 4, u32> num_input_attributes;
1131 };
1127 1132
1128 // Offset to shader program entry point (in words) 1133 // Offset to shader program entry point (in words)
1129 BitField<0, 16, u32> main_offset; 1134 BitField<0, 16, u32> main_offset;
@@ -1157,8 +1162,10 @@ struct Regs {
1157 } 1162 }
1158 } input_register_map; 1163 } input_register_map;
1159 1164
1160 // OUTMAP_MASK, 0x28E, CODETRANSFER_END 1165 BitField<0, 16, u32> output_mask;
1161 INSERT_PADDING_WORDS(0x3); 1166
1167 // 0x28E, CODETRANSFER_END
1168 INSERT_PADDING_WORDS(0x2);
1162 1169
1163 struct { 1170 struct {
1164 enum Format : u32 1171 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 {
12 12
13/// Struct used to describe current Pica state 13/// Struct used to describe current Pica state
14struct State { 14struct State {
15 void Reset();
16
15 /// Pica registers 17 /// Pica registers
16 Regs regs; 18 Regs regs;
17 19
@@ -46,13 +48,14 @@ struct State {
46 48
47 /// Struct used to describe immediate mode rendering state 49 /// Struct used to describe immediate mode rendering state
48 struct ImmediateModeState { 50 struct ImmediateModeState {
49 Shader::InputVertex input; 51 // Used to buffer partial vertices for immediate-mode rendering.
50 // This is constructed with a dummy triangle topology 52 Shader::InputVertex input_vertex;
51 PrimitiveAssembler<Shader::OutputVertex> primitive_assembler; 53 // Index of the next attribute to be loaded into `input_vertex`.
52 int attribute_id = 0; 54 int current_attribute = 0;
53
54 ImmediateModeState() : primitive_assembler(Regs::TriangleTopology::List) {}
55 } immediate; 55 } immediate;
56
57 // This is constructed with a dummy triangle topology
58 PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
56}; 59};
57 60
58extern State g_state; ///< Current Pica state 61extern 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 {
20 VertexType& v1, 20 VertexType& v1,
21 VertexType& v2)>; 21 VertexType& v2)>;
22 22
23 PrimitiveAssembler(Regs::TriangleTopology topology); 23 PrimitiveAssembler(Regs::TriangleTopology topology = Regs::TriangleTopology::List);
24 24
25 /* 25 /*
26 * Queues a vertex, builds primitives from the vertex queue according to the given 26 * 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,
190} 190}
191 191
192void RasterizerOpenGL::DrawTriangles() { 192void RasterizerOpenGL::DrawTriangles() {
193 if (vertex_batch.empty())
194 return;
195
193 SyncFramebuffer(); 196 SyncFramebuffer();
194 SyncDrawState(); 197 SyncDrawState();
195 198
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
121 OutputVertex ret; 121 OutputVertex ret;
122 // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to 122 // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
123 // figure out what those circumstances are and enable the remaining outputs then. 123 // figure out what those circumstances are and enable the remaining outputs then.
124 for (int i = 0; i < 7; ++i) { 124 unsigned index = 0;
125 const auto& output_register_map = g_state.regs.vs_output_attributes[i]; // TODO: Don't hardcode VS here 125 for (unsigned i = 0; i < 7; ++i) {
126
127 if (index >= g_state.regs.vs_output_total)
128 break;
129
130 if ((g_state.regs.vs.output_mask & (1 << i)) == 0)
131 continue;
132
133 const auto& output_register_map = g_state.regs.vs_output_attributes[index]; // TODO: Don't hardcode VS here
126 134
127 u32 semantics[4] = { 135 u32 semantics[4] = {
128 output_register_map.map_x, output_register_map.map_y, 136 output_register_map.map_x, output_register_map.map_y,
129 output_register_map.map_z, output_register_map.map_w 137 output_register_map.map_z, output_register_map.map_w
130 }; 138 };
131 139
132 for (int comp = 0; comp < 4; ++comp) { 140 for (unsigned comp = 0; comp < 4; ++comp) {
133 float24* out = ((float24*)&ret) + semantics[comp]; 141 float24* out = ((float24*)&ret) + semantics[comp];
134 if (semantics[comp] != Regs::VSOutputAttributes::INVALID) { 142 if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
135 *out = state.registers.output[i][comp]; 143 *out = state.registers.output[i][comp];
@@ -139,10 +147,12 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
139 memset(out, 0, sizeof(*out)); 147 memset(out, 0, sizeof(*out));
140 } 148 }
141 } 149 }
150
151 index++;
142 } 152 }
143 153
144 // The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation 154 // The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation
145 for (int i = 0; i < 4; ++i) { 155 for (unsigned i = 0; i < 4; ++i) {
146 ret.color[i] = float24::FromFloat32( 156 ret.color[i] = float24::FromFloat32(
147 std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f)); 157 std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
148 } 158 }
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp
index 02e1a1cb1..9b978583e 100644
--- a/src/video_core/shader/shader_interpreter.cpp
+++ b/src/video_core/shader/shader_interpreter.cpp
@@ -2,10 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <common/file_util.h> 5#include <numeric>
6
7#include <nihstro/shader_bytecode.h> 6#include <nihstro/shader_bytecode.h>
8 7
8#include "common/file_util.h"
9#include "video_core/pica.h" 9#include "video_core/pica.h"
10#include "video_core/pica_state.h" 10#include "video_core/pica_state.h"
11#include "video_core/shader/shader.h" 11#include "video_core/shader/shader.h"
@@ -214,10 +214,8 @@ void RunInterpreter(UnitState<Debug>& state) {
214 if (opcode == OpCode::Id::DPH || opcode == OpCode::Id::DPHI) 214 if (opcode == OpCode::Id::DPH || opcode == OpCode::Id::DPHI)
215 src1[3] = float24::FromFloat32(1.0f); 215 src1[3] = float24::FromFloat32(1.0f);
216 216
217 float24 dot = float24::FromFloat32(0.f);
218 int num_components = (opcode == OpCode::Id::DP3) ? 3 : 4; 217 int num_components = (opcode == OpCode::Id::DP3) ? 3 : 4;
219 for (int i = 0; i < num_components; ++i) 218 float24 dot = std::inner_product(src1, src1 + num_components, src2, float24::FromFloat32(0.f));
220 dot = dot + src1[i] * src2[i];
221 219
222 for (int i = 0; i < 4; ++i) { 220 for (int i = 0; i < 4; ++i) {
223 if (!swizzle.DestComponentEnabled(i)) 221 if (!swizzle.DestComponentEnabled(i))