| 110 | } |
| 111 | |
| 112 | Result io::copyFile( |
| 113 | FS_Archive srcArch, FS_Archive dstArch, const std::u16string& srcPath, const std::u16string& dstPath, ProgressSink& sink, u8* buffer) |
| 114 | { |
| 115 | u32 size = 0; |
| 116 | FSStream input(srcArch, srcPath, FS_OPEN_READ); |
| 117 | if (input.good()) { |
| 118 | size = input.size() > BUFFER_SIZE ? BUFFER_SIZE : input.size(); |
| 119 | } |
| 120 | else { |
| 121 | Logging::error("Failed to open source file {} during copy with result {}.", StringUtils::UTF16toUTF8(srcPath), input.result()); |
| 122 | return input.result(); |
| 123 | } |
| 124 | |
| 125 | FSStream output(dstArch, dstPath, FS_OPEN_WRITE, input.size()); |
| 126 | if (!output.good()) { |
| 127 | Logging::error("Failed to open destination file {} during copy with result {}.", StringUtils::UTF16toUTF8(dstPath), output.result()); |
| 128 | input.close(); |
| 129 | return output.result(); |
| 130 | } |
| 131 | |
| 132 | size_t slashpos = srcPath.rfind(StringUtils::UTF8toUTF16("/")); |
| 133 | sink.startFile(srcPath.substr(slashpos + 1, srcPath.length() - slashpos - 1), input.size()); |
| 134 | |
| 135 | Result res = 0; |
| 136 | u32 offset = 0; |
| 137 | u32 chunk = size; // shrinks toward the readable-data boundary on RES_FS_PAST_DATA |
| 138 | do { |
| 139 | if (sink.cancelled()) { |
| 140 | break; |
| 141 | } |
| 142 | u32 rd = input.read(buffer, chunk); |
| 143 | if (R_FAILED(input.result())) { |
| 144 | // Sparse extdata: this offset+chunk runs past the file's real data. Halve |
| 145 | // the request to recover any readable prefix at this offset; once even a |
| 146 | // single byte can't be read here, we've hit the true end of data — stop |
| 147 | // this file cleanly and keep the backup going. |
| 148 | if ((u32)input.result() == RES_FS_PAST_DATA) { |
| 149 | if (chunk > 1) { |
| 150 | chunk /= 2; |
| 151 | continue; |
| 152 | } |
| 153 | Logging::info("Reached end of readable data for {} at offset {} (sparse extdata); backing up {} bytes.", |
| 154 | StringUtils::UTF16toUTF8(srcPath), offset, offset); |
| 155 | break; |
| 156 | } |
| 157 | res = input.result(); |
| 158 | Logging::error("Read failure during copy of {} with result 0x{:08X}.", StringUtils::UTF16toUTF8(srcPath), (u32)res); |
| 159 | break; |
| 160 | } |
| 161 | if (rd == 0) { |
| 162 | break; |
| 163 | } |
| 164 | u32 wt = output.write(buffer, rd); |
| 165 | if (R_FAILED(output.result())) { |
| 166 | res = output.result(); |
| 167 | Logging::error("Write failure during copy of {} with result 0x{:08X}.", StringUtils::UTF16toUTF8(dstPath), (u32)res); |
| 168 | break; |
| 169 | } |
nothing calls this directly
no test coverage detected