Working Libtomcrypt Implementation
LayeredFS now allows for encrypting game files dynamically
This commit is contained in:
parent
8214d961a9
commit
15c01fbe7c
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,6 +11,8 @@ subprojects/xxHash-0.8.2
|
||||
subprojects/safetyhook
|
||||
subprojects/zxing-cpp-2.2.1
|
||||
subprojects/stb
|
||||
subprojects/zlib
|
||||
libtomcrypt-1.18.2
|
||||
dist.7z
|
||||
.vscode
|
||||
out/
|
2
Makefile
2
Makefile
@ -11,5 +11,5 @@ dist-no-7z: all
|
||||
@cp -r dist/* out/
|
||||
|
||||
dist: dist-no-7z
|
||||
@cd out && 7z a -t7z ../dist.7z .
|
||||
@cd out && 7zz a -t7z ../dist.7z .
|
||||
@rm -rf out
|
||||
|
2
dist/config.toml
vendored
2
dist/config.toml
vendored
@ -48,3 +48,5 @@ jp_layout = false
|
||||
|
||||
[layeredfs]
|
||||
enabled = false
|
||||
datatable_key = ""
|
||||
fumen_key = ""
|
13
meson.build
13
meson.build
@ -40,6 +40,10 @@ stb = subproject('stb')
|
||||
opt_var.add_cmake_defines({'BUILD_EXAMPLES': false})
|
||||
zxing_proj = cmake.subproject('zxing', options: opt_var)
|
||||
zxing_dep = zxing_proj.dependency('ZXing')
|
||||
zlib_proj = subproject('zlib')
|
||||
zlib_dep = zlib_proj.get_variable('zlib_dep')
|
||||
libtomcrypt = subproject('libtomcrypt')
|
||||
libtomcrypt_dep = libtomcrypt.get_variable('tomcrypt_dep')
|
||||
|
||||
library(
|
||||
'bnusio',
|
||||
@ -48,6 +52,8 @@ library(
|
||||
tomlc99.get_variable('tomlc99_lib'),
|
||||
sdl2.get_variable('sdl2'),
|
||||
xxhash.get_variable('xxhash'),
|
||||
zlib_proj.get_variable('zlib_lib'),
|
||||
libtomcrypt.get_variable('tomcryptlib'),
|
||||
],
|
||||
include_directories: [
|
||||
'src',
|
||||
@ -55,11 +61,16 @@ library(
|
||||
tomlc99.get_variable('tomlc99_inc'),
|
||||
sdl2.get_variable('core_inc'),
|
||||
xxhash.get_variable('inc'),
|
||||
zlib_proj.get_variable('zlib_inc'),
|
||||
libtomcrypt.get_variable('core_inc'),
|
||||
],
|
||||
dependencies: [stb.get_variable('stb_dep'),
|
||||
dependencies: [
|
||||
stb.get_variable('stb_dep'),
|
||||
zxing_dep,
|
||||
safetyhook_dep,
|
||||
zydis_dep,
|
||||
zlib_dep,
|
||||
libtomcrypt_dep,
|
||||
],
|
||||
sources : [
|
||||
'src/dllmain.cpp',
|
||||
|
265
src/dllmain.cpp
265
src/dllmain.cpp
@ -3,6 +3,9 @@
|
||||
#include "helpers.h"
|
||||
#include "patches/patches.h"
|
||||
#include "poll.h"
|
||||
#include <zlib.h>
|
||||
#include <tomcrypt.h>
|
||||
#define POLY 0x82f63b78
|
||||
|
||||
GameVersion gameVersion = GameVersion::UNKNOWN;
|
||||
std::vector<HMODULE> plugins;
|
||||
@ -24,6 +27,159 @@ char chipId2[33] = "00000000000000000000000000000002";
|
||||
bool autoIME = false;
|
||||
bool jpLayout = false;
|
||||
bool useLayeredFs = false;
|
||||
std::string datatableKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
std::string fumenKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
uint32_t crc32c(uint32_t crc, const unsigned char *buf, size_t len)
|
||||
{
|
||||
int k;
|
||||
|
||||
crc = ~crc;
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
for (k = 0; k < 8; k++)
|
||||
crc = (crc >> 1) ^ (POLY & (0 - (crc & 1)));
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
bool checkCRC(const std::string &path, uint32_t crc){
|
||||
if (std::filesystem::exists(path)){
|
||||
std::filesystem::path crc_path = path;
|
||||
crc_path.replace_extension(".crc");
|
||||
std::ifstream crc_file(crc_path, std::ios::binary);
|
||||
std::string crc_content((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
|
||||
return std::stoul(crc_content) != crc;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void create_directories(const std::string &path) {
|
||||
size_t pos = 0;
|
||||
std::string delimiter = "\\";
|
||||
std::string current_path;
|
||||
|
||||
while ((pos = path.find(delimiter, pos)) != std::string::npos) {
|
||||
current_path = path.substr(0, pos++);
|
||||
if (!current_path.empty() && !CreateDirectory(current_path.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
throw std::runtime_error("Error creating directory: " + current_path);
|
||||
}
|
||||
}
|
||||
if (!path.empty() && !CreateDirectory(path.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
throw std::runtime_error("Error creating directory: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
void write_file(const std::string &filename, const std::vector<uint8_t> &data, uint32_t original_crc) {
|
||||
std::string::size_type pos = filename.find_last_of("\\");
|
||||
if (pos != std::string::npos) {
|
||||
std::string directory = filename.substr(0, pos);
|
||||
create_directories(directory);
|
||||
}
|
||||
|
||||
std::filesystem::path crc_path = filename;
|
||||
crc_path.replace_extension(".crc");
|
||||
std::ofstream crc_file(crc_path.string());
|
||||
crc_file << std::to_string(original_crc);
|
||||
crc_file.close();
|
||||
|
||||
std::ofstream file(filename, std::ios::binary);
|
||||
file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
||||
}
|
||||
|
||||
std::vector<unsigned char> gzip_compress(const std::vector<unsigned char>& data) {
|
||||
z_stream deflate_stream;
|
||||
deflate_stream.zalloc = Z_NULL;
|
||||
deflate_stream.zfree = Z_NULL;
|
||||
deflate_stream.opaque = Z_NULL;
|
||||
deflate_stream.avail_in = data.size();
|
||||
deflate_stream.next_in = const_cast<Bytef*>(data.data());
|
||||
|
||||
deflateInit2(&deflate_stream, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
|
||||
std::vector<unsigned char> compressed_data;
|
||||
compressed_data.resize(deflateBound(&deflate_stream, data.size()));
|
||||
|
||||
deflate_stream.avail_out = compressed_data.size();
|
||||
deflate_stream.next_out = compressed_data.data();
|
||||
|
||||
deflate(&deflate_stream, Z_FINISH);
|
||||
deflateEnd(&deflate_stream);
|
||||
|
||||
compressed_data.resize(deflate_stream.total_out);
|
||||
return compressed_data;
|
||||
}
|
||||
|
||||
// Function to pad data according to PKCS7
|
||||
std::vector<uint8_t> pad_data(const std::vector<uint8_t> &data, size_t block_size) {
|
||||
size_t padding = block_size - (data.size() % block_size);
|
||||
std::vector<uint8_t> padded_data = data;
|
||||
padded_data.insert(padded_data.end(), padding, static_cast<uint8_t>(padding));
|
||||
return padded_data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> hex_to_bytes(const std::string &hex) {
|
||||
std::vector<uint8_t> bytes;
|
||||
for (size_t i = 0; i < hex.length(); i += 2) {
|
||||
uint8_t byte = static_cast<uint8_t>(std::stoi(hex.substr(i, 2), nullptr, 16));
|
||||
bytes.push_back(byte);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypt_file(const std::string &input_file, const std::string &hex_key) {
|
||||
// Convert the key from hex to bytes
|
||||
std::vector<uint8_t> key = hex_to_bytes(hex_key);
|
||||
|
||||
// Generate the 128 bits IV
|
||||
std::vector<uint8_t> iv(16);
|
||||
for (size_t i = 0; i < iv.size(); ++i) iv[i] = static_cast<uint8_t>(i);
|
||||
|
||||
// Read the entire file into memory
|
||||
std::ifstream file(input_file, std::ios::binary);
|
||||
|
||||
std::vector<uint8_t> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
// Compress the data
|
||||
std::vector<uint8_t> compressed_data = gzip_compress(data);
|
||||
|
||||
// Pad the compressed data
|
||||
std::vector<uint8_t> padded_data = pad_data(compressed_data, 16);
|
||||
|
||||
// Encrypt the data
|
||||
symmetric_CBC cbc;
|
||||
if (cbc_start(find_cipher("aes"), iv.data(), key.data(), key.size(), 0, &cbc) != CRYPT_OK) {
|
||||
throw std::runtime_error("Error initializing CBC");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypted_data(padded_data.size());
|
||||
if (cbc_encrypt(padded_data.data(), encrypted_data.data(), padded_data.size(), &cbc) != CRYPT_OK) {
|
||||
throw std::runtime_error("Error during encryption");
|
||||
}
|
||||
|
||||
cbc_done(&cbc);
|
||||
|
||||
// Return IV concatenated with the encrypted data
|
||||
encrypted_data.insert(encrypted_data.begin(), iv.begin(), iv.end());
|
||||
return encrypted_data;
|
||||
}
|
||||
|
||||
bool is_fumen_encrypted(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
file.seekg(0x210, std::ios::beg);
|
||||
std::vector<unsigned char> buffer(32);
|
||||
file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
|
||||
|
||||
// Check if the read bytes match the expected pattern
|
||||
std::vector<unsigned char> expected_bytes = {
|
||||
0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
return buffer != expected_bytes;
|
||||
}
|
||||
|
||||
HOOK (i32, ShowMouse, PROC_ADDRESS ("user32.dll", "ShowCursor"), bool) { return originalShowMouse (true); }
|
||||
HOOK (i32, ExitWindows, PROC_ADDRESS ("user32.dll", "ExitWindowsEx")) { ExitProcess (0); }
|
||||
@ -51,48 +207,102 @@ HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPC
|
||||
if (!path.is_absolute ()) path = std::filesystem::absolute (path);
|
||||
auto originalDataFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data" / "x64";
|
||||
auto originalLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64";
|
||||
auto encryptedLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64_enc";
|
||||
|
||||
if (path.string ().find (originalDataFolder.string ()) == 0) {
|
||||
auto newPath = path.string ();
|
||||
auto encPath = path.string ();
|
||||
newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
|
||||
encPath.replace (0, originalDataFolder.string ().length (), encryptedLayeredFsFolder.string ());
|
||||
|
||||
if (std::filesystem::exists (newPath)) {
|
||||
std::cout << "Redirecting " << path << " to " << newPath << std::endl;
|
||||
//The following code handles file redirection and if need be, file encryption.
|
||||
//It's a bit of a mess but it works well ! -Kit
|
||||
|
||||
if (std::filesystem::exists (newPath)) { //If a file exists in the datamod folder
|
||||
if(is_fumen_encrypted(newPath)){ //And if it's an encrypted fumen or a different type of file, use it.
|
||||
std::cout << "Redirecting " << std::filesystem::relative (path).string() << std::endl;
|
||||
return originalCreateFileAHook (newPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
else{ //Otherwise if it's an unencrypted fumen.
|
||||
if (!std::filesystem::exists(encPath)) { //We check if we don't already have a cached file.
|
||||
if(fumenKey.length() == 64){
|
||||
std::cout << "Encrypting " << std::filesystem::relative (newPath) << std::endl; // If we don't we encrypt the file
|
||||
std::ifstream crc_file(newPath, std::ios::binary);
|
||||
std::vector<uint8_t> crc_vector((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
|
||||
uint32_t crc = crc32c(0, crc_vector.data(), crc_vector.size());
|
||||
write_file(encPath, encrypt_file(newPath, fumenKey), crc); // And we save it
|
||||
} else {
|
||||
std::cout << "Missing or invalid fumen key : " << std::filesystem::relative (newPath) << " couldn't be encrypted." << std::endl;
|
||||
encPath = path.string();
|
||||
}
|
||||
} else std::cout << "Using cached file for " << std::filesystem::relative (newPath) << std::endl;
|
||||
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
}
|
||||
|
||||
//We check separately for unencrypted json files.
|
||||
std::filesystem::path json_path = newPath;
|
||||
json_path.replace_extension(".json");
|
||||
if (std::filesystem::exists(json_path)) { //If a json file exists in the folder
|
||||
bool crcBool = false;
|
||||
|
||||
if (std::filesystem::exists(encPath)){
|
||||
std::ifstream crc_file(json_path, std::ios::binary);
|
||||
std::vector<uint8_t> crc_vector((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
|
||||
uint32_t crc = crc32c(0, crc_vector.data(), crc_vector.size());
|
||||
crcBool = checkCRC(encPath, crc);
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(encPath) || crcBool) { //And if it hasn't been encrypted before
|
||||
if(datatableKey.length() == 64){
|
||||
std::cout << "Encrypting " << std::filesystem::relative (json_path) << std::endl; //Encrypt the file
|
||||
std::ifstream crc_file(json_path.string(), std::ios::binary);
|
||||
std::vector<uint8_t> crc_vector((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
|
||||
uint32_t crc = crc32c(0, crc_vector.data(), crc_vector.size());
|
||||
write_file(encPath, encrypt_file(json_path.string(), datatableKey), crc); //And save it
|
||||
} else {
|
||||
std::cout << "Missing or invalid datatable key : " << std::filesystem::relative (newPath) << " couldn't be encrypted." << std::endl;
|
||||
encPath = path.string();
|
||||
}
|
||||
}
|
||||
else std::cout << "Using cached file for " << std::filesystem::relative (json_path) << std::endl; //Otherwise use the already encrypted file.
|
||||
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
}
|
||||
|
||||
return originalCreateFileAHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
|
||||
hTemplateFile);
|
||||
}
|
||||
|
||||
HOOK (HANDLE, CreateFileWHook, PROC_ADDRESS ("kernel32.dll", "CreateFileW"), LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
|
||||
std::string strFileName = converter.to_bytes (lpFileName);
|
||||
// HOOK (HANDLE, CreateFileWHook, PROC_ADDRESS ("kernel32.dll", "CreateFileW"), LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
// LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
// std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
|
||||
// std::string strFileName = converter.to_bytes (lpFileName);
|
||||
|
||||
std::filesystem::path path (strFileName);
|
||||
if (!path.is_absolute ()) path = std::filesystem::absolute (path);
|
||||
// std::filesystem::path path (strFileName);
|
||||
// if (!path.is_absolute ()) path = std::filesystem::absolute (path);
|
||||
|
||||
auto originalDataFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data" / "x64";
|
||||
auto originalLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64";
|
||||
// auto originalDataFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data" / "x64";
|
||||
// auto originalLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64";
|
||||
|
||||
if (path.string ().find (originalDataFolder.string ()) != std::string::npos) {
|
||||
auto newPath = path.string ();
|
||||
newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
|
||||
// if (path.string ().find (originalDataFolder.string ()) != std::string::npos) {
|
||||
// auto newPath = path.string ();
|
||||
// newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
|
||||
|
||||
if (std::filesystem::exists (newPath)) {
|
||||
std::wstring wNewPath = converter.from_bytes (newPath);
|
||||
std::wcout << L"Redirecting " << lpFileName << L" to " << wNewPath << std::endl;
|
||||
return originalCreateFileWHook (wNewPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
}
|
||||
// if (std::filesystem::exists (newPath)) {
|
||||
// std::wstring wNewPath = converter.from_bytes (newPath);
|
||||
// std::wcout << L"Redirecting " << lpFileName << L" to " << wNewPath << std::endl;
|
||||
// return originalCreateFileWHook (wNewPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
// dwFlagsAndAttributes, hTemplateFile);
|
||||
// }
|
||||
// }
|
||||
|
||||
return originalCreateFileWHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
|
||||
hTemplateFile);
|
||||
}
|
||||
// return originalCreateFileWHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
|
||||
// hTemplateFile);
|
||||
// }
|
||||
|
||||
void
|
||||
GetGameVersion () {
|
||||
@ -184,7 +394,11 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
}
|
||||
|
||||
auto layeredFs = openConfigSection (config, "layeredfs");
|
||||
if (layeredFs) useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
|
||||
if (layeredFs) {
|
||||
useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
|
||||
datatableKey = readConfigString (layeredFs, "datatable_key", datatableKey);
|
||||
fumenKey = readConfigString (layeredFs, "fumen_key", fumenKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (version == "auto") {
|
||||
@ -243,8 +457,9 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
|
||||
if (useLayeredFs) {
|
||||
std::wcout << "Using LayeredFs!" << std::endl;
|
||||
register_cipher(&aes_desc);
|
||||
INSTALL_HOOK (CreateFileAHook);
|
||||
INSTALL_HOOK (CreateFileWHook);
|
||||
// INSTALL_HOOK (CreateFileWHook);
|
||||
}
|
||||
|
||||
bnusio::Init ();
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <winsock2.h>
|
||||
#include <MinHook.h>
|
||||
#include <bits/stdc++.h>
|
||||
#include <format>
|
||||
@ -8,7 +9,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <toml.h>
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
/*
|
||||
|
12
subprojects/libtomcrypt.wrap
Normal file
12
subprojects/libtomcrypt.wrap
Normal file
@ -0,0 +1,12 @@
|
||||
[wrap-file]
|
||||
directory = libtomcrypt-1.18.2
|
||||
source_url = https://github.com/libtom/libtomcrypt/releases/download/v1.18.2/crypt-1.18.2.tar.xz
|
||||
source_filename = crypt-1.18.2.tar.xz
|
||||
source_hash = 96ad4c3b8336050993c5bc2cf6c057484f2b0f9f763448151567fbab5e767b84
|
||||
patch_filename = libtomcrypt_1.18.2-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/libtomcrypt_1.18.2-1/get_patch
|
||||
patch_hash = 0ca889b864e5a23525774438439d9e385268ff7234d49029902375a6c5e18f14
|
||||
|
||||
[provide]
|
||||
libtomcrypt = tomcrypt_dep
|
||||
|
33
subprojects/packagefiles/zlib.patch
Normal file
33
subprojects/packagefiles/zlib.patch
Normal file
@ -0,0 +1,33 @@
|
||||
+++ zlib/meson.build
|
||||
@@ -0,0 +1,31 @@
|
||||
+project('zlib', 'c', version: '1.0.0')
|
||||
+
|
||||
+zlib_inc = include_directories('.')
|
||||
+zlib_sources = files(
|
||||
+ 'adler32.c',
|
||||
+ 'compress.c',
|
||||
+ 'crc32.c',
|
||||
+ 'deflate.c',
|
||||
+ 'gzclose.c',
|
||||
+ 'gzlib.c',
|
||||
+ 'gzread.c',
|
||||
+ 'gzwrite.c',
|
||||
+ 'infback.c',
|
||||
+ 'inffast.c',
|
||||
+ 'inflate.c',
|
||||
+ 'inftrees.c',
|
||||
+ 'trees.c',
|
||||
+ 'uncompr.c',
|
||||
+ 'zutil.c'
|
||||
+)
|
||||
+
|
||||
+zlib_lib = static_library(
|
||||
+ 'zlib',
|
||||
+ zlib_sources,
|
||||
+ include_directories: zlib_inc
|
||||
+)
|
||||
+
|
||||
+zlib_dep = declare_dependency(
|
||||
+ include_directories: zlib_inc,
|
||||
+ link_with: zlib_lib
|
||||
+)
|
4
subprojects/zlib.wrap
Executable file
4
subprojects/zlib.wrap
Executable file
@ -0,0 +1,4 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/madler/zlib
|
||||
revision = 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf
|
||||
diff_files = zlib.patch
|
Loading…
Reference in New Issue
Block a user