Skip to content

Commit cd6fcb3

Browse files
Port XEX patcher from Unleashed Recompiled. (#4)
* Port XEX patcher from Unleashed Recompiled. * Fix compilation error on Linux.
1 parent 0fc545a commit cd6fcb3

17 files changed

Lines changed: 1340 additions & 148 deletions

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@
77
[submodule "thirdparty/tomlplusplus"]
88
path = thirdparty/tomlplusplus
99
url = https://github.com/marzer/tomlplusplus.git
10+
[submodule "thirdparty/libmspack"]
11+
path = thirdparty/libmspack
12+
url = https://github.com/kyz/libmspack
13+
[submodule "thirdparty/tiny-AES-c"]
14+
path = thirdparty/tiny-AES-c
15+
url = https://github.com/kokke/tiny-AES-c.git

XenonRecomp/main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ int main(int argc, char* argv[])
1414
if (std::filesystem::is_regular_file(path))
1515
{
1616
Recompiler recompiler;
17-
recompiler.LoadConfig(path);
17+
if (!recompiler.LoadConfig(path))
18+
return -1;
19+
1820
recompiler.Analyse();
1921

2022
auto entry = recompiler.image.symbols.find(recompiler.image.entry_point);

XenonRecomp/recompiler.cpp

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "pch.h"
22
#include "recompiler.h"
3+
#include <xex_patcher.h>
34

45
static uint64_t ComputeMask(uint32_t mstart, uint32_t mstop)
56
{
@@ -9,12 +10,87 @@ static uint64_t ComputeMask(uint32_t mstart, uint32_t mstop)
910
return mstart <= mstop ? value : ~value;
1011
}
1112

12-
void Recompiler::LoadConfig(const std::string_view& configFilePath)
13+
bool Recompiler::LoadConfig(const std::string_view& configFilePath)
1314
{
1415
config.Load(configFilePath);
1516

16-
const auto file = LoadFile((config.directoryPath + config.filePath).c_str());
17+
std::vector<uint8_t> file;
18+
if (!config.patchedFilePath.empty())
19+
file = LoadFile((config.directoryPath + config.patchedFilePath).c_str());
20+
21+
if (file.empty())
22+
{
23+
file = LoadFile((config.directoryPath + config.filePath).c_str());
24+
25+
if (!config.patchFilePath.empty())
26+
{
27+
const auto patchFile = LoadFile((config.directoryPath + config.patchFilePath).c_str());
28+
if (!patchFile.empty())
29+
{
30+
std::vector<uint8_t> outBytes;
31+
auto result = XexPatcher::apply(file.data(), file.size(), patchFile.data(), patchFile.size(), outBytes, false);
32+
if (result == XexPatcher::Result::Success)
33+
{
34+
std::exchange(file, outBytes);
35+
36+
if (!config.patchedFilePath.empty())
37+
{
38+
std::ofstream stream(config.directoryPath + config.patchedFilePath, std::ios::binary);
39+
if (stream.good())
40+
{
41+
stream.write(reinterpret_cast<const char*>(file.data()), file.size());
42+
stream.close();
43+
}
44+
}
45+
}
46+
else
47+
{
48+
fmt::print("ERROR: Unable to apply the patch file, ");
49+
50+
switch (result)
51+
{
52+
case XexPatcher::Result::XexFileUnsupported:
53+
fmt::println("XEX file unsupported");
54+
break;
55+
56+
case XexPatcher::Result::XexFileInvalid:
57+
fmt::println("XEX file invalid");
58+
break;
59+
60+
case XexPatcher::Result::PatchFileInvalid:
61+
fmt::println("patch file invalid");
62+
break;
63+
64+
case XexPatcher::Result::PatchIncompatible:
65+
fmt::println("patch file incompatible");
66+
break;
67+
68+
case XexPatcher::Result::PatchFailed:
69+
fmt::println("patch failed");
70+
break;
71+
72+
case XexPatcher::Result::PatchUnsupported:
73+
fmt::println("patch unsupported");
74+
break;
75+
76+
default:
77+
fmt::println("reason unknown");
78+
break;
79+
}
80+
81+
return false;
82+
}
83+
}
84+
else
85+
{
86+
fmt::println("ERROR: Unable to load the patch file");
87+
return false;
88+
}
89+
}
90+
}
91+
1792
image = Image::ParseImage(file.data(), file.size());
93+
return true;
1894
}
1995

2096
void Recompiler::Analyse()

XenonRecomp/recompiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ struct Recompiler
3535
size_t cppFileIndex = 0;
3636
RecompilerConfig config;
3737

38-
void LoadConfig(const std::string_view& configFilePath);
38+
bool LoadConfig(const std::string_view& configFilePath);
3939

4040
template<class... Args>
4141
void print(fmt::format_string<Args...> fmt, Args&&... args)

XenonRecomp/recompiler_config.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ void RecompilerConfig::Load(const std::string_view& configFilePath)
1313
{
1414
const auto& main = *mainPtr;
1515
filePath = main["file_path"].value_or<std::string>("");
16+
patchFilePath = main["patch_file_path"].value_or<std::string>("");
17+
patchedFilePath = main["patched_file_path"].value_or<std::string>("");
1618
outDirectoryPath = main["out_directory_path"].value_or<std::string>("");
1719
switchTableFilePath = main["switch_table_file_path"].value_or<std::string>("");
1820

XenonRecomp/recompiler_config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct RecompilerConfig
2626
{
2727
std::string directoryPath;
2828
std::string filePath;
29+
std::string patchFilePath;
30+
std::string patchedFilePath;
2931
std::string outDirectoryPath;
3032
std::string switchTableFilePath;
3133
std::unordered_map<uint32_t, RecompilerSwitchTable> switchTables;

XenonUtils/CMakeLists.txt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,28 @@ add_library(XenonUtils
44
"disasm.cpp"
55
"xex.cpp"
66
"image.cpp"
7-
"xdbf_wrapper.cpp")
7+
"xdbf_wrapper.cpp"
8+
"xex_patcher.cpp"
9+
"memory_mapped_file.cpp"
10+
"${THIRDPARTY_ROOT}/libmspack/libmspack/mspack/lzxd.c"
11+
"${THIRDPARTY_ROOT}/tiny-AES-c/aes.c"
12+
)
813

9-
target_include_directories(XenonUtils PUBLIC .)
10-
target_link_libraries(XenonUtils PUBLIC disasm)
14+
target_compile_definitions(XenonUtils
15+
PRIVATE
16+
NOMINMAX
17+
)
18+
19+
target_include_directories(XenonUtils
20+
PUBLIC
21+
.
22+
PRIVATE
23+
"${THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
24+
"${THIRDPARTY_ROOT}/tiny-AES-c"
25+
"${THIRDPARTY_ROOT}/TinySHA1"
26+
)
27+
28+
target_link_libraries(XenonUtils
29+
PUBLIC
30+
disasm
31+
)

XenonUtils/image.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Image Image::ParseImage(const uint8_t* data, size_t size)
3737
}
3838
else if (data[0] == 'X' && data[1] == 'E' && data[2] == 'X' && data[3] == '2')
3939
{
40-
return Xex2LoadImage(data);
40+
return Xex2LoadImage(data, size);
4141
}
4242

4343
return {};

XenonUtils/memory_mapped_file.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include "memory_mapped_file.h"
2+
3+
#if !defined(_WIN32)
4+
# include <cstring>
5+
# include <cstdio>
6+
# include <fcntl.h>
7+
# include <unistd.h>
8+
#endif
9+
10+
MemoryMappedFile::MemoryMappedFile()
11+
{
12+
// Default constructor.
13+
}
14+
15+
MemoryMappedFile::MemoryMappedFile(const std::filesystem::path &path)
16+
{
17+
open(path);
18+
}
19+
20+
MemoryMappedFile::~MemoryMappedFile()
21+
{
22+
close();
23+
}
24+
25+
MemoryMappedFile::MemoryMappedFile(MemoryMappedFile &&other)
26+
{
27+
#if defined(_WIN32)
28+
fileHandle = other.fileHandle;
29+
fileMappingHandle = other.fileMappingHandle;
30+
fileView = other.fileView;
31+
fileSize = other.fileSize;
32+
33+
other.fileHandle = nullptr;
34+
other.fileMappingHandle = nullptr;
35+
other.fileView = nullptr;
36+
other.fileSize.QuadPart = 0;
37+
#else
38+
fileHandle = other.fileHandle;
39+
fileView = other.fileView;
40+
fileSize = other.fileSize;
41+
42+
other.fileHandle = -1;
43+
other.fileView = MAP_FAILED;
44+
other.fileSize = 0;
45+
#endif
46+
}
47+
48+
bool MemoryMappedFile::open(const std::filesystem::path &path)
49+
{
50+
#if defined(_WIN32)
51+
fileHandle = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
52+
if (fileHandle == INVALID_HANDLE_VALUE)
53+
{
54+
fprintf(stderr, "CreateFileW failed with error %lu.\n", GetLastError());
55+
fileHandle = nullptr;
56+
return false;
57+
}
58+
59+
if (!GetFileSizeEx(fileHandle, &fileSize))
60+
{
61+
fprintf(stderr, "GetFileSizeEx failed with error %lu.\n", GetLastError());
62+
CloseHandle(fileHandle);
63+
fileHandle = nullptr;
64+
return false;
65+
}
66+
67+
fileMappingHandle = CreateFileMappingW(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
68+
if (fileMappingHandle == nullptr)
69+
{
70+
fprintf(stderr, "CreateFileMappingW failed with error %lu.\n", GetLastError());
71+
CloseHandle(fileHandle);
72+
fileHandle = nullptr;
73+
return false;
74+
}
75+
76+
fileView = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
77+
if (fileView == nullptr)
78+
{
79+
fprintf(stderr, "MapViewOfFile failed with error %lu.\n", GetLastError());
80+
CloseHandle(fileMappingHandle);
81+
CloseHandle(fileHandle);
82+
fileMappingHandle = nullptr;
83+
fileHandle = nullptr;
84+
return false;
85+
}
86+
87+
return true;
88+
#else
89+
fileHandle = ::open(path.c_str(), O_RDONLY);
90+
if (fileHandle == -1)
91+
{
92+
fprintf(stderr, "open for %s failed with error %s.\n", path.c_str(), strerror(errno));
93+
return false;
94+
}
95+
96+
fileSize = lseek(fileHandle, 0, SEEK_END);
97+
if (fileSize == (off_t)(-1))
98+
{
99+
fprintf(stderr, "lseek failed with error %s.\n", strerror(errno));
100+
::close(fileHandle);
101+
fileHandle = -1;
102+
return false;
103+
}
104+
105+
fileView = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0);
106+
if (fileView == MAP_FAILED)
107+
{
108+
fprintf(stderr, "mmap failed with error %s.\n", strerror(errno));
109+
::close(fileHandle);
110+
fileHandle = -1;
111+
return false;
112+
}
113+
114+
return true;
115+
#endif
116+
}
117+
118+
void MemoryMappedFile::close()
119+
{
120+
#if defined(_WIN32)
121+
if (fileView != nullptr)
122+
{
123+
UnmapViewOfFile(fileView);
124+
}
125+
126+
if (fileMappingHandle != nullptr)
127+
{
128+
CloseHandle(fileMappingHandle);
129+
}
130+
131+
if (fileHandle != nullptr)
132+
{
133+
CloseHandle(fileHandle);
134+
}
135+
#else
136+
if (fileView != MAP_FAILED)
137+
{
138+
munmap(fileView, fileSize);
139+
}
140+
141+
if (fileHandle != -1)
142+
{
143+
::close(fileHandle);
144+
}
145+
#endif
146+
}
147+
148+
bool MemoryMappedFile::isOpen() const
149+
{
150+
#if defined(_WIN32)
151+
return (fileView != nullptr);
152+
#else
153+
return (fileView != MAP_FAILED);
154+
#endif
155+
}
156+
157+
uint8_t *MemoryMappedFile::data() const
158+
{
159+
return reinterpret_cast<uint8_t *>(fileView);
160+
}
161+
162+
size_t MemoryMappedFile::size() const
163+
{
164+
#if defined(_WIN32)
165+
return fileSize.QuadPart;
166+
#else
167+
return static_cast<size_t>(fileSize);
168+
#endif
169+
}

XenonUtils/memory_mapped_file.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <filesystem>
4+
5+
#if defined(_WIN32)
6+
# include <Windows.h>
7+
#else
8+
# include <sys/mman.h>
9+
#endif
10+
11+
struct MemoryMappedFile {
12+
#if defined(_WIN32)
13+
HANDLE fileHandle = nullptr;
14+
HANDLE fileMappingHandle = nullptr;
15+
LPVOID fileView = nullptr;
16+
LARGE_INTEGER fileSize = {};
17+
#else
18+
int fileHandle = -1;
19+
void *fileView = MAP_FAILED;
20+
off_t fileSize = 0;
21+
#endif
22+
23+
MemoryMappedFile();
24+
MemoryMappedFile(const std::filesystem::path &path);
25+
MemoryMappedFile(MemoryMappedFile &&other);
26+
~MemoryMappedFile();
27+
bool open(const std::filesystem::path &path);
28+
void close();
29+
bool isOpen() const;
30+
uint8_t *data() const;
31+
size_t size() const;
32+
};

0 commit comments

Comments
 (0)