| 455 | } |
| 456 | |
| 457 | static int uncompressFile(const CommandLineOptions& options) |
| 458 | { |
| 459 | using namespace S5; |
| 460 | |
| 461 | if (options.path.empty()) |
| 462 | { |
| 463 | Logging::error("No file specified."); |
| 464 | return EXIT_FAILURE; |
| 465 | } |
| 466 | |
| 467 | try |
| 468 | { |
| 469 | auto path = fs::u8path(options.path); |
| 470 | |
| 471 | // Copy the whole input file into memory to allow writing over input file |
| 472 | MemoryStream ms; |
| 473 | { |
| 474 | FileStream fsInput(path, StreamMode::read); |
| 475 | ms.resize(fsInput.getLength()); |
| 476 | fsInput.read(ms.data(), fsInput.getLength()); |
| 477 | } |
| 478 | SawyerStreamReader reader(ms); |
| 479 | |
| 480 | auto outputPath = options.outputPath; |
| 481 | if (outputPath.empty()) |
| 482 | { |
| 483 | outputPath = options.path; |
| 484 | } |
| 485 | |
| 486 | FileStream fsOutput(outputPath, StreamMode::write); |
| 487 | SawyerStreamWriter writer(fsOutput); |
| 488 | |
| 489 | if (!reader.validateChecksum()) |
| 490 | { |
| 491 | throw Exception::RuntimeError("Invalid checksum"); |
| 492 | } |
| 493 | |
| 494 | // Header chunk |
| 495 | Header header; |
| 496 | reader.readChunk(&header, sizeof(header)); |
| 497 | writer.writeChunk(SawyerEncoding::uncompressed, header); |
| 498 | |
| 499 | // Optional save details chunk |
| 500 | if (header.hasFlags(HeaderFlags::hasSaveDetails)) |
| 501 | { |
| 502 | auto saveDetails = std::make_unique<SaveDetails>(); |
| 503 | reader.readChunk(saveDetails.get(), sizeof(SaveDetails)); |
| 504 | writer.writeChunk(SawyerEncoding::uncompressed, *saveDetails); |
| 505 | } |
| 506 | |
| 507 | // Packed objects |
| 508 | for (auto i = 0; i < header.numPackedObjects; i++) |
| 509 | { |
| 510 | ObjectHeader object; |
| 511 | reader.read(&object, sizeof(ObjectHeader)); |
| 512 | writer.write(&object, sizeof(ObjectHeader)); |
| 513 | |
| 514 | auto chunk = reader.readChunk(); |
no test coverage detected