mirror of
				https://github.com/hedge-dev/XenonRecomp.git
				synced 2025-10-30 07:11:38 +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:
		
							parent
							
								
									0fc545a6e2
								
							
						
					
					
						commit
						cd6fcb33bd
					
				
					 17 changed files with 1336 additions and 144 deletions
				
			
		
							
								
								
									
										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 | ||||
|     const Xex2Header* xex2Header = (const Xex2Header*)(moduleBytes); | ||||
|     for (uint32_t i = 0; i < xex2Header->headerCount; i++) | ||||
|     { | ||||
|     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) | ||||
|         const Xex2OptHeader& optHeader = ((const Xex2OptHeader*)(xex2Header + 1))[i]; | ||||
|         if (optHeader.key == headerKey) | ||||
|         { | ||||
|     for (size_t i = 0; i < n; i++) | ||||
|             if ((headerKey & 0xFF) == 0) | ||||
|             { | ||||
|         if (headers[i].Type == (uint32_t)type) | ||||
|         { | ||||
|             if ((type & 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
									
								
							|  | @ -0,0 +1 @@ | |||
| Subproject commit 305907723a4e7ab2018e58040059ffb5e77db837 | ||||
							
								
								
									
										1
									
								
								thirdparty/tiny-AES-c
									
										
									
									
										vendored
									
									
										Submodule
									
								
							
							
						
						
									
										1
									
								
								thirdparty/tiny-AES-c
									
										
									
									
										vendored
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | |||
| Subproject commit 23856752fbd139da0b8ca6e471a13d5bcc99a08d | ||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Skyth (Asilkan)
						Skyth (Asilkan)