mirror of
				https://github.com/hedge-dev/XenonRecomp.git
				synced 2025-11-04 06:47:09 +00:00 
			
		
		
		
	Port XEX patcher from Unleashed Recompiled. (#4)
* Port XEX patcher from Unleashed Recompiled. * Fix compilation error on Linux.
This commit is contained in:
		
							
								
								
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -7,3 +7,9 @@
 | 
			
		||||
[submodule "thirdparty/tomlplusplus"]
 | 
			
		||||
	path = thirdparty/tomlplusplus
 | 
			
		||||
	url = https://github.com/marzer/tomlplusplus.git
 | 
			
		||||
[submodule "thirdparty/libmspack"]
 | 
			
		||||
	path = thirdparty/libmspack
 | 
			
		||||
	url = https://github.com/kyz/libmspack
 | 
			
		||||
[submodule "thirdparty/tiny-AES-c"]
 | 
			
		||||
	path = thirdparty/tiny-AES-c
 | 
			
		||||
	url = https://github.com/kokke/tiny-AES-c.git
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,9 @@ int main(int argc, char* argv[])
 | 
			
		||||
    if (std::filesystem::is_regular_file(path))
 | 
			
		||||
    {
 | 
			
		||||
        Recompiler recompiler;
 | 
			
		||||
        recompiler.LoadConfig(path);
 | 
			
		||||
        if (!recompiler.LoadConfig(path))
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        recompiler.Analyse();
 | 
			
		||||
 | 
			
		||||
        auto entry = recompiler.image.symbols.find(recompiler.image.entry_point);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include "pch.h"
 | 
			
		||||
#include "recompiler.h"
 | 
			
		||||
#include <xex_patcher.h>
 | 
			
		||||
 | 
			
		||||
static uint64_t ComputeMask(uint32_t mstart, uint32_t mstop)
 | 
			
		||||
{
 | 
			
		||||
@@ -9,12 +10,87 @@ static uint64_t ComputeMask(uint32_t mstart, uint32_t mstop)
 | 
			
		||||
    return mstart <= mstop ? value : ~value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Recompiler::LoadConfig(const std::string_view& configFilePath)
 | 
			
		||||
bool Recompiler::LoadConfig(const std::string_view& configFilePath)
 | 
			
		||||
{
 | 
			
		||||
    config.Load(configFilePath);
 | 
			
		||||
 | 
			
		||||
    const auto file = LoadFile((config.directoryPath + config.filePath).c_str());
 | 
			
		||||
    std::vector<uint8_t> file;
 | 
			
		||||
    if (!config.patchedFilePath.empty())
 | 
			
		||||
        file = LoadFile((config.directoryPath + config.patchedFilePath).c_str());
 | 
			
		||||
 | 
			
		||||
    if (file.empty())
 | 
			
		||||
    {
 | 
			
		||||
        file = LoadFile((config.directoryPath + config.filePath).c_str());
 | 
			
		||||
 | 
			
		||||
        if (!config.patchFilePath.empty())
 | 
			
		||||
        {
 | 
			
		||||
            const auto patchFile = LoadFile((config.directoryPath + config.patchFilePath).c_str());
 | 
			
		||||
            if (!patchFile.empty())
 | 
			
		||||
            {
 | 
			
		||||
                std::vector<uint8_t> outBytes;
 | 
			
		||||
                auto result = XexPatcher::apply(file.data(), file.size(), patchFile.data(), patchFile.size(), outBytes, false);
 | 
			
		||||
                if (result == XexPatcher::Result::Success)
 | 
			
		||||
                {
 | 
			
		||||
                    std::exchange(file, outBytes);
 | 
			
		||||
 | 
			
		||||
                    if (!config.patchedFilePath.empty())
 | 
			
		||||
                    {
 | 
			
		||||
                        std::ofstream stream(config.directoryPath + config.patchedFilePath, std::ios::binary);
 | 
			
		||||
                        if (stream.good())
 | 
			
		||||
                        {
 | 
			
		||||
                            stream.write(reinterpret_cast<const char*>(file.data()), file.size());
 | 
			
		||||
                            stream.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    fmt::print("ERROR: Unable to apply the patch file, ");
 | 
			
		||||
 | 
			
		||||
                    switch (result)
 | 
			
		||||
                    {
 | 
			
		||||
                    case XexPatcher::Result::XexFileUnsupported:
 | 
			
		||||
                        fmt::println("XEX file unsupported");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case XexPatcher::Result::XexFileInvalid:
 | 
			
		||||
                        fmt::println("XEX file invalid");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case XexPatcher::Result::PatchFileInvalid:
 | 
			
		||||
                        fmt::println("patch file invalid");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case XexPatcher::Result::PatchIncompatible:
 | 
			
		||||
                        fmt::println("patch file incompatible");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case XexPatcher::Result::PatchFailed:
 | 
			
		||||
                        fmt::println("patch failed");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case XexPatcher::Result::PatchUnsupported:
 | 
			
		||||
                        fmt::println("patch unsupported");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    default:
 | 
			
		||||
                        fmt::println("reason unknown");
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                fmt::println("ERROR: Unable to load the patch file");
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    image = Image::ParseImage(file.data(), file.size());
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Recompiler::Analyse()
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ struct Recompiler
 | 
			
		||||
    size_t cppFileIndex = 0;
 | 
			
		||||
    RecompilerConfig config;
 | 
			
		||||
 | 
			
		||||
    void LoadConfig(const std::string_view& configFilePath);
 | 
			
		||||
    bool LoadConfig(const std::string_view& configFilePath);
 | 
			
		||||
 | 
			
		||||
    template<class... Args>
 | 
			
		||||
    void print(fmt::format_string<Args...> fmt, Args&&... args)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ void RecompilerConfig::Load(const std::string_view& configFilePath)
 | 
			
		||||
    {
 | 
			
		||||
        const auto& main = *mainPtr;
 | 
			
		||||
        filePath = main["file_path"].value_or<std::string>("");
 | 
			
		||||
        patchFilePath = main["patch_file_path"].value_or<std::string>("");
 | 
			
		||||
        patchedFilePath = main["patched_file_path"].value_or<std::string>("");
 | 
			
		||||
        outDirectoryPath = main["out_directory_path"].value_or<std::string>("");
 | 
			
		||||
        switchTableFilePath = main["switch_table_file_path"].value_or<std::string>("");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,8 @@ struct RecompilerConfig
 | 
			
		||||
{
 | 
			
		||||
    std::string directoryPath;
 | 
			
		||||
    std::string filePath;
 | 
			
		||||
    std::string patchFilePath;
 | 
			
		||||
    std::string patchedFilePath;
 | 
			
		||||
    std::string outDirectoryPath;
 | 
			
		||||
    std::string switchTableFilePath;
 | 
			
		||||
    std::unordered_map<uint32_t, RecompilerSwitchTable> switchTables;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,28 @@ add_library(XenonUtils
 | 
			
		||||
    "disasm.cpp" 
 | 
			
		||||
    "xex.cpp" 
 | 
			
		||||
    "image.cpp" 
 | 
			
		||||
    "xdbf_wrapper.cpp")
 | 
			
		||||
    "xdbf_wrapper.cpp"
 | 
			
		||||
    "xex_patcher.cpp"
 | 
			
		||||
    "memory_mapped_file.cpp"
 | 
			
		||||
    "${THIRDPARTY_ROOT}/libmspack/libmspack/mspack/lzxd.c"
 | 
			
		||||
    "${THIRDPARTY_ROOT}/tiny-AES-c/aes.c"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_include_directories(XenonUtils PUBLIC .)
 | 
			
		||||
target_link_libraries(XenonUtils PUBLIC disasm)
 | 
			
		||||
target_compile_definitions(XenonUtils
 | 
			
		||||
    PRIVATE
 | 
			
		||||
        NOMINMAX
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_include_directories(XenonUtils 
 | 
			
		||||
    PUBLIC 
 | 
			
		||||
        .
 | 
			
		||||
    PRIVATE
 | 
			
		||||
        "${THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
 | 
			
		||||
        "${THIRDPARTY_ROOT}/tiny-AES-c"
 | 
			
		||||
        "${THIRDPARTY_ROOT}/TinySHA1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(XenonUtils 
 | 
			
		||||
    PUBLIC
 | 
			
		||||
        disasm
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ Image Image::ParseImage(const uint8_t* data, size_t size)
 | 
			
		||||
    }
 | 
			
		||||
    else if (data[0] == 'X' && data[1] == 'E' && data[2] == 'X' && data[3] == '2')
 | 
			
		||||
    {
 | 
			
		||||
        return Xex2LoadImage(data);
 | 
			
		||||
        return Xex2LoadImage(data, size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										169
									
								
								XenonUtils/memory_mapped_file.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								XenonUtils/memory_mapped_file.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
#include "memory_mapped_file.h"
 | 
			
		||||
 | 
			
		||||
#if !defined(_WIN32)
 | 
			
		||||
#   include <cstring>
 | 
			
		||||
#   include <cstdio>
 | 
			
		||||
#   include <fcntl.h>
 | 
			
		||||
#   include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
MemoryMappedFile::MemoryMappedFile()
 | 
			
		||||
{
 | 
			
		||||
    // Default constructor.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemoryMappedFile::MemoryMappedFile(const std::filesystem::path &path)
 | 
			
		||||
{
 | 
			
		||||
    open(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemoryMappedFile::~MemoryMappedFile()
 | 
			
		||||
{
 | 
			
		||||
    close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemoryMappedFile::MemoryMappedFile(MemoryMappedFile &&other)
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    fileHandle = other.fileHandle;
 | 
			
		||||
    fileMappingHandle = other.fileMappingHandle;
 | 
			
		||||
    fileView = other.fileView;
 | 
			
		||||
    fileSize = other.fileSize;
 | 
			
		||||
 | 
			
		||||
    other.fileHandle = nullptr;
 | 
			
		||||
    other.fileMappingHandle = nullptr;
 | 
			
		||||
    other.fileView = nullptr;
 | 
			
		||||
    other.fileSize.QuadPart = 0;
 | 
			
		||||
#else
 | 
			
		||||
    fileHandle = other.fileHandle;
 | 
			
		||||
    fileView = other.fileView;
 | 
			
		||||
    fileSize = other.fileSize;
 | 
			
		||||
 | 
			
		||||
    other.fileHandle = -1;
 | 
			
		||||
    other.fileView = MAP_FAILED;
 | 
			
		||||
    other.fileSize = 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MemoryMappedFile::open(const std::filesystem::path &path)
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    fileHandle = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
 | 
			
		||||
    if (fileHandle == INVALID_HANDLE_VALUE)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "CreateFileW failed with error %lu.\n", GetLastError());
 | 
			
		||||
        fileHandle = nullptr;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!GetFileSizeEx(fileHandle, &fileSize))
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "GetFileSizeEx failed with error %lu.\n", GetLastError());
 | 
			
		||||
        CloseHandle(fileHandle);
 | 
			
		||||
        fileHandle = nullptr;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fileMappingHandle = CreateFileMappingW(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
 | 
			
		||||
    if (fileMappingHandle == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "CreateFileMappingW failed with error %lu.\n", GetLastError());
 | 
			
		||||
        CloseHandle(fileHandle);
 | 
			
		||||
        fileHandle = nullptr;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fileView = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
 | 
			
		||||
    if (fileView == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "MapViewOfFile failed with error %lu.\n", GetLastError());
 | 
			
		||||
        CloseHandle(fileMappingHandle);
 | 
			
		||||
        CloseHandle(fileHandle);
 | 
			
		||||
        fileMappingHandle = nullptr;
 | 
			
		||||
        fileHandle = nullptr;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
#else
 | 
			
		||||
    fileHandle = ::open(path.c_str(), O_RDONLY);
 | 
			
		||||
    if (fileHandle == -1)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "open for %s failed with error %s.\n", path.c_str(), strerror(errno));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fileSize = lseek(fileHandle, 0, SEEK_END);
 | 
			
		||||
    if (fileSize == (off_t)(-1))
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "lseek failed with error %s.\n", strerror(errno));
 | 
			
		||||
        ::close(fileHandle);
 | 
			
		||||
        fileHandle = -1;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fileView = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0);
 | 
			
		||||
    if (fileView == MAP_FAILED)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "mmap failed with error %s.\n", strerror(errno));
 | 
			
		||||
        ::close(fileHandle);
 | 
			
		||||
        fileHandle = -1;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MemoryMappedFile::close()
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    if (fileView != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        UnmapViewOfFile(fileView);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fileMappingHandle != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        CloseHandle(fileMappingHandle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fileHandle != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        CloseHandle(fileHandle);
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    if (fileView != MAP_FAILED)
 | 
			
		||||
    {
 | 
			
		||||
        munmap(fileView, fileSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fileHandle != -1)
 | 
			
		||||
    {
 | 
			
		||||
        ::close(fileHandle);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MemoryMappedFile::isOpen() const
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    return (fileView != nullptr);
 | 
			
		||||
#else
 | 
			
		||||
    return (fileView != MAP_FAILED);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t *MemoryMappedFile::data() const
 | 
			
		||||
{
 | 
			
		||||
    return reinterpret_cast<uint8_t *>(fileView);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t MemoryMappedFile::size() const
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    return fileSize.QuadPart;
 | 
			
		||||
#else
 | 
			
		||||
    return static_cast<size_t>(fileSize);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								XenonUtils/memory_mapped_file.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								XenonUtils/memory_mapped_file.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
#   include <Windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#   include <sys/mman.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct MemoryMappedFile {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    HANDLE fileHandle = nullptr;
 | 
			
		||||
    HANDLE fileMappingHandle = nullptr;
 | 
			
		||||
    LPVOID fileView = nullptr;
 | 
			
		||||
    LARGE_INTEGER fileSize = {};
 | 
			
		||||
#else
 | 
			
		||||
    int fileHandle = -1;
 | 
			
		||||
    void *fileView = MAP_FAILED;
 | 
			
		||||
    off_t fileSize = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    MemoryMappedFile();
 | 
			
		||||
    MemoryMappedFile(const std::filesystem::path &path);
 | 
			
		||||
    MemoryMappedFile(MemoryMappedFile &&other);
 | 
			
		||||
    ~MemoryMappedFile();
 | 
			
		||||
    bool open(const std::filesystem::path &path);
 | 
			
		||||
    void close();
 | 
			
		||||
    bool isOpen() const;
 | 
			
		||||
    uint8_t *data() const;
 | 
			
		||||
    size_t size() const;
 | 
			
		||||
};
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <aes.hpp>
 | 
			
		||||
 | 
			
		||||
#define STRINGIFY(X) #X
 | 
			
		||||
#define XE_EXPORT(MODULE, ORDINAL, NAME, TYPE) { (ORDINAL), "__imp__" STRINGIFY(NAME) }
 | 
			
		||||
@@ -121,58 +122,80 @@ std::unordered_map<size_t, const char*> XboxKernelExports =
 | 
			
		||||
    #include "xbox/xboxkrnl_table.inc"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Image Xex2LoadImage(const uint8_t* data)
 | 
			
		||||
Image Xex2LoadImage(const uint8_t* data, size_t dataSize)
 | 
			
		||||
{
 | 
			
		||||
    auto* header = reinterpret_cast<const XEX_HEADER*>(data);
 | 
			
		||||
    auto* security = reinterpret_cast<const XEX2_SECURITY_INFO*>(data + header->AddressOfSecurityInfo);
 | 
			
		||||
 | 
			
		||||
    const auto* compressionInfo = Xex2FindOptionalHeader<XEX_FILE_FORMAT_INFO>(header, XEX_HEADER_FILE_FORMAT_INFO);
 | 
			
		||||
    auto* header = reinterpret_cast<const Xex2Header*>(data);
 | 
			
		||||
    auto* security = reinterpret_cast<const Xex2SecurityInfo*>(data + header->securityOffset);
 | 
			
		||||
    const auto* fileFormatInfo = reinterpret_cast<const Xex2OptFileFormatInfo*>(getOptHeaderPtr(data, XEX_HEADER_FILE_FORMAT_INFO));
 | 
			
		||||
 | 
			
		||||
    Image image{};
 | 
			
		||||
    std::unique_ptr<uint8_t[]> result{};
 | 
			
		||||
    size_t imageSize = security->SizeOfImage;
 | 
			
		||||
    size_t imageSize = security->imageSize;
 | 
			
		||||
 | 
			
		||||
    // Decompress image
 | 
			
		||||
    if (compressionInfo != nullptr)
 | 
			
		||||
    if (fileFormatInfo != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        assert(compressionInfo->CompressionType <= XEX_COMPRESSION_BASIC);
 | 
			
		||||
        assert(compressionInfo->EncryptionType == XEX_ENCRYPTION_NONE);
 | 
			
		||||
        assert(fileFormatInfo->compressionType <= XEX_COMPRESSION_BASIC);
 | 
			
		||||
 | 
			
		||||
        if (compressionInfo->CompressionType == XEX_COMPRESSION_NONE)
 | 
			
		||||
        std::unique_ptr<uint8_t[]> decryptedData;
 | 
			
		||||
        const uint8_t* srcData = nullptr;
 | 
			
		||||
 | 
			
		||||
        if (fileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL)
 | 
			
		||||
        {
 | 
			
		||||
            constexpr uint32_t KeySize = 16;
 | 
			
		||||
            AES_ctx aesContext;
 | 
			
		||||
 | 
			
		||||
            uint8_t decryptedKey[KeySize];
 | 
			
		||||
            memcpy(decryptedKey, security->aesKey, KeySize);
 | 
			
		||||
            AES_init_ctx_iv(&aesContext, Xex2RetailKey, AESBlankIV);
 | 
			
		||||
            AES_CBC_decrypt_buffer(&aesContext, decryptedKey, KeySize);
 | 
			
		||||
 | 
			
		||||
            decryptedData = std::make_unique<uint8_t[]>(dataSize - header->headerSize);
 | 
			
		||||
            memcpy(decryptedData.get(), data + header->headerSize, dataSize - header->headerSize);
 | 
			
		||||
            AES_init_ctx_iv(&aesContext, decryptedKey, AESBlankIV);
 | 
			
		||||
            AES_CBC_decrypt_buffer(&aesContext, decryptedData.get(), dataSize - header->headerSize);
 | 
			
		||||
 | 
			
		||||
            srcData = decryptedData.get();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            srcData = data + header->headerSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fileFormatInfo->compressionType == XEX_COMPRESSION_NONE)
 | 
			
		||||
        {
 | 
			
		||||
            result = std::make_unique<uint8_t[]>(imageSize);
 | 
			
		||||
            memcpy(result.get(), data + header->SizeOfHeader, imageSize);
 | 
			
		||||
            memcpy(result.get(), srcData, imageSize);
 | 
			
		||||
        }
 | 
			
		||||
        else if (compressionInfo->CompressionType == XEX_COMPRESSION_BASIC)
 | 
			
		||||
        else if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC)
 | 
			
		||||
        {
 | 
			
		||||
            auto* blocks = reinterpret_cast<const XEX_BASIC_FILE_COMPRESSION_INFO*>(compressionInfo + 1);
 | 
			
		||||
            const size_t numBlocks = (compressionInfo->SizeOfHeader / sizeof(XEX_BASIC_FILE_COMPRESSION_INFO)) - 1;
 | 
			
		||||
            auto* blocks = reinterpret_cast<const Xex2FileBasicCompressionBlock*>(fileFormatInfo + 1);
 | 
			
		||||
            const size_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionInfo)) - 1;
 | 
			
		||||
 | 
			
		||||
            imageSize = 0;
 | 
			
		||||
            for (size_t i = 0; i < numBlocks; i++)
 | 
			
		||||
            {
 | 
			
		||||
                imageSize += blocks[i].SizeOfData + blocks[i].SizeOfPadding;
 | 
			
		||||
                imageSize += blocks[i].dataSize + blocks[i].zeroSize;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result = std::make_unique<uint8_t[]>(imageSize);
 | 
			
		||||
            auto* srcData = data + header->SizeOfHeader;
 | 
			
		||||
            auto* destData = result.get();
 | 
			
		||||
 | 
			
		||||
            for (size_t i = 0; i < numBlocks; i++)
 | 
			
		||||
            {
 | 
			
		||||
                memcpy(destData, srcData, blocks[i].SizeOfData);
 | 
			
		||||
                memcpy(destData, srcData, blocks[i].dataSize);
 | 
			
		||||
 | 
			
		||||
                srcData += blocks[i].SizeOfData;
 | 
			
		||||
                destData += blocks[i].SizeOfData;
 | 
			
		||||
                srcData += blocks[i].dataSize;
 | 
			
		||||
                destData += blocks[i].dataSize;
 | 
			
		||||
 | 
			
		||||
                memset(destData, 0, blocks[i].SizeOfPadding);
 | 
			
		||||
                destData += blocks[i].SizeOfPadding;
 | 
			
		||||
                memset(destData, 0, blocks[i].zeroSize);
 | 
			
		||||
                destData += blocks[i].zeroSize;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    image.data = std::move(result);
 | 
			
		||||
    image.size = imageSize;
 | 
			
		||||
    image.size = security->imageSize;
 | 
			
		||||
 | 
			
		||||
    // Map image
 | 
			
		||||
    const auto* dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(image.data.get());
 | 
			
		||||
@@ -198,22 +221,22 @@ Image Xex2LoadImage(const uint8_t* data)
 | 
			
		||||
            section.Misc.VirtualSize, flags, image.data.get() + section.VirtualAddress);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto* imports = Xex2FindOptionalHeader<XEX_IMPORT_HEADER>(header, XEX_HEADER_IMPORT_LIBRARIES);
 | 
			
		||||
    auto* imports = reinterpret_cast<const Xex2ImportHeader*>(getOptHeaderPtr(data, XEX_HEADER_IMPORT_LIBRARIES));
 | 
			
		||||
    if (imports != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::string_view> stringTable;
 | 
			
		||||
        auto* pStrTable = reinterpret_cast<const char*>(imports + 1);
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < imports->NumImports; i++)
 | 
			
		||||
        for (size_t i = 0; i < imports->numImports; i++)
 | 
			
		||||
        {
 | 
			
		||||
            stringTable.emplace_back(pStrTable);
 | 
			
		||||
            pStrTable += strlen(pStrTable) + 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto* library = (XEX_IMPORT_LIBRARY*)(((char*)imports) + sizeof(XEX_IMPORT_HEADER) + imports->SizeOfStringTable);
 | 
			
		||||
        auto* library = (Xex2ImportLibrary*)(((char*)imports) + sizeof(Xex2ImportHeader) + imports->sizeOfStringTable);
 | 
			
		||||
        for (size_t i = 0; i < stringTable.size(); i++)
 | 
			
		||||
        {
 | 
			
		||||
            auto* descriptors = (XEX_IMPORT_DESCRIPTOR*)(library + 1);
 | 
			
		||||
            auto* descriptors = (Xex2ImportDescriptor*)(library + 1);
 | 
			
		||||
            static std::unordered_map<size_t, const char*> DummyExports;
 | 
			
		||||
            const std::unordered_map<size_t, const char*>* names = &DummyExports;
 | 
			
		||||
 | 
			
		||||
@@ -226,25 +249,25 @@ Image Xex2LoadImage(const uint8_t* data)
 | 
			
		||||
                names = &XboxKernelExports;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (size_t im = 0; im < library->NumberOfImports; im++)
 | 
			
		||||
            for (size_t im = 0; im < library->numberOfImports; im++)
 | 
			
		||||
            {
 | 
			
		||||
                auto originalThunk = (XEX_THUNK_DATA*)image.Find(descriptors[im].FirstThunk);
 | 
			
		||||
                auto originalThunk = (Xex2ThunkData*)image.Find(descriptors[im].firstThunk);
 | 
			
		||||
                auto originalData = originalThunk;
 | 
			
		||||
                originalData->Data = ByteSwap(originalData->Data);
 | 
			
		||||
                originalData->data = ByteSwap(originalData->data);
 | 
			
		||||
 | 
			
		||||
                if (originalData->OriginalData.Type != 0)
 | 
			
		||||
                if (originalData->originalData.type != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    uint32_t thunk[4] = { 0x00000060, 0x00000060, 0x00000060, 0x2000804E };
 | 
			
		||||
                    auto name = names->find(originalData->OriginalData.Ordinal);
 | 
			
		||||
                    auto name = names->find(originalData->originalData.ordinal);
 | 
			
		||||
                    if (name != names->end())
 | 
			
		||||
                    {
 | 
			
		||||
                        image.symbols.insert({ name->second, descriptors[im].FirstThunk, sizeof(thunk), Symbol_Function });
 | 
			
		||||
                        image.symbols.insert({ name->second, descriptors[im].firstThunk, sizeof(thunk), Symbol_Function });
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    memcpy(originalThunk, thunk, sizeof(thunk));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            library = (XEX_IMPORT_LIBRARY*)((char*)(library + 1) + library->NumberOfImports * sizeof(XEX_IMPORT_DESCRIPTOR));
 | 
			
		||||
            library = (Xex2ImportLibrary*)((char*)(library + 1) + library->numberOfImports * sizeof(Xex2ImportDescriptor));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										293
									
								
								XenonUtils/xex.h
									
									
									
									
									
								
							
							
						
						
									
										293
									
								
								XenonUtils/xex.h
									
									
									
									
									
								
							@@ -2,18 +2,17 @@
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "xbox.h"
 | 
			
		||||
 | 
			
		||||
#define XEX_COMPRESSION_NONE 0
 | 
			
		||||
#define XEX_COMPRESSION_BASIC 1
 | 
			
		||||
inline constexpr uint8_t Xex2RetailKey[16] = { 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91 };
 | 
			
		||||
inline constexpr uint8_t AESBlankIV[16] = {};
 | 
			
		||||
 | 
			
		||||
#define XEX_ENCRYPTION_NONE 0
 | 
			
		||||
 | 
			
		||||
enum _XEX_THUNK_TYPES
 | 
			
		||||
enum Xex2ModuleFlags
 | 
			
		||||
{
 | 
			
		||||
    XEX_THUNK_VARIABLE = 0,
 | 
			
		||||
    XEX_THUNK_FUNCTION = 1,
 | 
			
		||||
    XEX_MODULE_MODULE_PATCH = 0x10,
 | 
			
		||||
    XEX_MODULE_PATCH_FULL = 0x20,
 | 
			
		||||
    XEX_MODULE_PATCH_DELTA = 0x40,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum _XEX_OPTIONAL_HEADER_TYPES
 | 
			
		||||
enum Xex2HeaderKeys
 | 
			
		||||
{
 | 
			
		||||
    XEX_HEADER_RESOURCE_INFO = 0x000002FF,
 | 
			
		||||
    XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF,
 | 
			
		||||
@@ -47,118 +46,212 @@ enum _XEX_OPTIONAL_HEADER_TYPES
 | 
			
		||||
    XEX_HEADER_EXPORTS_BY_NAME = 0x00E10402,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_FILE_FORMAT_INFO
 | 
			
		||||
enum Xex2EncryptionType
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> SizeOfHeader;
 | 
			
		||||
    be<uint16_t> EncryptionType;
 | 
			
		||||
    be<uint16_t> CompressionType;
 | 
			
		||||
} XEX_FILE_FORMAT_INFO;
 | 
			
		||||
    XEX_ENCRYPTION_NONE = 0,
 | 
			
		||||
    XEX_ENCRYPTION_NORMAL = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_RESOURCE_INFO
 | 
			
		||||
enum Xex2CompressionType
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> SizeOfHeader;
 | 
			
		||||
    uint8_t ResourceID[8];
 | 
			
		||||
    be<uint32_t> Offset;
 | 
			
		||||
    be<uint32_t> SizeOfData;
 | 
			
		||||
} XEX_RESOURCE_INFO;
 | 
			
		||||
    XEX_COMPRESSION_NONE = 0,
 | 
			
		||||
    XEX_COMPRESSION_BASIC = 1,
 | 
			
		||||
    XEX_COMPRESSION_NORMAL = 2,
 | 
			
		||||
    XEX_COMPRESSION_DELTA = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_BASIC_FILE_COMPRESSION_INFO
 | 
			
		||||
enum Xex2SectionType
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> SizeOfData;
 | 
			
		||||
    be<uint32_t> SizeOfPadding;
 | 
			
		||||
} XEX_BASIC_FILE_COMPRESSION_INFO;
 | 
			
		||||
    XEX_SECTION_CODE = 1,
 | 
			
		||||
    XEX_SECTION_DATA = 2,
 | 
			
		||||
    XEX_SECTION_READONLY_DATA = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_THUNK_DATA {
 | 
			
		||||
enum Xex2ThunkTypes
 | 
			
		||||
{
 | 
			
		||||
    XEX_THUNK_VARIABLE = 0,
 | 
			
		||||
    XEX_THUNK_FUNCTION = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2OptHeader
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> key;
 | 
			
		||||
 | 
			
		||||
    union
 | 
			
		||||
    {
 | 
			
		||||
        be<uint32_t> value;
 | 
			
		||||
        be<uint32_t> offset;
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2Header
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> magic;
 | 
			
		||||
    be<uint32_t> moduleFlags;
 | 
			
		||||
    be<uint32_t> headerSize;
 | 
			
		||||
    be<uint32_t> reserved;
 | 
			
		||||
    be<uint32_t> securityOffset;
 | 
			
		||||
    be<uint32_t> headerCount;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2PageDescriptor
 | 
			
		||||
{
 | 
			
		||||
    union
 | 
			
		||||
    {
 | 
			
		||||
        // Must be endian-swapped before reading the bitfield.
 | 
			
		||||
        uint32_t beValue;
 | 
			
		||||
        struct
 | 
			
		||||
        {
 | 
			
		||||
            uint32_t info : 4;
 | 
			
		||||
            uint32_t pageCount : 28;
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    char dataDigest[0x14];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2SecurityInfo
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> headerSize;
 | 
			
		||||
    be<uint32_t> imageSize;
 | 
			
		||||
    char rsaSignature[0x100];
 | 
			
		||||
    be<uint32_t> unknown;
 | 
			
		||||
    be<uint32_t> imageFlags;
 | 
			
		||||
    be<uint32_t> loadAddress;
 | 
			
		||||
    char sectionDigest[0x14];
 | 
			
		||||
    be<uint32_t> importTableCount;
 | 
			
		||||
    char importTableDigest[0x14];
 | 
			
		||||
    char xgd2MediaId[0x10];
 | 
			
		||||
    char aesKey[0x10];
 | 
			
		||||
    be<uint32_t> exportTable;
 | 
			
		||||
    char headerDigest[0x14];
 | 
			
		||||
    be<uint32_t> region;
 | 
			
		||||
    be<uint32_t> allowedMediaTypes;
 | 
			
		||||
    be<uint32_t> pageDescriptorCount;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2DeltaPatch
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> oldAddress;
 | 
			
		||||
    be<uint32_t> newAddress;
 | 
			
		||||
    be<uint16_t> uncompressedLength;
 | 
			
		||||
    be<uint16_t> compressedLength;
 | 
			
		||||
    char patchData[1];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2OptDeltaPatchDescriptor
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> size;
 | 
			
		||||
    be<uint32_t> targetVersionValue;
 | 
			
		||||
    be<uint32_t> sourceVersionValue;
 | 
			
		||||
    uint8_t digestSource[0x14];
 | 
			
		||||
    uint8_t imageKeySource[0x10];
 | 
			
		||||
    be<uint32_t> sizeOfTargetHeaders;
 | 
			
		||||
    be<uint32_t> deltaHeadersSourceOffset;
 | 
			
		||||
    be<uint32_t> deltaHeadersSourceSize;
 | 
			
		||||
    be<uint32_t> deltaHeadersTargetOffset;
 | 
			
		||||
    be<uint32_t> deltaImageSourceOffset;
 | 
			
		||||
    be<uint32_t> deltaImageSourceSize;
 | 
			
		||||
    be<uint32_t> deltaImageTargetOffset;
 | 
			
		||||
    Xex2DeltaPatch info;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2FileBasicCompressionBlock
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> dataSize;
 | 
			
		||||
    be<uint32_t> zeroSize;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2FileBasicCompressionInfo
 | 
			
		||||
{
 | 
			
		||||
    Xex2FileBasicCompressionBlock firstBlock;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2CompressedBlockInfo
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> blockSize;
 | 
			
		||||
    uint8_t blockHash[20];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2FileNormalCompressionInfo
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> windowSize;
 | 
			
		||||
    Xex2CompressedBlockInfo firstBlock;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2OptFileFormatInfo
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> infoSize;
 | 
			
		||||
    be<uint16_t> encryptionType;
 | 
			
		||||
    be<uint16_t> compressionType;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2ImportHeader
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> sizeOfHeader;
 | 
			
		||||
    be<uint32_t> sizeOfStringTable;
 | 
			
		||||
    be<uint32_t> numImports;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2ImportLibrary 
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> size;
 | 
			
		||||
    char nextImportDigest[0x14];
 | 
			
		||||
    be<uint32_t> id;
 | 
			
		||||
    be<uint32_t> version;
 | 
			
		||||
    be<uint32_t> minVersion;
 | 
			
		||||
    be<uint16_t> name;
 | 
			
		||||
    be<uint16_t> numberOfImports;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2ImportDescriptor 
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> firstThunk; // VA XEX_THUNK_DATA
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Xex2ThunkData 
 | 
			
		||||
{
 | 
			
		||||
    union
 | 
			
		||||
    {
 | 
			
		||||
        struct
 | 
			
		||||
        {
 | 
			
		||||
            uint16_t Ordinal : 16;
 | 
			
		||||
            uint16_t Hint : 8;
 | 
			
		||||
            uint16_t Type : 8;
 | 
			
		||||
        } OriginalData;
 | 
			
		||||
            uint16_t ordinal : 16;
 | 
			
		||||
            uint16_t hint : 8;
 | 
			
		||||
            uint16_t type : 8;
 | 
			
		||||
        } originalData;
 | 
			
		||||
 | 
			
		||||
        be<uint32_t> Ordinal;
 | 
			
		||||
        be<uint32_t> Function;
 | 
			
		||||
        be<uint32_t> AddressOfData;
 | 
			
		||||
        be<uint32_t> ordinal;
 | 
			
		||||
        be<uint32_t> function;
 | 
			
		||||
        be<uint32_t> addressOfData;
 | 
			
		||||
 | 
			
		||||
        // For easier swapping
 | 
			
		||||
        uint32_t Data;
 | 
			
		||||
        uint32_t data;
 | 
			
		||||
    };
 | 
			
		||||
} XEX_THUNK_DATA;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_IMPORT_HEADER {
 | 
			
		||||
    be<uint32_t> SizeOfHeader;
 | 
			
		||||
    be<uint32_t> SizeOfStringTable;
 | 
			
		||||
    be<uint32_t> NumImports;
 | 
			
		||||
} XEX_IMPORT_HEADER;
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_IMPORT_LIBRARY {
 | 
			
		||||
    be<uint32_t> Size;
 | 
			
		||||
    char NextImportDigest[0x14];
 | 
			
		||||
    be<uint32_t> ID;
 | 
			
		||||
    be<uint32_t> Version;
 | 
			
		||||
    be<uint32_t> MinVersion;
 | 
			
		||||
    be<uint16_t> Name;
 | 
			
		||||
    be<uint16_t> NumberOfImports;
 | 
			
		||||
} XEX_IMPORT_LIBRARY;
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(XEX_IMPORT_LIBRARY) == 0x28);
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_IMPORT_DESCRIPTOR {
 | 
			
		||||
    be<uint32_t> FirstThunk; // VA XEX_THUNK_DATA
 | 
			
		||||
} XEX_IMPORT_DESCRIPTOR;
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_OPTIONAL_HEADER
 | 
			
		||||
struct Xex2ResourceInfo
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> Type;
 | 
			
		||||
    be<uint32_t> Address;
 | 
			
		||||
} XEX_OPTIONAL_HEADER;
 | 
			
		||||
    be<uint32_t> sizeOfHeader;
 | 
			
		||||
    uint8_t resourceID[8];
 | 
			
		||||
    be<uint32_t> offset;
 | 
			
		||||
    be<uint32_t> sizeOfData;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX2_SECURITY_INFO
 | 
			
		||||
inline const void* getOptHeaderPtr(const uint8_t* moduleBytes, uint32_t headerKey)
 | 
			
		||||
{
 | 
			
		||||
    be<uint32_t> SizeOfHeader;
 | 
			
		||||
    be<uint32_t> SizeOfImage;
 | 
			
		||||
    char RsaSignature[0x100];
 | 
			
		||||
    be<uint32_t> Unknown108;
 | 
			
		||||
    be<uint32_t> ImageFlags;
 | 
			
		||||
    be<uint32_t> ImageBase;
 | 
			
		||||
    char SectionDigest[0x14];
 | 
			
		||||
    be<uint32_t> NumberOfImports;
 | 
			
		||||
    char ImportsDigest[0x14];
 | 
			
		||||
    char Xgd2MediaID[0x10];
 | 
			
		||||
    char AesKey[0x10];
 | 
			
		||||
    be<uint32_t> AddressOfExports;
 | 
			
		||||
    char HeaderDigest[0x14];
 | 
			
		||||
    be<uint32_t> Region;
 | 
			
		||||
    be<uint32_t> AllowedMediaTypes;
 | 
			
		||||
    be<uint32_t> NumberOfPageDescriptors;
 | 
			
		||||
} XEX2_SECURITY_INFO;
 | 
			
		||||
 | 
			
		||||
typedef struct _XEX_HEADER
 | 
			
		||||
{
 | 
			
		||||
    char Signature[4];
 | 
			
		||||
    be<uint32_t> Flags;
 | 
			
		||||
    be<uint32_t> SizeOfHeader;
 | 
			
		||||
    char Reserved[4];
 | 
			
		||||
    be<uint32_t> AddressOfSecurityInfo;
 | 
			
		||||
    be<uint32_t> NumberOfOptionalHeaders;
 | 
			
		||||
} XEX_HEADER;
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
inline static const T* Xex2FindOptionalHeader(const void* base, const XEX_OPTIONAL_HEADER* headers, size_t n, _XEX_OPTIONAL_HEADER_TYPES type)
 | 
			
		||||
{
 | 
			
		||||
    for (size_t i = 0; i < n; i++)
 | 
			
		||||
    const Xex2Header* xex2Header = (const Xex2Header*)(moduleBytes);
 | 
			
		||||
    for (uint32_t i = 0; i < xex2Header->headerCount; i++)
 | 
			
		||||
    {
 | 
			
		||||
        if (headers[i].Type == (uint32_t)type)
 | 
			
		||||
        const Xex2OptHeader& optHeader = ((const Xex2OptHeader*)(xex2Header + 1))[i];
 | 
			
		||||
        if (optHeader.key == headerKey)
 | 
			
		||||
        {
 | 
			
		||||
            if ((type & 0xFF) == 0)
 | 
			
		||||
            if ((headerKey & 0xFF) == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return reinterpret_cast<const T*>(&headers[i].Address);
 | 
			
		||||
                return &optHeader.value;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return reinterpret_cast<const T*>(static_cast<const char*>(base) + headers[i].Address);
 | 
			
		||||
                return &moduleBytes[optHeader.offset];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -166,11 +259,5 @@ inline static const T* Xex2FindOptionalHeader(const void* base, const XEX_OPTION
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
inline static const T* Xex2FindOptionalHeader(const XEX_HEADER* header, _XEX_OPTIONAL_HEADER_TYPES type)
 | 
			
		||||
{
 | 
			
		||||
    return Xex2FindOptionalHeader<T>(header, (XEX_OPTIONAL_HEADER*)(header + 1), header->NumberOfOptionalHeaders, type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Image;
 | 
			
		||||
Image Xex2LoadImage(const uint8_t* data);
 | 
			
		||||
Image Xex2LoadImage(const uint8_t* data, size_t dataSize);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										512
									
								
								XenonUtils/xex_patcher.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										512
									
								
								XenonUtils/xex_patcher.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,512 @@
 | 
			
		||||
// Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 ******************************************************************************
 | 
			
		||||
 * Xenia : Xbox 360 Emulator Research Project                                 *
 | 
			
		||||
 ******************************************************************************
 | 
			
		||||
 * Copyright 2023 Ben Vanik. All rights reserved.                             *
 | 
			
		||||
 * Released under the BSD license - see LICENSE in the root for more details. *
 | 
			
		||||
 ******************************************************************************
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "xex_patcher.h"
 | 
			
		||||
#include "xex.h"
 | 
			
		||||
 | 
			
		||||
#include <bit>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <climits>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#include <aes.hpp>
 | 
			
		||||
#include <lzx.h>
 | 
			
		||||
#include <mspack.h>
 | 
			
		||||
#include <TinySHA1.hpp>
 | 
			
		||||
 | 
			
		||||
#include "memory_mapped_file.h"
 | 
			
		||||
 | 
			
		||||
struct mspack_memory_file
 | 
			
		||||
{
 | 
			
		||||
    mspack_system sys;
 | 
			
		||||
    void *buffer;
 | 
			
		||||
    size_t bufferSize;
 | 
			
		||||
    size_t offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static mspack_memory_file *mspack_memory_open(mspack_system *sys, void *buffer, size_t bufferSize)
 | 
			
		||||
{
 | 
			
		||||
    assert(bufferSize < INT_MAX);
 | 
			
		||||
 | 
			
		||||
    if (bufferSize >= INT_MAX)
 | 
			
		||||
    {
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mspack_memory_file *memoryFile = (mspack_memory_file *)(std::calloc(1, sizeof(mspack_memory_file)));
 | 
			
		||||
    if (memoryFile == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        return memoryFile;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memoryFile->buffer = buffer;
 | 
			
		||||
    memoryFile->bufferSize = bufferSize;
 | 
			
		||||
    memoryFile->offset = 0;
 | 
			
		||||
    return memoryFile;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mspack_memory_close(mspack_memory_file *file)
 | 
			
		||||
{
 | 
			
		||||
    std::free(file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mspack_memory_read(mspack_file *file, void *buffer, int chars)
 | 
			
		||||
{
 | 
			
		||||
    mspack_memory_file *memoryFile = (mspack_memory_file *)(file);
 | 
			
		||||
    const size_t remaining = memoryFile->bufferSize - memoryFile->offset;
 | 
			
		||||
    const size_t total = std::min(size_t(chars), remaining);
 | 
			
		||||
    std::memcpy(buffer, (uint8_t *)(memoryFile->buffer) + memoryFile->offset, total);
 | 
			
		||||
    memoryFile->offset += total;
 | 
			
		||||
    return int(total);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mspack_memory_write(mspack_file *file, void *buffer, int chars)
 | 
			
		||||
{
 | 
			
		||||
    mspack_memory_file *memoryFile = (mspack_memory_file *)(file);
 | 
			
		||||
    const size_t remaining = memoryFile->bufferSize - memoryFile->offset;
 | 
			
		||||
    const size_t total = std::min(size_t(chars), remaining);
 | 
			
		||||
    std::memcpy((uint8_t *)(memoryFile->buffer) + memoryFile->offset, buffer, total);
 | 
			
		||||
    memoryFile->offset += total;
 | 
			
		||||
    return int(total);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *mspack_memory_alloc(mspack_system *sys, size_t chars)
 | 
			
		||||
{
 | 
			
		||||
    return std::calloc(chars, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mspack_memory_free(void *ptr)
 | 
			
		||||
{
 | 
			
		||||
    std::free(ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mspack_memory_copy(void *src, void *dest, size_t chars)
 | 
			
		||||
{
 | 
			
		||||
    std::memcpy(dest, src, chars);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static mspack_system *mspack_memory_sys_create()
 | 
			
		||||
{
 | 
			
		||||
    auto sys = (mspack_system *)(std::calloc(1, sizeof(mspack_system)));
 | 
			
		||||
    if (!sys)
 | 
			
		||||
    {
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sys->read = mspack_memory_read;
 | 
			
		||||
    sys->write = mspack_memory_write;
 | 
			
		||||
    sys->alloc = mspack_memory_alloc;
 | 
			
		||||
    sys->free = mspack_memory_free;
 | 
			
		||||
    sys->copy = mspack_memory_copy;
 | 
			
		||||
    return sys;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mspack_memory_sys_destroy(struct mspack_system *sys)
 | 
			
		||||
{
 | 
			
		||||
    free(sys);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex)
 | 
			
		||||
{
 | 
			
		||||
    return _BitScanForward((unsigned long *)(outFirstSetIndex), v) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex)
 | 
			
		||||
{
 | 
			
		||||
    return _BitScanForward64((unsigned long *)(outFirstSetIndex), v) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex)
 | 
			
		||||
{
 | 
			
		||||
    int i = ffs(v);
 | 
			
		||||
    *outFirstSetIndex = i - 1;
 | 
			
		||||
    return i != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex)
 | 
			
		||||
{
 | 
			
		||||
    int i = __builtin_ffsll(v);
 | 
			
		||||
    *outFirstSetIndex = i - 1;
 | 
			
		||||
    return i != 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int lzxDecompress(const void *lzxData, size_t lzxLength, void *dst, size_t dstLength, uint32_t windowSize, void *windowData, size_t windowDataLength)
 | 
			
		||||
{
 | 
			
		||||
    int resultCode = 1;
 | 
			
		||||
    uint32_t windowBits;
 | 
			
		||||
    if (!bitScanForward(windowSize, &windowBits)) {
 | 
			
		||||
        return resultCode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mspack_system *sys = mspack_memory_sys_create();
 | 
			
		||||
    mspack_memory_file *lzxSrc = mspack_memory_open(sys, (void *)(lzxData), lzxLength);
 | 
			
		||||
    mspack_memory_file *lzxDst = mspack_memory_open(sys, dst, dstLength);
 | 
			
		||||
    lzxd_stream *lzxd = lzxd_init(sys, (mspack_file *)(lzxSrc), (mspack_file *)(lzxDst), windowBits, 0, 0x8000, dstLength, 0);
 | 
			
		||||
    if (lzxd != nullptr) {
 | 
			
		||||
        if (windowData != nullptr) {
 | 
			
		||||
            size_t paddingLength = windowSize - windowDataLength;
 | 
			
		||||
            std::memset(&lzxd->window[0], 0, paddingLength);
 | 
			
		||||
            std::memcpy(&lzxd->window[paddingLength], windowData, windowDataLength);
 | 
			
		||||
            lzxd->ref_data_size = windowSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        resultCode = lzxd_decompress(lzxd, dstLength);
 | 
			
		||||
        lzxd_free(lzxd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (lzxSrc) {
 | 
			
		||||
        mspack_memory_close(lzxSrc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (lzxDst) {
 | 
			
		||||
        mspack_memory_close(lzxDst);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sys) {
 | 
			
		||||
        mspack_memory_sys_destroy(sys);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return resultCode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lzxDeltaApplyPatch(const Xex2DeltaPatch *deltaPatch, uint32_t patchLength, uint32_t windowSize, uint8_t *dstData)
 | 
			
		||||
{
 | 
			
		||||
    const void *patchEnd = (const uint8_t *)(deltaPatch) + patchLength;
 | 
			
		||||
    const Xex2DeltaPatch *curPatch = deltaPatch;
 | 
			
		||||
    while (patchEnd > curPatch)
 | 
			
		||||
    {
 | 
			
		||||
        int patchSize = -4; 
 | 
			
		||||
        if (curPatch->compressedLength == 0 && curPatch->uncompressedLength == 0 && curPatch->newAddress == 0 && curPatch->oldAddress == 0)
 | 
			
		||||
        {
 | 
			
		||||
            // End of patch.
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (curPatch->compressedLength)
 | 
			
		||||
        {
 | 
			
		||||
        case 0:
 | 
			
		||||
            // Set the data to zeroes.
 | 
			
		||||
            std::memset(&dstData[curPatch->newAddress], 0, curPatch->uncompressedLength);
 | 
			
		||||
            break;
 | 
			
		||||
        case 1:
 | 
			
		||||
            // Move the data.
 | 
			
		||||
            std::memcpy(&dstData[curPatch->newAddress], &dstData[curPatch->oldAddress], curPatch->uncompressedLength);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            // Decompress the data into the destination.
 | 
			
		||||
            patchSize = curPatch->compressedLength - 4;
 | 
			
		||||
            int result = lzxDecompress(curPatch->patchData, curPatch->compressedLength, &dstData[curPatch->newAddress], curPatch->uncompressedLength, windowSize, &dstData[curPatch->oldAddress], curPatch->uncompressedLength);
 | 
			
		||||
            if (result != 0)
 | 
			
		||||
            {
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        curPatch++;
 | 
			
		||||
        curPatch = (const Xex2DeltaPatch *)((const uint8_t *)(curPatch) + patchSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XexPatcher::Result XexPatcher::apply(const uint8_t* xexBytes, size_t xexBytesSize, const uint8_t* patchBytes, size_t patchBytesSize, std::vector<uint8_t> &outBytes, bool skipData)
 | 
			
		||||
{
 | 
			
		||||
    // Validate headers.
 | 
			
		||||
    static const char Xex2Magic[] = "XEX2";
 | 
			
		||||
    const Xex2Header *xexHeader = (const Xex2Header *)(xexBytes);
 | 
			
		||||
    if (memcmp(xexBytes, Xex2Magic, 4) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::XexFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const Xex2Header *patchHeader = (const Xex2Header *)(patchBytes);
 | 
			
		||||
    if (memcmp(patchBytes, Xex2Magic, 4) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((patchHeader->moduleFlags & (XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA | XEX_MODULE_PATCH_FULL)) == 0)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Validate patch.
 | 
			
		||||
    const Xex2OptDeltaPatchDescriptor *patchDescriptor = (const Xex2OptDeltaPatchDescriptor *)(getOptHeaderPtr(patchBytes, XEX_HEADER_DELTA_PATCH_DESCRIPTOR));
 | 
			
		||||
    if (patchDescriptor == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const Xex2OptFileFormatInfo *patchFileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(patchBytes, XEX_HEADER_FILE_FORMAT_INFO));
 | 
			
		||||
    if (patchFileFormatInfo == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (patchFileFormatInfo->compressionType != XEX_COMPRESSION_DELTA)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (patchDescriptor->deltaHeadersSourceOffset > xexHeader->headerSize)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchIncompatible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (patchDescriptor->deltaHeadersSourceSize > (xexHeader->headerSize - patchDescriptor->deltaHeadersSourceOffset))
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchIncompatible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (patchDescriptor->deltaHeadersTargetOffset > patchDescriptor->sizeOfTargetHeaders)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchIncompatible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t deltaTargetSize = patchDescriptor->sizeOfTargetHeaders - patchDescriptor->deltaHeadersTargetOffset;
 | 
			
		||||
    if (patchDescriptor->deltaHeadersSourceSize > deltaTargetSize)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchIncompatible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Apply patch.
 | 
			
		||||
    uint32_t headerTargetSize = patchDescriptor->sizeOfTargetHeaders;
 | 
			
		||||
    if (headerTargetSize == 0)
 | 
			
		||||
    {
 | 
			
		||||
        headerTargetSize = patchDescriptor->deltaHeadersTargetOffset + patchDescriptor->deltaHeadersSourceSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create the bytes for the new XEX header. Copy over the existing data.
 | 
			
		||||
    uint32_t newXexHeaderSize = std::max(headerTargetSize, xexHeader->headerSize.get());
 | 
			
		||||
    outBytes.resize(newXexHeaderSize);
 | 
			
		||||
    memset(outBytes.data(), 0, newXexHeaderSize);
 | 
			
		||||
    memcpy(outBytes.data(), xexBytes, headerTargetSize);
 | 
			
		||||
 | 
			
		||||
    Xex2Header *newXexHeader = (Xex2Header *)(outBytes.data());
 | 
			
		||||
    if (patchDescriptor->deltaHeadersSourceOffset > 0)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(&outBytes[patchDescriptor->deltaHeadersTargetOffset], &outBytes[patchDescriptor->deltaHeadersSourceOffset], patchDescriptor->deltaHeadersSourceSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int resultCode = lzxDeltaApplyPatch(&patchDescriptor->info, patchDescriptor->size, ((const Xex2FileNormalCompressionInfo*)(patchFileFormatInfo + 1))->windowSize, outBytes.data());
 | 
			
		||||
    if (resultCode != 0)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFailed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Make the header the specified size by the patch.
 | 
			
		||||
    outBytes.resize(headerTargetSize);
 | 
			
		||||
    newXexHeader = (Xex2Header *)(outBytes.data());
 | 
			
		||||
 | 
			
		||||
    // Copy the rest of the data.
 | 
			
		||||
    const Xex2SecurityInfo *newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]);
 | 
			
		||||
    outBytes.resize(outBytes.size() + newSecurityInfo->imageSize);
 | 
			
		||||
    memset(&outBytes[headerTargetSize], 0, outBytes.size() - headerTargetSize);
 | 
			
		||||
    memcpy(&outBytes[headerTargetSize], &xexBytes[xexHeader->headerSize], xexBytesSize - xexHeader->headerSize);
 | 
			
		||||
    newXexHeader = (Xex2Header *)(outBytes.data());
 | 
			
		||||
    newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]);
 | 
			
		||||
    
 | 
			
		||||
    // Decrypt the keys and validate that the patch is compatible with the base file.
 | 
			
		||||
    constexpr uint32_t KeySize = 16;
 | 
			
		||||
    const Xex2SecurityInfo *originalSecurityInfo = (const Xex2SecurityInfo *)(&xexBytes[xexHeader->securityOffset]);
 | 
			
		||||
    const Xex2SecurityInfo *patchSecurityInfo = (const Xex2SecurityInfo *)(&patchBytes[patchHeader->securityOffset]);
 | 
			
		||||
    uint8_t decryptedOriginalKey[KeySize];
 | 
			
		||||
    uint8_t decryptedNewKey[KeySize];
 | 
			
		||||
    uint8_t decryptedPatchKey[KeySize];
 | 
			
		||||
    uint8_t decrpytedImageKeySource[KeySize];
 | 
			
		||||
    memcpy(decryptedOriginalKey, originalSecurityInfo->aesKey, KeySize);
 | 
			
		||||
    memcpy(decryptedNewKey, newSecurityInfo->aesKey, KeySize);
 | 
			
		||||
    memcpy(decryptedPatchKey, patchSecurityInfo->aesKey, KeySize);
 | 
			
		||||
    memcpy(decrpytedImageKeySource, patchDescriptor->imageKeySource, KeySize);
 | 
			
		||||
 | 
			
		||||
    AES_ctx aesContext;
 | 
			
		||||
    AES_init_ctx_iv(&aesContext, Xex2RetailKey, AESBlankIV);
 | 
			
		||||
    AES_CBC_decrypt_buffer(&aesContext, decryptedOriginalKey, KeySize);
 | 
			
		||||
 | 
			
		||||
    AES_ctx_set_iv(&aesContext, AESBlankIV);
 | 
			
		||||
    AES_CBC_decrypt_buffer(&aesContext, decryptedNewKey, KeySize);
 | 
			
		||||
 | 
			
		||||
    AES_init_ctx_iv(&aesContext, decryptedNewKey, AESBlankIV);
 | 
			
		||||
    AES_CBC_decrypt_buffer(&aesContext, decryptedPatchKey, KeySize);
 | 
			
		||||
 | 
			
		||||
    AES_ctx_set_iv(&aesContext, AESBlankIV);
 | 
			
		||||
    AES_CBC_decrypt_buffer(&aesContext, decrpytedImageKeySource, KeySize);
 | 
			
		||||
 | 
			
		||||
    // Validate the patch's key matches the one from the original XEX.
 | 
			
		||||
    if (memcmp(decrpytedImageKeySource, decryptedOriginalKey, KeySize) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchIncompatible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Don't process the rest of the patch.
 | 
			
		||||
    if (skipData)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::Success;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Decrypt base XEX if necessary.
 | 
			
		||||
    const Xex2OptFileFormatInfo *fileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(xexBytes, XEX_HEADER_FILE_FORMAT_INFO));
 | 
			
		||||
    if (fileFormatInfo == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::XexFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL)
 | 
			
		||||
    {
 | 
			
		||||
        AES_init_ctx_iv(&aesContext, decryptedOriginalKey, AESBlankIV);
 | 
			
		||||
        AES_CBC_decrypt_buffer(&aesContext, &outBytes[headerTargetSize], xexBytesSize - xexHeader->headerSize);
 | 
			
		||||
    }
 | 
			
		||||
    else if (fileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::XexFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Decompress base XEX if necessary.
 | 
			
		||||
    if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC)
 | 
			
		||||
    {
 | 
			
		||||
        const Xex2FileBasicCompressionBlock *blocks = &((const Xex2FileBasicCompressionInfo*)(fileFormatInfo + 1))->firstBlock;
 | 
			
		||||
        int32_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionBlock)) - 1;
 | 
			
		||||
        int32_t baseCompressedSize = 0;
 | 
			
		||||
        int32_t baseImageSize = 0;
 | 
			
		||||
        for (int32_t i = 0; i < numBlocks; i++) {
 | 
			
		||||
            baseCompressedSize += blocks[i].dataSize;
 | 
			
		||||
            baseImageSize += blocks[i].dataSize + blocks[i].zeroSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (outBytes.size() < (headerTargetSize + baseImageSize))
 | 
			
		||||
        {
 | 
			
		||||
            return Result::XexFileInvalid;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Reverse iteration allows to perform this decompression in place.
 | 
			
		||||
        uint8_t *srcDataCursor = outBytes.data() + headerTargetSize + baseCompressedSize;
 | 
			
		||||
        uint8_t *outDataCursor = outBytes.data() + headerTargetSize + baseImageSize;
 | 
			
		||||
        for (int32_t i = numBlocks - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            outDataCursor -= blocks[i].zeroSize;
 | 
			
		||||
            memset(outDataCursor, 0, blocks[i].zeroSize);
 | 
			
		||||
            outDataCursor -= blocks[i].dataSize;
 | 
			
		||||
            srcDataCursor -= blocks[i].dataSize;
 | 
			
		||||
            memmove(outDataCursor, srcDataCursor, blocks[i].dataSize);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (fileFormatInfo->compressionType == XEX_COMPRESSION_NORMAL || fileFormatInfo->compressionType == XEX_COMPRESSION_DELTA)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::XexFileUnsupported;
 | 
			
		||||
    }
 | 
			
		||||
    else if (fileFormatInfo->compressionType != XEX_COMPRESSION_NONE)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::XexFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Xex2OptFileFormatInfo *newFileFormatInfo = (Xex2OptFileFormatInfo *)(getOptHeaderPtr(outBytes.data(), XEX_HEADER_FILE_FORMAT_INFO));
 | 
			
		||||
    if (newFileFormatInfo == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFailed;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Update the header to indicate no encryption or compression is used.
 | 
			
		||||
    newFileFormatInfo->encryptionType = XEX_ENCRYPTION_NONE;
 | 
			
		||||
    newFileFormatInfo->compressionType = XEX_COMPRESSION_NONE;
 | 
			
		||||
 | 
			
		||||
    // Copy and decrypt patch data if necessary.
 | 
			
		||||
    std::vector<uint8_t> patchData;
 | 
			
		||||
    patchData.resize(patchBytesSize - patchHeader->headerSize);
 | 
			
		||||
    memcpy(patchData.data(), &patchBytes[patchHeader->headerSize], patchData.size());
 | 
			
		||||
 | 
			
		||||
    if (patchFileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL)
 | 
			
		||||
    {
 | 
			
		||||
        AES_init_ctx_iv(&aesContext, decryptedPatchKey, AESBlankIV);
 | 
			
		||||
        AES_CBC_decrypt_buffer(&aesContext, patchData.data(), patchData.size());
 | 
			
		||||
    }
 | 
			
		||||
    else if (patchFileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE)
 | 
			
		||||
    {
 | 
			
		||||
        return Result::PatchFileInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const Xex2CompressedBlockInfo *currentBlock = &((const Xex2FileNormalCompressionInfo*)(patchFileFormatInfo + 1))->firstBlock;
 | 
			
		||||
    uint8_t *outExe = &outBytes[newXexHeader->headerSize];
 | 
			
		||||
    if (patchDescriptor->deltaImageSourceOffset > 0)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(&outExe[patchDescriptor->deltaImageTargetOffset], &outExe[patchDescriptor->deltaImageSourceOffset], patchDescriptor->deltaImageSourceSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static const uint32_t DigestSize = 20;
 | 
			
		||||
    uint8_t sha1Digest[DigestSize];
 | 
			
		||||
    sha1::SHA1 sha1Context;
 | 
			
		||||
    uint8_t *patchDataCursor = patchData.data();
 | 
			
		||||
    while (currentBlock->blockSize > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const Xex2CompressedBlockInfo *nextBlock = (const Xex2CompressedBlockInfo *)(patchDataCursor);
 | 
			
		||||
 | 
			
		||||
        // Hash and validate the block.
 | 
			
		||||
        sha1Context.reset();
 | 
			
		||||
        sha1Context.processBytes(patchDataCursor, currentBlock->blockSize);
 | 
			
		||||
        sha1Context.finalize(sha1Digest);
 | 
			
		||||
        if (memcmp(sha1Digest, currentBlock->blockHash, DigestSize) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            return Result::PatchFailed;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        patchDataCursor += 24;
 | 
			
		||||
 | 
			
		||||
        // Apply the block's patch data.
 | 
			
		||||
        uint32_t blockDataSize = currentBlock->blockSize - 24;
 | 
			
		||||
        if (lzxDeltaApplyPatch((const Xex2DeltaPatch *)(patchDataCursor), blockDataSize, ((const Xex2FileNormalCompressionInfo*)(patchFileFormatInfo + 1))->windowSize, outExe) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            return Result::PatchFailed;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        patchDataCursor += blockDataSize;
 | 
			
		||||
        currentBlock = nextBlock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Result::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XexPatcher::Result XexPatcher::apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath)
 | 
			
		||||
{
 | 
			
		||||
    MemoryMappedFile baseXexFile(baseXexPath);
 | 
			
		||||
    MemoryMappedFile patchFile(patchXexPath);
 | 
			
		||||
    if (!baseXexFile.isOpen() || !patchFile.isOpen())
 | 
			
		||||
    {
 | 
			
		||||
        return Result::FileOpenFailed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> newXexBytes;
 | 
			
		||||
    Result result = apply(baseXexFile.data(), baseXexFile.size(), patchFile.data(), patchFile.size(), newXexBytes, false);
 | 
			
		||||
    if (result != Result::Success)
 | 
			
		||||
    {
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ofstream newXexFile(newXexPath, std::ios::binary);
 | 
			
		||||
    if (!newXexFile.is_open())
 | 
			
		||||
    {
 | 
			
		||||
        return Result::FileOpenFailed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    newXexFile.write((const char *)(newXexBytes.data()), newXexBytes.size());
 | 
			
		||||
    newXexFile.close();
 | 
			
		||||
 | 
			
		||||
    if (newXexFile.bad())
 | 
			
		||||
    {
 | 
			
		||||
        std::filesystem::remove(newXexPath);
 | 
			
		||||
        return Result::FileWriteFailed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Result::Success;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								XenonUtils/xex_patcher.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								XenonUtils/xex_patcher.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
// Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 ******************************************************************************
 | 
			
		||||
 * Xenia : Xbox 360 Emulator Research Project                                 *
 | 
			
		||||
 ******************************************************************************
 | 
			
		||||
 * Copyright 2023 Ben Vanik. All rights reserved.                             *
 | 
			
		||||
 * Released under the BSD license - see LICENSE in the root for more details. *
 | 
			
		||||
 ******************************************************************************
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <span>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
struct XexPatcher
 | 
			
		||||
{
 | 
			
		||||
    enum class Result {
 | 
			
		||||
        Success,
 | 
			
		||||
        FileOpenFailed,
 | 
			
		||||
        FileWriteFailed,
 | 
			
		||||
        XexFileUnsupported,
 | 
			
		||||
        XexFileInvalid,
 | 
			
		||||
        PatchFileInvalid,
 | 
			
		||||
        PatchIncompatible,
 | 
			
		||||
        PatchFailed,
 | 
			
		||||
        PatchUnsupported
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static Result apply(const uint8_t* xexBytes, size_t xexBytesSize, const uint8_t* patchBytes, size_t patchBytesSize, std::vector<uint8_t> &outBytes, bool skipData);
 | 
			
		||||
    static Result apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath);
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										223
									
								
								thirdparty/TinySHA1/TinySHA1.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								thirdparty/TinySHA1/TinySHA1.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,223 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based
 | 
			
		||||
 * on the implementation in boost::uuid::details.
 | 
			
		||||
 *
 | 
			
		||||
 * SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 * Taken from https://github.com/mohaps/TinySHA1
 | 
			
		||||
 * Modified for use by Xenia
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _TINY_SHA1_HPP_
 | 
			
		||||
#define _TINY_SHA1_HPP_
 | 
			
		||||
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
namespace sha1 {
 | 
			
		||||
class SHA1 {
 | 
			
		||||
 public:
 | 
			
		||||
  typedef uint32_t digest32_t[5];
 | 
			
		||||
  typedef uint8_t digest8_t[20];
 | 
			
		||||
  inline static uint32_t LeftRotate(uint32_t value, size_t count) {
 | 
			
		||||
    return (value << count) ^ (value >> (32 - count));
 | 
			
		||||
  }
 | 
			
		||||
  SHA1() { reset(); }
 | 
			
		||||
  virtual ~SHA1() {}
 | 
			
		||||
  SHA1(const SHA1& s) { *this = s; }
 | 
			
		||||
  const SHA1& operator=(const SHA1& s) {
 | 
			
		||||
    memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t));
 | 
			
		||||
    memcpy(m_block, s.m_block, 64);
 | 
			
		||||
    m_blockByteIndex = s.m_blockByteIndex;
 | 
			
		||||
    m_byteCount = s.m_byteCount;
 | 
			
		||||
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SHA1& init(const uint32_t digest[5], const uint8_t block[64],
 | 
			
		||||
             uint32_t count) {
 | 
			
		||||
    std::memcpy(m_digest, digest, 20);
 | 
			
		||||
    std::memcpy(m_block, block, count % 64);
 | 
			
		||||
    m_byteCount = count;
 | 
			
		||||
    m_blockByteIndex = count % 64;
 | 
			
		||||
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const uint32_t* getDigest() const { return m_digest; }
 | 
			
		||||
  const uint8_t* getBlock() const { return m_block; }
 | 
			
		||||
  size_t getBlockByteIndex() const { return m_blockByteIndex; }
 | 
			
		||||
  size_t getByteCount() const { return m_byteCount; }
 | 
			
		||||
 | 
			
		||||
  SHA1& reset() {
 | 
			
		||||
    m_digest[0] = 0x67452301;
 | 
			
		||||
    m_digest[1] = 0xEFCDAB89;
 | 
			
		||||
    m_digest[2] = 0x98BADCFE;
 | 
			
		||||
    m_digest[3] = 0x10325476;
 | 
			
		||||
    m_digest[4] = 0xC3D2E1F0;
 | 
			
		||||
    m_blockByteIndex = 0;
 | 
			
		||||
    m_byteCount = 0;
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SHA1& processByte(uint8_t octet) {
 | 
			
		||||
    this->m_block[this->m_blockByteIndex++] = octet;
 | 
			
		||||
    ++this->m_byteCount;
 | 
			
		||||
    if (m_blockByteIndex == 64) {
 | 
			
		||||
      this->m_blockByteIndex = 0;
 | 
			
		||||
      processBlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SHA1& processBlock(const void* const start, const void* const end) {
 | 
			
		||||
    const uint8_t* begin = static_cast<const uint8_t*>(start);
 | 
			
		||||
    const uint8_t* finish = static_cast<const uint8_t*>(end);
 | 
			
		||||
    while (begin != finish) {
 | 
			
		||||
      processByte(*begin);
 | 
			
		||||
      begin++;
 | 
			
		||||
    }
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SHA1& processBytes(const void* const data, size_t len) {
 | 
			
		||||
    const uint8_t* block = static_cast<const uint8_t*>(data);
 | 
			
		||||
    processBlock(block, block + len);
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const uint32_t* finalize(digest32_t digest) {
 | 
			
		||||
    size_t bitCount = this->m_byteCount * 8;
 | 
			
		||||
    processByte(0x80);
 | 
			
		||||
    if (this->m_blockByteIndex > 56) {
 | 
			
		||||
      while (m_blockByteIndex != 0) {
 | 
			
		||||
        processByte(0);
 | 
			
		||||
      }
 | 
			
		||||
      while (m_blockByteIndex < 56) {
 | 
			
		||||
        processByte(0);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      while (m_blockByteIndex < 56) {
 | 
			
		||||
        processByte(0);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    processByte(0);
 | 
			
		||||
    processByte(0);
 | 
			
		||||
    processByte(0);
 | 
			
		||||
    processByte(0);
 | 
			
		||||
    processByte(static_cast<unsigned char>((bitCount >> 24) & 0xFF));
 | 
			
		||||
    processByte(static_cast<unsigned char>((bitCount >> 16) & 0xFF));
 | 
			
		||||
    processByte(static_cast<unsigned char>((bitCount >> 8) & 0xFF));
 | 
			
		||||
    processByte(static_cast<unsigned char>((bitCount)&0xFF));
 | 
			
		||||
 | 
			
		||||
    memcpy(digest, m_digest, 5 * sizeof(uint32_t));
 | 
			
		||||
    return digest;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const uint8_t* finalize(digest8_t digest) {
 | 
			
		||||
    digest32_t d32;
 | 
			
		||||
    finalize(d32);
 | 
			
		||||
    size_t di = 0;
 | 
			
		||||
    digest[di++] = ((d32[0] >> 24) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[0] >> 16) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[0] >> 8) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[0]) & 0xFF);
 | 
			
		||||
 | 
			
		||||
    digest[di++] = ((d32[1] >> 24) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[1] >> 16) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[1] >> 8) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[1]) & 0xFF);
 | 
			
		||||
 | 
			
		||||
    digest[di++] = ((d32[2] >> 24) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[2] >> 16) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[2] >> 8) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[2]) & 0xFF);
 | 
			
		||||
 | 
			
		||||
    digest[di++] = ((d32[3] >> 24) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[3] >> 16) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[3] >> 8) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[3]) & 0xFF);
 | 
			
		||||
 | 
			
		||||
    digest[di++] = ((d32[4] >> 24) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[4] >> 16) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[4] >> 8) & 0xFF);
 | 
			
		||||
    digest[di++] = ((d32[4]) & 0xFF);
 | 
			
		||||
    return digest;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void processBlock() {
 | 
			
		||||
    uint32_t w[80];
 | 
			
		||||
    for (size_t i = 0; i < 16; i++) {
 | 
			
		||||
      w[i] = (m_block[i * 4 + 0] << 24);
 | 
			
		||||
      w[i] |= (m_block[i * 4 + 1] << 16);
 | 
			
		||||
      w[i] |= (m_block[i * 4 + 2] << 8);
 | 
			
		||||
      w[i] |= (m_block[i * 4 + 3]);
 | 
			
		||||
    }
 | 
			
		||||
    for (size_t i = 16; i < 80; i++) {
 | 
			
		||||
      w[i] = LeftRotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t a = m_digest[0];
 | 
			
		||||
    uint32_t b = m_digest[1];
 | 
			
		||||
    uint32_t c = m_digest[2];
 | 
			
		||||
    uint32_t d = m_digest[3];
 | 
			
		||||
    uint32_t e = m_digest[4];
 | 
			
		||||
 | 
			
		||||
    for (std::size_t i = 0; i < 80; ++i) {
 | 
			
		||||
      uint32_t f = 0;
 | 
			
		||||
      uint32_t k = 0;
 | 
			
		||||
 | 
			
		||||
      if (i < 20) {
 | 
			
		||||
        f = (b & c) | (~b & d);
 | 
			
		||||
        k = 0x5A827999;
 | 
			
		||||
      } else if (i < 40) {
 | 
			
		||||
        f = b ^ c ^ d;
 | 
			
		||||
        k = 0x6ED9EBA1;
 | 
			
		||||
      } else if (i < 60) {
 | 
			
		||||
        f = (b & c) | (b & d) | (c & d);
 | 
			
		||||
        k = 0x8F1BBCDC;
 | 
			
		||||
      } else {
 | 
			
		||||
        f = b ^ c ^ d;
 | 
			
		||||
        k = 0xCA62C1D6;
 | 
			
		||||
      }
 | 
			
		||||
      uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i];
 | 
			
		||||
      e = d;
 | 
			
		||||
      d = c;
 | 
			
		||||
      c = LeftRotate(b, 30);
 | 
			
		||||
      b = a;
 | 
			
		||||
      a = temp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_digest[0] += a;
 | 
			
		||||
    m_digest[1] += b;
 | 
			
		||||
    m_digest[2] += c;
 | 
			
		||||
    m_digest[3] += d;
 | 
			
		||||
    m_digest[4] += e;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  digest32_t m_digest;
 | 
			
		||||
  uint8_t m_block[64];
 | 
			
		||||
  size_t m_blockByteIndex;
 | 
			
		||||
  size_t m_byteCount;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1
									
								
								thirdparty/libmspack
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								thirdparty/libmspack
									
									
									
									
										vendored
									
									
										Submodule
									
								
							 Submodule thirdparty/libmspack added at 305907723a
									
								
							
							
								
								
									
										1
									
								
								thirdparty/tiny-AES-c
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								thirdparty/tiny-AES-c
									
									
									
									
										vendored
									
									
										Submodule
									
								
							 Submodule thirdparty/tiny-AES-c added at 23856752fb
									
								
							
		Reference in New Issue
	
	Block a user