Files
FEX-Emu--FEX/Source/Tools/FEXRootFSFetcher/XXFileHash.cpp
T
Ryan Houdek 2c31080fd4 FEXRootFSFetcher: Adds a new tool to help set up a new RootFS
This tool supports both a zenity and tty interface.
TTY will be presented if available while Zenity will be used otherwise.

This tool pulls a rootfs list from https://rootfs.fex-emu.org/
It then allows you to select a rootfs from the list, download it, place
it in to the correct working folder, extract it if desired, and set it
as the current default RootFS.

This requires curl and potentially zenity installed to use.
Maybe also unsquashfs if the user chooses to extract the image.

This is an all in one tool to quickly get a new user up and running.

Additionally if you pass in a file path in to the tool, it will generate
an xxhash of the file and exit out. This is the hash used to ensure the
files are valid.
2021-12-24 13:38:20 -08:00

85 lines
2.1 KiB
C++

#include "XXFileHash.h"
#include <chrono>
#include <fcntl.h>
#include <fmt/format.h>
#include <unistd.h>
#include <vector>
#include <xxhash.h>
namespace XXFileHash {
// 32MB blocks
constexpr static size_t BLOCK_SIZE = 32 * 1024 * 1024;
std::pair<bool, uint64_t> HashFile(const std::string &Filepath) {
int fd = open(Filepath.c_str(), O_RDONLY);
if (fd == -1) {
return {false, 0};
}
auto HadError = [fd]() -> std::pair<bool, uint64_t> {
close(fd);
return {false, 0};
};
// Get file size
off_t Size = lseek(fd, 0, SEEK_END);
double SizeD = Size;
// Reset to beginning
lseek(fd, 0, SEEK_SET);
// Set up XXHash state
XXH64_state_t* const State = XXH64_createState();
XXH64_hash_t const Seed = 0;
if (!State) {
return HadError();
}
if (XXH64_reset(State, Seed) == XXH_ERROR) {
return HadError();
}
std::vector<char> Data(BLOCK_SIZE);
off_t DataRemaining = Size - BLOCK_SIZE;
off_t DataTail = Size - DataRemaining;
off_t CurrentOffset = 0;
auto Now = std::chrono::high_resolution_clock::now();
// Let the kernel know that we will be reading linearly
posix_fadvise(fd, 0, Size, POSIX_FADV_SEQUENTIAL);
while (CurrentOffset < DataRemaining) {
ssize_t Result = pread(fd, Data.data(), BLOCK_SIZE, CurrentOffset);
if (Result == -1) {
return HadError();
}
if (XXH64_update(State, Data.data(), BLOCK_SIZE) == XXH_ERROR) {
return HadError();
}
auto Cur = std::chrono::high_resolution_clock::now();
auto Dur = Cur - Now;
if (Dur >= std::chrono::seconds(5)) {
fmt::print("{}% hashed\n", (double)CurrentOffset / SizeD);
Now = Cur;
}
CurrentOffset += BLOCK_SIZE;
}
// Finish the tail
ssize_t Result = pread(fd, Data.data(), DataTail, CurrentOffset);
if (Result == -1) {
return HadError();
}
if (XXH64_update(State, Data.data(), DataTail) == XXH_ERROR) {
return HadError();
}
XXH64_hash_t const Hash = XXH64_digest(State);
XXH64_freeState(State);
close(fd);
return {true, Hash};
}
}