| 184 | } |
| 185 | |
| 186 | Result io::copyFile( |
| 187 | FS_Archive srcArch, FS_Archive dstArch, const std::u16string& srcPath, const std::u16string& dstPath, ProgressSink& sink, u8* buffer) |
| 188 | { |
| 189 | u32 size = 0; |
| 190 | FSStream input(srcArch, srcPath, FS_OPEN_READ); |
| 191 | if (input.good()) { |
| 192 | size = input.size() > BUFFER_SIZE ? BUFFER_SIZE : input.size(); |
| 193 | } |
| 194 | else { |
| 195 | Logging::error("Failed to open source file {} during copy with result {}.", StringUtils::UTF16toUTF8(srcPath), input.result()); |
| 196 | return input.result(); |
| 197 | } |
| 198 | |
| 199 | FSStream output(dstArch, dstPath, FS_OPEN_WRITE, input.size()); |
| 200 | if (!output.good()) { |
| 201 | Logging::error("Failed to open destination file {} during copy with result {}.", StringUtils::UTF16toUTF8(dstPath), output.result()); |
| 202 | input.close(); |
| 203 | return output.result(); |
| 204 | } |
| 205 | |
| 206 | size_t slashpos = srcPath.rfind(StringUtils::UTF8toUTF16("/")); |
| 207 | sink.startFile(srcPath.substr(slashpos + 1, srcPath.length() - slashpos - 1), input.size()); |
| 208 | |
| 209 | Result res = 0; |
| 210 | u32 offset = 0; |
| 211 | u32 chunk = size; // shrinks toward the readable-data boundary on RES_FS_PAST_DATA |
| 212 | do { |
| 213 | if (sink.cancelled()) { |
| 214 | break; |
| 215 | } |
| 216 | u32 rd = input.read(buffer, chunk); |
| 217 | if (R_FAILED(input.result())) { |
| 218 | // Sparse extdata: this offset+chunk runs past the file's real data. Halve |
| 219 | // the request to recover any readable prefix at this offset; once even a |
| 220 | // single byte can't be read here, we've hit the true end of data — stop |
| 221 | // this file cleanly and keep the backup going. |
| 222 | if ((u32)input.result() == RES_FS_PAST_DATA) { |
| 223 | if (chunk > 1) { |
| 224 | chunk /= 2; |
| 225 | continue; |
| 226 | } |
| 227 | Logging::info("Reached end of readable data for {} at offset {} (sparse extdata); backing up {} bytes.", |
| 228 | StringUtils::UTF16toUTF8(srcPath), offset, offset); |
| 229 | break; |
| 230 | } |
| 231 | res = input.result(); |
| 232 | Logging::error("Read failure during copy of {} with result 0x{:08X}.", StringUtils::UTF16toUTF8(srcPath), (u32)res); |
| 233 | break; |
| 234 | } |
| 235 | if (rd == 0) { |
| 236 | break; |
| 237 | } |
| 238 | u32 wt = output.write(buffer, rd); |
| 239 | if (R_FAILED(output.result())) { |
| 240 | res = output.result(); |
| 241 | Logging::error("Write failure during copy of {} with result 0x{:08X}.", StringUtils::UTF16toUTF8(dstPath), (u32)res); |
| 242 | break; |
| 243 | } |
nothing calls this directly
no test coverage detected