UnleashedRecomp/tools/bc_diff/bc_diff.cpp
Skyth (Asilkan) 67633917bf
Linux support. (#54)
* Initial Linux attempt.

* Add clang toolchain & make tools compile.

* vcpkg as submodule.

* First implementation of IO rewrite. (#31)

* Fix directory iteration resolving symlinks.

* Refactor kernel objects to be lock-free.

* Implement guest critical sections using std::atomic.

* Make D3D12 support optional. (#33)

* Make D3D12 support optional.

* Update ShaderRecomp, fix macros.

* Replace QueryPerformanceCounter. (#35)

* Add Linux home path for GetUserPath(). (#36)

* Cross-platform Sleep. (#37)

* Add mmap implementations for virtual allocation. (#38)

* Cross-platform TLS. (#34)

* Cross-platform TLS.

* Fix front() to back(), use Mutex.

* Fix global variable namings.

---------

Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com>

* Unicode support. (#39)

* Replace CreateDirectoryA with Unicode version.

* Cross platform thread implementation. (#41)

* Cross-platform thread implementation.

* Put set thread name calls behind a Win32 macro.

* Cross-platform semaphore implementation. (#43)

* xam: use SDL for keyboard input

* Cross-platform atomic operations. (#44)

* Cross-platform spin lock implementation.

* Cross-platform reference counting.

* Cross-platform event implementation. (#47)

* Compiling and running on Linux. (#49)

* Current work trying to get it to compile.

* Update vcpkg.json baseline.

* vcpkg, memory mapped file.

* Bitscan forward.

* Fix localtime_s.

* FPS patches high res clock.

* Rename Window to GameWindow. Fix guest pointers.

* GetCurrentThreadID gone.

* Code cache pointers, RenderWindow type.

* Add Linux stubs.

* Refactor Config.

* Fix paths.

* Add linux-release config.

* FS fixes.

* Fix Windows compilation errors & unicode converter crash.

* Rename physical memory allocation functions to not clash with X11.

* Fix NULL character being added on RtlMultiByteToUnicodeN.

* Use std::exit.

* Add protection to memory on Linux.

* Convert majority of dependencies to submodules. (#48)

* Convert majority of dependencies to submodules.

* Don't compile header-only libraries.

* Fix a few incorrect data types.

* Fix config directory.

* Unicode fixes & sizeof asserts.

* Change the exit function to not call static destructors.

* Fix files picker.

* Add RelWithDebInfo preset for Linux.

* Implement OS Restart on Linux. (#50)

---------

Co-authored-by: Dario <dariosamo@gmail.com>

* Update PowerRecomp.

* Add Env Var detection for VCPKG_ROOT, add DLC detection.

* Use error code version on DLC directory iterator.

* Set D3D12MA::ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED flag.

* Linux flatpak. (#51)

* Add flatpak support.

* Add game install directory override for flatpak.

* Flatpak'ing.

* Flatpak it some more.

* We flat it, we pak it.

* Flatpak'd.

* The Marvelous Misadventures of Flatpak.

* Attempt to change logic of NFD and show error.

* Flattenpakken.

* Use game install directory instead of current path.

* Attempt to fix line endings.

* Update io.github.hedge_dev.unleashedrecomp.json

* Fix system time query implementation.

* Add Present Wait to Vulkan to improve frame pacing and reduce latency. (#53)

* Add present wait support to Vulkan.

* Default to triple buffering if presentWait is supported.

* Bracey fellas.

* Update paths.h

* SDL2 audio (again). (#52)

* Implement SDL2 audio (again).

* Call timeBeginPeriod/timeEndPeriod.

* Replace miniaudio with SDL mixer.

* Queue audio samples in a separate thread.

* Enable CMake option override policy & fix compilation error.

* Fix compilation error on Linux.

* Fix but also trim shared strings.

* Wayland support. (#55)

* Make channel index a global variable in embedded player.

* Fix SDL Audio selection for OGG on Flatpak.

* Minor installer wizard fixes.

* Fix compilation error.

* Yield in model consumer and pipeline compiler threads.

* Special case Sleep(0) to yield on Linux.

* Add App Id hint.

* Correct implementation for auto reset events. (#57)

---------

Co-authored-by: Dario <dariosamo@gmail.com>
Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
2024-12-21 00:44:05 +03:00

144 lines
4.6 KiB
C++

#include "bc_diff.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <filesystem>
#include <vector>
#include <xxhash.h>
static std::vector<uint8_t> readAllBytes(const char* filePath)
{
FILE* file = fopen(filePath, "rb");
if (!file)
return {};
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
std::vector<uint8_t> data(fileSize);
fread(data.data(), 1, fileSize, file);
fclose(file);
return data;
}
int main(int argc, char** argv)
{
if (argc != 4)
{
printf("Usage: %s [old directory] [new directory] [destination file]", argv[0]);
return 0;
}
// Debug configuration doesn't compile without this???
assert(argc == 4);
std::filesystem::path oldDirectoryPath = argv[1];
std::filesystem::path newDirectoryPath = argv[2];
std::vector<BlockCompressionDiffPatchEntry> entries;
std::vector<BlockCompressionDiffPatch> patches;
std::vector<uint8_t> patchBytes;
for (auto& oldFile : std::filesystem::recursive_directory_iterator(oldDirectoryPath))
{
auto newFile = newDirectoryPath / std::filesystem::relative(oldFile, oldDirectoryPath);
if (!std::filesystem::exists(newFile))
{
fprintf(stderr, "Cannot locate %s\n", newFile.string().c_str());
continue;
}
auto oldFileData = readAllBytes(oldFile.path().string().c_str());
auto newFileData = readAllBytes(newFile.string().c_str());
constexpr size_t BC_STRIDE = 8;
if (oldFileData.size() != newFileData.size())
{
fprintf(stderr, "%s does not match %s in file size\n", oldFile.path().string().c_str(), newFile.string().c_str());
continue;
}
if ((oldFileData.size() % BC_STRIDE) != 0)
{
fprintf(stderr, "%s is not aligned to %d bytes\n", oldFile.path().string().c_str(), BC_STRIDE);
continue;
}
if (oldFileData.size() >= BC_STRIDE)
{
size_t patchIndex = patches.size();
for (size_t i = 0; i < oldFileData.size() - BC_STRIDE + 1; i += BC_STRIDE)
{
if (memcmp(&oldFileData[i], &newFileData[i], BC_STRIDE) == 0)
continue;
size_t patchBytesOffset = patchBytes.size();
patchBytes.insert(patchBytes.end(), newFileData.begin() + i, newFileData.begin() + i + BC_STRIDE);
if (patchIndex >= patches.size() || ((patches.back().destinationOffset + patches.back().patchBytesSize) != i))
{
auto& patch = patches.emplace_back();
patch.destinationOffset = i;
patch.patchBytesOffset = patchBytesOffset;
patch.patchBytesSize = BC_STRIDE;
}
else
{
patches.back().patchBytesSize += BC_STRIDE;
}
}
size_t patchCount = patches.size() - patchIndex;
if (patchCount != 0)
{
auto& entry = entries.emplace_back();
entry.hash = XXH3_64bits(oldFileData.data(), oldFileData.size());
entry.patchesOffset = patchIndex * sizeof(BlockCompressionDiffPatch);
entry.patchCount = patchCount;
printf("Generated BC patch for %s\n", oldFile.path().string().c_str());
}
else
{
printf("Skipping %s, files are identical\n", oldFile.path().string().c_str());
}
}
}
std::sort(entries.begin(), entries.end(), [](auto& lhs, auto& rhs) { return lhs.hash < rhs.hash; });
BlockCompressionDiffPatchHeader header;
header.entriesOffset = sizeof(BlockCompressionDiffPatchHeader);
header.entryCount = entries.size();
size_t patchesOffset = header.entriesOffset + sizeof(BlockCompressionDiffPatchEntry) * entries.size();
size_t patchBytesOffset = patchesOffset + sizeof(BlockCompressionDiffPatch) * patches.size();
for (auto& entry : entries)
entry.patchesOffset += patchesOffset;
for (auto& patch : patches)
patch.patchBytesOffset += patchBytesOffset;
FILE* file = fopen(argv[3], "wb");
if (!file)
{
fprintf(stderr, "Cannot open %s for writing\n", argv[3]);
return 1;
}
fwrite(&header, sizeof(header), 1, file);
fwrite(entries.data(), sizeof(BlockCompressionDiffPatchEntry), entries.size(), file);
fwrite(patches.data(), sizeof(BlockCompressionDiffPatch), patches.size(), file);
fwrite(patchBytes.data(), 1, patchBytes.size(), file);
fclose(file);
return 0;
}