| 472 | }; |
| 473 | |
| 474 | static void SaveObject( |
| 475 | std::string_view path, const RCTObjectEntry* entry, const void* data, size_t dataSize, bool fixChecksum = true) |
| 476 | { |
| 477 | if (fixChecksum) |
| 478 | { |
| 479 | uint32_t realChecksum = ObjectCalculateChecksum(entry, data, dataSize); |
| 480 | if (realChecksum != entry->checksum) |
| 481 | { |
| 482 | char objectName[9]; |
| 483 | ObjectEntryGetNameFixed(objectName, sizeof(objectName), entry); |
| 484 | LOG_VERBOSE("[%s] Incorrect checksum, adding salt bytes...", objectName); |
| 485 | |
| 486 | // Calculate the value of extra bytes that can be appended to the data so that the |
| 487 | // data is then valid for the object's checksum |
| 488 | const auto extraBytes = calculateExtraBytesToFixChecksum(realChecksum, entry->checksum); |
| 489 | |
| 490 | // Create new data blob with appended bytes |
| 491 | std::vector<uint8_t> newData; |
| 492 | newData.reserve(dataSize + extraBytes.size()); |
| 493 | |
| 494 | const auto dataSpan = std::span(static_cast<const uint8_t*>(data), dataSize); |
| 495 | newData.insert(newData.end(), dataSpan.begin(), dataSpan.end()); |
| 496 | newData.insert(newData.end(), extraBytes.begin(), extraBytes.end()); |
| 497 | |
| 498 | uint32_t newRealChecksum = ObjectCalculateChecksum(entry, newData.data(), newData.size()); |
| 499 | if (newRealChecksum != entry->checksum) |
| 500 | { |
| 501 | Console::Error::WriteLine("CalculateExtraBytesToFixChecksum failed to fix checksum."); |
| 502 | |
| 503 | // Save old data form |
| 504 | SaveObject(path, entry, data, dataSize, false); |
| 505 | } |
| 506 | else |
| 507 | { |
| 508 | // Save new data form |
| 509 | SaveObject(path, entry, newData.data(), newData.size(), false); |
| 510 | } |
| 511 | return; |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | // Encode data |
| 516 | ObjectType objectType = entry->GetType(); |
| 517 | ChunkHeader chunkHeader; |
| 518 | chunkHeader.encoding = kLegacyObjectEntryGroupEncoding[EnumValue(objectType)]; |
| 519 | chunkHeader.length = static_cast<uint32_t>(dataSize); |
| 520 | const auto encodedDataBuffer = std::make_unique_for_overwrite<uint8_t[]>(0x600000); |
| 521 | const size_t encodedDataSize = WriteChunkBuffer( |
| 522 | encodedDataBuffer.get(), reinterpret_cast<const uint8_t*>(data), chunkHeader); |
| 523 | |
| 524 | // Save to file |
| 525 | auto fs = FileStream(std::string(path), FileMode::write); |
| 526 | fs.Write(entry, sizeof(RCTObjectEntry)); |
| 527 | fs.Write(encodedDataBuffer.get(), encodedDataSize); |
| 528 | } |
| 529 | |
| 530 | static std::array<uint8_t, 11> calculateExtraBytesToFixChecksum( |
| 531 | const int32_t currentChecksum, const int32_t targetChecksum) |
nothing calls this directly
no test coverage detected